Initialize icaltimetype variables in Vcalendar's feed_fetch().
[claws.git] / src / plugins / vcalendar / vcal_folder.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2015 Colin Leroy <colin@colino.net> and
4  * the Claws Mail team
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include <stddef.h>
26 #include <glib.h>
27 #include <glib/gi18n.h>
28
29 #include "defs.h"
30
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <curl/curl.h>
36 #include <curl/curlver.h>
37
38 #include "account.h"
39 #include "utils.h"
40 #include "procmsg.h"
41 #include "procheader.h"
42 #include "folder.h"
43 #include "folderview.h"
44 #include "folder_item_prefs.h"
45 #include "vcalendar.h"
46 #include "vcal_folder.h"
47 #include "vcal_prefs.h"
48 #include "vcal_manager.h"
49 #include "vcal_meeting_gtk.h"
50 #include "vcal_interface.h"
51 #include "prefs_account.h"
52 #include "prefs_common.h"
53 #include "claws.h"
54 #include "menu.h"
55 #include "inputdialog.h"
56 #include "inc.h"
57 #include "xml.h"
58 #include "alertpanel.h"
59 #include "log.h"
60 #include "mainwindow.h"
61 #include "statusbar.h"
62 #include "msgcache.h"
63 #include "passwordstore.h"
64 #include "timing.h"
65 #include "messageview.h"
66
67 #include <gtk/gtk.h>
68 #include <dirent.h>
69
70 #define VCAL_FOLDERITEM(item) ((VCalFolderItem *) item)
71
72 #ifdef USE_PTHREAD
73 #include <pthread.h>
74 #endif
75
76 typedef struct _thread_data {
77         const gchar *url;
78         gchar *result;
79         gchar *error;
80         gboolean done;
81 } thread_data;
82
83 typedef struct _IcalFeedData {
84         icalcomponent *event;
85         gchar *pseudoevent_id;
86 } IcalFeedData;
87
88 typedef struct _VCalFolder VCalFolder;
89 typedef struct _VCalFolderItem VCalFolderItem;
90
91 static Folder *vcal_folder_new(const gchar * name,
92                                   const gchar * folder);
93 static void vcal_folder_destroy(Folder * folder);
94 static void vcal_item_destroy(Folder *folder, FolderItem *_item);
95 static gchar *vcal_item_get_path(Folder *folder, FolderItem *item);
96
97 static gint vcal_scan_tree(Folder * folder);
98 static FolderItem *vcal_item_new(Folder * folder);
99 static gint vcal_get_num_list(Folder * folder, FolderItem * item,
100                                  MsgNumberList ** list,
101                                  gboolean * old_uids_valid);
102 static MsgInfo *vcal_get_msginfo(Folder * folder, FolderItem * item,
103                                     gint num);
104 static gchar *vcal_fetch_msg(Folder * folder, FolderItem * item,
105                                 gint num);
106 static gint vcal_add_msg(Folder * folder, FolderItem * _dest,
107                             const gchar * file, MsgFlags * flags);
108 static gint vcal_remove_msg(Folder * folder, FolderItem * _item,
109                                gint num);
110 static FolderItem *vcal_create_folder(Folder * folder,
111                                          FolderItem * parent,
112                                          const gchar * name);
113 static gint vcal_create_tree(Folder *folder);
114 static gint vcal_remove_folder(Folder *folder, FolderItem *item);
115 static gboolean vcal_scan_required(Folder *folder, FolderItem *item);
116 static void vcal_set_mtime(Folder *folder, FolderItem *item);
117 static void vcal_change_flags(Folder *folder, FolderItem *_item, MsgInfo *msginfo, MsgPermFlags newflags);
118
119 static void new_meeting_cb(GtkAction *action, gpointer data);
120 static void export_cal_cb(GtkAction *action, gpointer data);
121 static void subscribe_cal_cb(GtkAction *action, gpointer data);
122 static void check_subs_cb(GtkAction *action, gpointer data);
123 static void unsubscribe_cal_cb(GtkAction *action, gpointer data);
124 static void rename_cb(GtkAction *action, gpointer data);
125 static void set_view_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
126
127 static void add_menuitems(GtkUIManager *ui_manager, FolderItem *item);
128 static void set_sensitivity(GtkUIManager *ui_manager, FolderItem *item);
129
130 static void update_subscription(const gchar *uri, gboolean verbose);
131 static void vcal_folder_set_batch       (Folder         *folder,
132                                          FolderItem     *item,
133                                          gboolean        batch);
134 static void convert_to_utc(icalcomponent *calendar);
135
136 gboolean vcal_subscribe_uri(Folder *folder, const gchar *uri);
137
138 FolderClass vcal_class;
139
140 static GSList *created_files = NULL;
141 static GHashTable *hash_uids = NULL;
142
143 struct _VCalFolder
144 {
145         Folder folder;
146 };
147
148 struct _VCalFolderItem
149 {
150         FolderItem item;
151         gchar *uri;
152         gchar *feed;
153         icalcomponent *cal;
154         GSList *numlist;
155         GSList *evtlist;
156         gboolean batching;
157         gboolean dirty;
158         day_win *dw;
159         month_win *mw;
160         time_t last_fetch;
161         int use_cal_view;
162 };
163
164 static char *vcal_popup_labels[] =
165 {
166         N_("_New meeting..."),
167         N_("_Export calendar..."),
168         N_("_Subscribe to Webcal..."),
169         N_("_Unsubscribe..."),
170         N_("_Rename..."),
171         N_("U_pdate subscriptions"),
172         N_("_List view"),
173         N_("_Week view"),
174         N_("_Month view"),
175         NULL
176 };
177
178 static GtkActionEntry vcal_popup_entries[] = 
179 {
180         {"FolderViewPopup/NewMeeting",          NULL, NULL, NULL, NULL, G_CALLBACK(new_meeting_cb) },
181         {"FolderViewPopup/ExportCal",           NULL, NULL, NULL, NULL, G_CALLBACK(export_cal_cb) },
182
183         {"FolderViewPopup/SubscribeCal",        NULL, NULL, NULL, NULL, G_CALLBACK(subscribe_cal_cb) },
184         {"FolderViewPopup/UnsubscribeCal",      NULL, NULL, NULL, NULL, G_CALLBACK(unsubscribe_cal_cb) },
185
186         {"FolderViewPopup/RenameFolder",        NULL, NULL, NULL, NULL, G_CALLBACK(rename_cb) },
187
188         {"FolderViewPopup/CheckSubs",           NULL, NULL, NULL, NULL, G_CALLBACK(check_subs_cb) },
189
190 };                      
191
192 static GtkRadioActionEntry vcal_popup_radio_entries[] = { /* set_view_cb */
193         {"FolderViewPopup/ListView",            NULL, NULL, NULL, NULL, 0 }, 
194         {"FolderViewPopup/WeekView",            NULL, NULL, NULL, NULL, 1 }, 
195         {"FolderViewPopup/MonthView",           NULL, NULL, NULL, NULL, 2 }, 
196 };
197                 
198 static IcalFeedData *icalfeeddata_new(icalcomponent *evt, gchar *str)
199 {
200         IcalFeedData *data = g_new0(IcalFeedData, 1);
201         if (str)
202                 data->pseudoevent_id = g_strdup(str);
203         data->event = evt;
204         return data;
205 }
206
207 static void icalfeeddata_free(IcalFeedData *data)
208 {
209         g_free(data->pseudoevent_id);
210         if (data->event)
211                 icalcomponent_free(data->event);
212         g_free(data);
213 }
214
215 static void slist_free_icalfeeddata(GSList *list)
216 {
217         while (list) {
218                 IcalFeedData *data = (IcalFeedData *)list->data;
219                 icalfeeddata_free(data);
220                 list = list->next;
221         }
222 }
223
224 static void vcal_fill_popup_menu_labels(void)
225 {
226         vcal_popup_entries[0].label = _(vcal_popup_labels[0]);
227         vcal_popup_entries[1].label = _(vcal_popup_labels[1]);
228         vcal_popup_entries[2].label = _(vcal_popup_labels[2]);
229         vcal_popup_entries[3].label = _(vcal_popup_labels[3]);
230         vcal_popup_entries[4].label = _(vcal_popup_labels[4]);
231         vcal_popup_entries[5].label = _(vcal_popup_labels[5]);
232         vcal_popup_radio_entries[0].label = _(vcal_popup_labels[6]);
233         vcal_popup_radio_entries[1].label = _(vcal_popup_labels[7]);
234         vcal_popup_radio_entries[2].label = _(vcal_popup_labels[8]);
235 }
236
237 static FolderViewPopup vcal_popup =
238 {
239         PLUGIN_NAME,
240         "<vCalendar>",
241         vcal_popup_entries,
242         G_N_ELEMENTS(vcal_popup_entries),
243         NULL, 0,
244         vcal_popup_radio_entries, 
245         G_N_ELEMENTS(vcal_popup_radio_entries), 1, set_view_cb,
246         add_menuitems,
247         set_sensitivity
248 };
249
250 static void vcal_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
251 {
252         GList *cur;
253         folder_item_set_xml(folder, item, tag);
254         gboolean found_cal_view_setting = FALSE;
255
256         for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
257                 XMLAttr *attr = (XMLAttr *) cur->data;
258
259                 if (!attr || !attr->name || !attr->value) continue;
260                 if (!strcmp(attr->name, "uri")) {
261                         if (((VCalFolderItem *)item)->uri != NULL)
262                                 g_free(((VCalFolderItem *)item)->uri);
263                         ((VCalFolderItem *)item)->uri = g_strdup(attr->value);
264                 } 
265                 if (!strcmp(attr->name, "use_cal_view")) {
266                         found_cal_view_setting = TRUE;
267                         ((VCalFolderItem *)item)->use_cal_view = atoi(attr->value);
268                 } 
269         }
270         if (((VCalFolderItem *)item)->uri == NULL) {
271                 /* give a path to inbox */
272                 g_free(item->path);
273                 item->path = g_strdup(".meetings");
274         }
275         if (!found_cal_view_setting)
276                 ((VCalFolderItem *)item)->use_cal_view = 1; /*week view */
277         
278 }
279
280 static XMLTag *vcal_item_get_xml(Folder *folder, FolderItem *item)
281 {
282         XMLTag *tag;
283
284         tag = folder_item_get_xml(folder, item);
285
286         if (((VCalFolderItem *)item)->uri)
287                 xml_tag_add_attr(tag, xml_attr_new("uri", ((VCalFolderItem *)item)->uri));
288
289         xml_tag_add_attr(tag, xml_attr_new_int("use_cal_view", ((VCalFolderItem *)item)->use_cal_view));
290
291         return tag;
292 }
293
294 static gint vcal_rename_folder(Folder *folder, FolderItem *item,
295                              const gchar *name)
296 {
297         if (!name)
298                 return -1;
299         g_free(item->name);
300         item->name = g_strdup(name);
301         return 0;
302 }
303
304 static void vcal_get_sort_type(Folder *folder, FolderSortKey *sort_key,
305                                FolderSortType *sort_type)
306 {
307         if (sort_key)
308                 *sort_key = SORT_BY_DATE;
309 }
310
311 static void vcal_item_opened(FolderItem *item)
312 {
313         struct tm tmdate;
314         time_t t = time(NULL);
315 #ifndef G_OS_WIN32
316         localtime_r(&t, &tmdate);
317 #else
318         if (t < 0)
319                 t = 1;
320         tmdate = *localtime(&t);
321 #endif
322         if (!((VCalFolderItem *)(item))->dw 
323             && ((VCalFolderItem *)(item))->use_cal_view == 1)
324                 ((VCalFolderItem *)(item))->dw = create_day_win(item, tmdate);          
325         else if (!((VCalFolderItem *)(item))->mw 
326             && ((VCalFolderItem *)(item))->use_cal_view == 2)
327                 ((VCalFolderItem *)(item))->mw = create_month_win(item, tmdate);
328         else if (((VCalFolderItem *)(item))->use_cal_view != 0)
329                 vcal_folder_refresh_cal(item);
330 }
331
332 void vcal_folder_refresh_cal(FolderItem *item)
333 {
334         Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
335         if (item->folder != folder)
336                 return;
337         if (((VCalFolderItem *)(item))->dw)
338                 refresh_day_win(((VCalFolderItem *)(item))->dw);
339         if (((VCalFolderItem *)(item))->mw)
340                 refresh_month_win(((VCalFolderItem *)(item))->mw);
341 }
342
343 static void vcal_item_closed(FolderItem *item)
344 {
345         if (((VCalFolderItem *)(item))->dw) {
346                 dw_close_window(((VCalFolderItem *)(item))->dw);
347                 ((VCalFolderItem *)(item))->dw = NULL;
348         }
349         if (((VCalFolderItem *)(item))->mw) {
350                 mw_close_window(((VCalFolderItem *)(item))->mw);
351                 ((VCalFolderItem *)(item))->mw = NULL;
352         }
353 }
354
355 FolderClass *vcal_folder_get_class()
356 {
357         if (vcal_class.idstr == NULL) {
358                 debug_print("register class\n");
359                 vcal_class.type = F_UNKNOWN;
360                 vcal_class.idstr = PLUGIN_NAME;
361                 vcal_class.uistr = PLUGIN_NAME;
362
363                 /* Folder functions */
364                 vcal_class.new_folder = vcal_folder_new;
365                 vcal_class.destroy_folder = vcal_folder_destroy;
366                 vcal_class.set_xml = folder_set_xml;
367                 vcal_class.get_xml = folder_get_xml;
368                 vcal_class.item_set_xml = vcal_item_set_xml;
369                 vcal_class.item_get_xml = vcal_item_get_xml;
370                 vcal_class.scan_tree = vcal_scan_tree;
371                 vcal_class.create_tree = vcal_create_tree;
372                 vcal_class.get_sort_type = vcal_get_sort_type;
373
374                 /* FolderItem functions */
375                 vcal_class.item_new = vcal_item_new;
376                 vcal_class.item_destroy = vcal_item_destroy;
377                 vcal_class.item_get_path = vcal_item_get_path;
378                 vcal_class.create_folder = vcal_create_folder;
379                 vcal_class.remove_folder = vcal_remove_folder;
380                 vcal_class.rename_folder = vcal_rename_folder;
381                 vcal_class.scan_required = vcal_scan_required;
382                 vcal_class.set_mtime = vcal_set_mtime;
383                 vcal_class.get_num_list = vcal_get_num_list;
384                 vcal_class.set_batch = vcal_folder_set_batch;
385
386                 /* Message functions */
387                 vcal_class.get_msginfo = vcal_get_msginfo;
388                 vcal_class.fetch_msg = vcal_fetch_msg;
389                 vcal_class.add_msg = vcal_add_msg;
390                 vcal_class.copy_msg = NULL;
391                 vcal_class.remove_msg = vcal_remove_msg;
392                 vcal_class.change_flags = vcal_change_flags;
393                 vcal_class.subscribe = vcal_subscribe_uri;
394
395                 /* FolderView functions */
396                 vcal_class.item_opened = vcal_item_opened;
397                 vcal_class.item_closed = vcal_item_closed;
398                 debug_print("registered class for real\n");
399         }
400
401         return &vcal_class;
402 }
403
404 static void vcal_folder_set_batch       (Folder         *folder,
405                                          FolderItem     *_item,
406                                          gboolean        batch)
407 {
408         VCalFolderItem *item = (VCalFolderItem *)_item;
409
410         g_return_if_fail(item != NULL);
411         
412         if (item->batching == batch)
413                 return;
414         
415         if (batch) {
416                 item->batching = TRUE;
417                 debug_print("vcal switching to batch mode\n");
418         } else {
419                 debug_print("vcal switching away from batch mode\n");
420                 /* ici */
421                 item->batching = FALSE;
422                 if (item->dirty)
423                         vcal_folder_export(folder);
424                 item->dirty = FALSE;
425         }
426 }
427
428 static Folder *vcal_folder_new(const gchar * name,
429                                   const gchar * path)
430 {
431         VCalFolder *folder;                
432         
433         debug_print("vcal_folder_new\n");
434         folder = g_new0(VCalFolder, 1);
435         FOLDER(folder)->klass = &vcal_class;
436         folder_init(FOLDER(folder), name);
437
438         return FOLDER(folder);
439 }
440
441 static void vcal_folder_destroy(Folder *_folder)
442 {
443 }
444
445 static FolderItem *vcal_item_new(Folder *folder)
446 {
447         VCalFolderItem *item;
448         item = g_new0(VCalFolderItem, 1);
449         item->use_cal_view = 1;
450         return (FolderItem *) item;
451
452 }
453
454 static void vcal_item_destroy(Folder *folder, FolderItem *_item)
455 {
456         VCalFolderItem *item = (VCalFolderItem *)_item;
457         g_return_if_fail(item != NULL);
458         g_free(item);
459 }
460
461 static gchar *vcal_item_get_path(Folder *folder, FolderItem *item)
462 {
463         VCalFolderItem *fitem = (VCalFolderItem *)item;
464         if (fitem->uri == NULL)
465                 return g_strdup(vcal_manager_get_event_path());
466         else {
467                 gchar *path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
468                                         "vcalendar", G_DIR_SEPARATOR_S,
469                                         item->path, NULL);
470                 return path;
471         }
472 }
473
474 static gint vcal_scan_tree(Folder *folder)
475 {
476         g_return_val_if_fail(folder != NULL, -1);
477
478         folder->outbox = NULL;
479         folder->draft = NULL;
480         folder->queue = NULL;
481         folder->trash = NULL;
482
483         debug_print("scanning tree\n");
484         vcal_create_tree(folder);
485
486         return 0;
487 }
488
489 gboolean manual_update = TRUE;
490
491 static gint feed_fetch(FolderItem *fitem, MsgNumberList ** list, gboolean *old_uids_valid)
492 {
493         VCalFolderItem *item = (VCalFolderItem *)fitem;
494         icalcomponent *evt = NULL;
495         icalcomponent_kind type = ICAL_VEVENT_COMPONENT;
496         gint num = 1;
497         gint past_msg = -1, today_msg = -1, tomorrow_msg = -1, 
498                 thisweek_msg = -1, later_msg = -1;
499
500         debug_print("fetching\n");
501
502         if (!item->uri) {
503                 debug_print("no uri!\n");
504                 return -1;
505         }
506
507         update_subscription(item->uri, TRUE);
508
509         *old_uids_valid = FALSE;
510         *list = NULL;
511
512         if (item->cal) {
513                 evt = icalcomponent_get_first_component(
514                         item->cal, ICAL_VEVENT_COMPONENT);
515                 if (evt == NULL) {
516                         evt = icalcomponent_get_first_component(
517                                 item->cal, ICAL_VTODO_COMPONENT);
518                         if (evt != NULL)
519                                 type = ICAL_VTODO_COMPONENT;
520                 }
521         } else
522                 debug_print("no cal!\n");
523
524         if (evt == NULL)
525                 debug_print("no event\n");
526
527         if (item->numlist) {
528                 g_slist_free(item->numlist);
529                 item->numlist = NULL;
530         }
531
532         if (item->evtlist) {
533                 slist_free_icalfeeddata(item->evtlist);
534                 g_slist_free(item->evtlist);
535                 item->evtlist = NULL;
536         }
537
538         while (evt) {
539                 icalproperty *prop;
540                 icalproperty *rprop = icalcomponent_get_first_property(evt, ICAL_RRULE_PROPERTY);
541                 struct icalrecurrencetype recur;
542                 struct icaltimetype next;
543                 icalrecur_iterator* ritr = NULL;
544                 EventTime days;
545
546                 if (rprop) {
547                         icalproperty *rprop2;
548                         recur = icalproperty_get_rrule(rprop);
549                         rprop2 = icalproperty_new_rrule(recur);
550                         prop = icalcomponent_get_first_property(evt, ICAL_DTSTART_PROPERTY);
551                         if (prop) {
552                                 ritr = icalrecur_iterator_new(recur, icalproperty_get_dtstart(prop));
553                                 next = icalrecur_iterator_next(ritr); /* skip first one */
554                         }
555                         
556                         rprop = rprop2;
557                 } 
558
559                 prop = icalcomponent_get_first_property(evt, ICAL_UID_PROPERTY);
560                 if (prop) {
561                         gchar *orig_uid = NULL;
562                         gchar *uid = g_strdup(icalproperty_get_uid(prop));
563                         IcalFeedData *data = icalfeeddata_new(
564                                                 icalcomponent_new_clone(evt), NULL);
565                         int i = 0;
566                         orig_uid = g_strdup(uid);
567
568 add_new:                        
569                         item->numlist = g_slist_prepend(item->numlist, GINT_TO_POINTER(num));
570                         item->evtlist = g_slist_prepend(item->evtlist, data);
571                         data = NULL;
572                         debug_print("add %d : %s\n", num, uid);
573                         g_free(uid);
574                         num++;
575                         prop = icalcomponent_get_first_property(evt, ICAL_DTSTART_PROPERTY);
576                         if (prop) {
577                                 struct icaltimetype itt = icalproperty_get_dtstart(prop);
578                                 days = event_to_today(NULL, icaltime_as_timet(itt));
579                                 if (days == EVENT_PAST && past_msg == -1) {
580                                         item->numlist = g_slist_prepend(item->numlist, GINT_TO_POINTER(num));
581                                         data = icalfeeddata_new(NULL, EVENT_PAST_ID);
582                                         past_msg = num++;
583                                 } else if (days == EVENT_TODAY && today_msg == -1) {
584                                         item->numlist = g_slist_prepend(item->numlist, GINT_TO_POINTER(num));
585                                         data = icalfeeddata_new(NULL, EVENT_TODAY_ID);
586                                         today_msg = num++;
587                                 } else if (days == EVENT_TOMORROW && tomorrow_msg == -1) {
588                                         item->numlist = g_slist_prepend(item->numlist, GINT_TO_POINTER(num));
589                                         data = icalfeeddata_new(NULL, EVENT_TOMORROW_ID);
590                                         tomorrow_msg = num++;
591                                 } else if (days == EVENT_THISWEEK && thisweek_msg == -1) {
592                                         item->numlist = g_slist_prepend(item->numlist, GINT_TO_POINTER(num));
593                                         data = icalfeeddata_new(NULL, EVENT_THISWEEK_ID);
594                                         thisweek_msg = num++;
595                                 } else if (days == EVENT_LATER && later_msg == -1) {
596                                         item->numlist = g_slist_prepend(item->numlist, GINT_TO_POINTER(num));
597                                         data = icalfeeddata_new(NULL, EVENT_LATER_ID);
598                                         later_msg = num++;
599                                 }
600                         } else {
601                                 if (past_msg == -1) {
602                                         item->numlist = g_slist_prepend(item->numlist, GINT_TO_POINTER(num));
603                                         data = icalfeeddata_new(NULL, EVENT_PAST_ID);
604                                         past_msg = num++;
605                                 }
606                         }
607                         if (data) {
608                                 item->evtlist = g_slist_prepend(item->evtlist, data);
609                                 data = NULL;
610                         }
611                         if (rprop && ritr) {
612                                 struct icaldurationtype ical_dur;
613                                 struct icaltimetype dtstart = icaltime_null_time();
614                                 struct icaltimetype dtend = icaltime_null_time();
615                                 evt = icalcomponent_new_clone(evt);
616                                 prop = icalcomponent_get_first_property(evt, ICAL_RRULE_PROPERTY);
617                                 if (prop) {
618                                         icalcomponent_remove_property(evt, prop);
619                                         icalproperty_free(prop);
620                                 }
621                                 prop = icalcomponent_get_first_property(evt, ICAL_DTSTART_PROPERTY);
622                                 if (prop)
623                                         dtstart = icalproperty_get_dtstart(prop);
624                                 else
625                                         debug_print("event has no DTSTART!\n");
626                                 prop = icalcomponent_get_first_property(evt, ICAL_DTEND_PROPERTY);
627                                 if (prop)
628                                         dtend = icalproperty_get_dtend(prop);
629                                 else
630                                         debug_print("event has no DTEND!\n");
631                                 ical_dur = icaltime_subtract(dtend, dtstart);
632                                 next = icalrecur_iterator_next(ritr);
633                                 if (!icaltime_is_null_time(next) &&
634                                     !icaltime_is_null_time(dtstart) && i < 100) {
635                                         prop = icalcomponent_get_first_property(evt, ICAL_DTSTART_PROPERTY);
636                                         icalproperty_set_dtstart(prop, next);
637
638                                         prop = icalcomponent_get_first_property(evt, ICAL_DTEND_PROPERTY);
639                                         if (prop)
640                                                 icalproperty_set_dtend(prop, icaltime_add(next, ical_dur));
641
642                                         prop = icalcomponent_get_first_property(evt, ICAL_UID_PROPERTY);
643                                         uid = g_strdup_printf("%s-%d", orig_uid, i);
644                                         icalproperty_set_uid(prop, uid);
645                                         /* dont free uid, used after (add_new) */
646                                         data = icalfeeddata_new(evt, NULL);
647                                         i++;
648                                         goto add_new;
649                                 } else {
650                                         icalcomponent_free(evt);
651                                         evt = NULL;
652                                 }
653                         }
654                         g_free(orig_uid);
655                 } else {
656                         debug_print("no uid!\n");
657                 }
658                 if (rprop) {
659                         icalproperty_free(rprop);
660                 }
661                 if (ritr) {
662                         icalrecur_iterator_free(ritr);
663                         ritr = NULL;
664                 }
665                 evt = icalcomponent_get_next_component(
666                         item->cal, type);
667         }
668         if (today_msg == -1) {
669                 IcalFeedData *data = icalfeeddata_new(NULL, EVENT_TODAY_ID);
670                 item->numlist = g_slist_prepend(item->numlist, GINT_TO_POINTER(num));
671                 today_msg = num++;
672                 item->evtlist = g_slist_prepend(item->evtlist, data);
673         }
674         item->numlist = g_slist_reverse(item->numlist);
675         item->evtlist = g_slist_reverse(item->evtlist);
676         
677         *list = item->numlist ? g_slist_copy(item->numlist) : NULL;
678         debug_print("return %d\n", num);
679         return num;
680 }
681
682 #define VCAL_FOLDER_ADD_EVENT(event) \
683 { \
684  \
685         *list = g_slist_prepend(*list, GINT_TO_POINTER(n_msg)); \
686         debug_print("add %d %s\n", n_msg, event->uid); \
687         n_msg++; \
688         days = event_to_today(event, 0); \
689  \
690         if (days == EVENT_PAST && past_msg == -1) { \
691                 *list = g_slist_prepend(*list, GINT_TO_POINTER(n_msg)); \
692                 past_msg = n_msg++; \
693                 g_hash_table_insert(hash_uids, GINT_TO_POINTER(past_msg), g_strdup(EVENT_PAST_ID)); \
694         } else if (days == EVENT_TODAY && today_msg == -1) { \
695                 *list = g_slist_prepend(*list, GINT_TO_POINTER(n_msg)); \
696                 today_msg = n_msg++; \
697                 g_hash_table_insert(hash_uids, GINT_TO_POINTER(today_msg), g_strdup(EVENT_TODAY_ID)); \
698         } else if (days == EVENT_TOMORROW && tomorrow_msg == -1) { \
699                 *list = g_slist_prepend(*list, GINT_TO_POINTER(n_msg)); \
700                 tomorrow_msg = n_msg++; \
701                 g_hash_table_insert(hash_uids, GINT_TO_POINTER(tomorrow_msg), g_strdup(EVENT_TOMORROW_ID)); \
702         } else if (days == EVENT_THISWEEK && thisweek_msg == -1) { \
703                 *list = g_slist_prepend(*list, GINT_TO_POINTER(n_msg)); \
704                 thisweek_msg = n_msg++; \
705                 g_hash_table_insert(hash_uids, GINT_TO_POINTER(thisweek_msg), g_strdup(EVENT_THISWEEK_ID)); \
706         } else if (days == EVENT_LATER && later_msg == -1) { \
707                 *list = g_slist_prepend(*list, GINT_TO_POINTER(n_msg)); \
708                 later_msg = n_msg++; \
709                 g_hash_table_insert(hash_uids, GINT_TO_POINTER(later_msg), g_strdup(EVENT_LATER_ID)); \
710         } \
711 }
712
713 GSList *vcal_get_events_list(FolderItem *item)
714 {
715         GDir *dp;
716         const gchar *d;
717         GSList *events = NULL;
718         GError *error = NULL;
719
720         if (item != item->folder->inbox) {
721                 GSList *subs = vcal_folder_get_webcal_events_for_folder(item);
722                 GSList *cur = NULL;
723                 for (cur = subs; cur; cur = cur->next) {
724                         /* Don't free that, it's done when subscriptions are
725                          * fetched */
726                         icalcomponent *ical = (icalcomponent *)cur->data;
727                         VCalEvent *event = vcal_get_event_from_ical(
728                                 icalcomponent_as_ical_string(ical), NULL);
729                         events = g_slist_prepend(events, event);
730                 }
731                 g_slist_free(subs);
732                 return events;
733         }
734
735         dp = g_dir_open(vcal_manager_get_event_path(), 0, &error);
736         
737         if (!dp) {
738                 debug_print("couldn't open dir '%s': %s (%d)\n",
739                                 vcal_manager_get_event_path(), error->message, error->code);
740                 g_error_free(error);
741                 return 0;
742         }
743
744         while ((d = g_dir_read_name(dp)) != NULL) {
745                 VCalEvent *event = NULL;
746                 if (d[0] == '.' || strstr(d, ".bak")
747                 ||  !strcmp(d, "internal.ics")
748                 ||  !strcmp(d, "internal.ifb")
749                 ||  !strcmp(d, "multisync")) 
750                         continue;
751
752                 event = vcal_manager_load_event(d);
753                 if (!event)
754                         continue;
755                 if (event->rec_occurrence) {
756                         vcal_manager_free_event(event);
757                         claws_unlink(d);
758                         continue;
759                 }
760
761                 if (event && event->method != ICAL_METHOD_CANCEL) {
762                         PrefsAccount *account = vcal_manager_get_account_from_event(event);
763                         enum icalparameter_partstat status =
764                                 account ? vcal_manager_get_reply_for_attendee(event, account->address): ICAL_PARTSTAT_NEEDSACTION;
765                         if (status == ICAL_PARTSTAT_ACCEPTED
766                         ||  status == ICAL_PARTSTAT_TENTATIVE) {
767                                 events = g_slist_prepend(events, event);
768                         } else {
769                                 vcal_manager_free_event(event);
770                                 continue;
771                         }
772                         if ((status == ICAL_PARTSTAT_ACCEPTED
773                              || status == ICAL_PARTSTAT_TENTATIVE) 
774                             && event->recur && *(event->recur)) {
775                                 struct icalrecurrencetype recur;
776                                 struct icaltimetype dtstart;
777                                 struct icaltimetype next;
778                                 icalrecur_iterator* ritr;
779                                 time_t duration = (time_t) NULL;
780                                 struct icaldurationtype ical_dur;
781                                 int i = 0;
782
783                                 debug_print("dumping recurring events from main event %s\n", d);
784                                 recur = icalrecurrencetype_from_string(event->recur);
785                                 dtstart = icaltime_from_string(event->dtstart);
786
787                                 duration = icaltime_as_timet(icaltime_from_string(event->dtend))
788                                                             - icaltime_as_timet(icaltime_from_string(event->dtstart));
789
790                                 ical_dur = icaldurationtype_from_int(duration);
791
792                                 ritr = icalrecur_iterator_new(recur, dtstart);
793
794                                 next = icalrecur_iterator_next(ritr); /* skip first one */
795                                 if (!icaltime_is_null_time(next))
796                                         next = icalrecur_iterator_next(ritr);
797                                 debug_print("next time is %snull\n", icaltime_is_null_time(next)?"":"not ");
798                                 while (!icaltime_is_null_time(next) && i < 100) {
799                                         const gchar *new_start = NULL, *new_end = NULL;
800                                         VCalEvent *nevent = NULL;
801                                         gchar *uid = g_strdup_printf("%s-%d", event->uid, i);
802                                         new_start = icaltime_as_ical_string(next);
803                                         new_end = icaltime_as_ical_string(
804                                                         icaltime_add(next, ical_dur));
805                                         debug_print("adding with start/end %s:%s\n", new_start, new_end);
806                                         nevent = vcal_manager_new_event(uid, event->organizer, event->orgname, 
807                                                                 event->location, event->summary, event->description, 
808                                                                 new_start, new_end, NULL, 
809                                                                 event->tzid, event->url, event->method, 
810                                                                 event->sequence, event->type);
811                                         g_free(uid);
812                                         vcal_manager_copy_attendees(event, nevent);
813                                         nevent->rec_occurrence = TRUE;
814                                         vcal_manager_save_event(nevent, FALSE);
815                                         account = vcal_manager_get_account_from_event(event);
816                                         status =
817                                                 account ? vcal_manager_get_reply_for_attendee(event, account->address): ICAL_PARTSTAT_NEEDSACTION;
818                                         if (status == ICAL_PARTSTAT_ACCEPTED
819                                         ||  status == ICAL_PARTSTAT_TENTATIVE) {
820                                                 events = g_slist_prepend(events, nevent);
821                                         } else {
822                                                 vcal_manager_free_event(nevent);
823                                         }
824                                         next = icalrecur_iterator_next(ritr);
825                                         debug_print("next time is %snull\n", icaltime_is_null_time(next)?"":"not ");
826                                         i++;
827                                 }
828                                 icalrecur_iterator_free(ritr);
829                         }
830                 } else {
831                         vcal_manager_free_event(event);
832                 }
833         }
834         g_dir_close(dp);
835         return g_slist_reverse(events);
836 }
837
838 static gint vcal_get_num_list(Folder *folder, FolderItem *item,
839                                  MsgNumberList ** list, gboolean *old_uids_valid)
840 {
841         int n_msg = 1;
842         gint past_msg = -1, today_msg = -1, tomorrow_msg = -1, 
843                 thisweek_msg = -1, later_msg = -1;
844         GSList *events = NULL, *cur;
845         START_TIMING("");
846         g_return_val_if_fail (*list == NULL, 0); /* we expect a NULL list */
847
848         debug_print(" num for %s\n", ((VCalFolderItem *)item)->uri ? ((VCalFolderItem *)item)->uri:"(null)");
849         
850         *old_uids_valid = FALSE;
851         
852         if (((VCalFolderItem *)item)->uri) 
853                 return feed_fetch(item, list, old_uids_valid);
854         
855         events = vcal_get_events_list(item);
856         
857         debug_print("get_num_list\n");
858
859         if (hash_uids != NULL)
860                 g_hash_table_destroy(hash_uids);
861                 
862         hash_uids = g_hash_table_new_full(g_direct_hash, g_direct_equal,
863                                           NULL, g_free);
864         
865         for (cur = events; cur; cur = cur->next) {
866                 VCalEvent *event = (VCalEvent *)cur->data;
867
868                 if (!event)
869                         continue;
870                 g_hash_table_insert(hash_uids, GINT_TO_POINTER(n_msg), g_strdup(event->uid));
871                 
872                 if (event->rec_occurrence) {
873                         vcal_manager_free_event(event);
874                         continue;
875                 }
876
877                 if (event->method != ICAL_METHOD_CANCEL) {
878                         EventTime days;
879                         VCAL_FOLDER_ADD_EVENT(event);
880                 }
881                 if (event)
882                         vcal_manager_free_event(event);
883
884
885         }
886
887         if (today_msg == -1) { 
888                 *list = g_slist_prepend(*list, GINT_TO_POINTER(n_msg)); 
889                 today_msg = n_msg++; 
890                 g_hash_table_insert(hash_uids, GINT_TO_POINTER(today_msg), g_strdup(EVENT_TODAY_ID)); 
891         }
892
893         g_slist_free(events);
894         vcal_folder_export(folder);
895
896         vcal_set_mtime(folder, item);
897         
898         *list = g_slist_reverse(*list);
899         END_TIMING();
900         return g_slist_length(*list);
901 }
902
903 static MsgInfo *vcal_parse_msg(const gchar *file, FolderItem *item, int num)
904 {
905         MsgInfo *msginfo = NULL;
906         MsgFlags flags;
907
908         debug_print("parse_msg\n");
909         
910         flags.perm_flags = 0;
911         flags.tmp_flags = 0;
912         msginfo = procheader_parse_file(file, flags, TRUE, TRUE);
913         
914         msginfo->msgnum = num;
915         msginfo->folder = item;
916         return msginfo;
917 }
918
919 static MsgInfo *vcal_get_msginfo(Folder * folder,
920                                     FolderItem * item, gint num)
921 {
922         MsgInfo *msginfo = NULL;
923         gchar *file;
924
925         debug_print("get_msginfo\n");
926         
927         g_return_val_if_fail(item != NULL, NULL);
928         g_return_val_if_fail(num > 0, NULL);
929
930         file = vcal_fetch_msg(folder, item, num);
931
932         if (!file) {
933                 return NULL;
934         }
935
936         msginfo = vcal_parse_msg(file, item, num);
937
938         if (msginfo) {
939                 msginfo->flags.perm_flags = 0;
940                 msginfo->flags.tmp_flags = 0;
941
942                 vcal_change_flags(NULL, NULL, msginfo, 0);
943
944                 debug_print("  adding %d\n", num);
945         }
946         g_unlink(file);
947         g_free(file);
948
949         debug_print("  got msginfo %p\n", msginfo);
950
951         return msginfo;
952 }
953
954 static gchar *feed_fetch_item(FolderItem * fitem, gint num)
955 {
956         gchar *filename = NULL;
957         VCalFolderItem *item = (VCalFolderItem *)fitem;
958         GSList *ncur, *ecur;
959         int i = 1;
960         IcalFeedData *data = NULL;
961
962         if (!item->numlist) {
963                 folder_item_scan_full(fitem, FALSE);
964         }       
965         if (!item->numlist) {
966                 debug_print("numlist null\n");
967                 return NULL;
968         }
969
970         ncur = item->numlist;
971         ecur = item->evtlist;
972         
973         while (i < num) {
974                 if (!ncur || !ecur) {
975                         debug_print("list short end (%d to %d) %d,%d\n", i, num, ncur!=NULL, ecur!=NULL);
976                         return NULL;
977                 }
978                 ncur = ncur->next;
979                 ecur = ecur->next;
980                 i++;
981         }
982         
983         data = (IcalFeedData *)ecur->data;
984         
985         if (!data) {
986                 return NULL;
987         }
988
989         if (data->event)
990                 filename = vcal_manager_icalevent_dump(data->event, fitem->name, NULL);
991         else if (data->pseudoevent_id) {
992                 filename = vcal_manager_dateevent_dump(data->pseudoevent_id, fitem);
993                 created_files = g_slist_prepend(created_files, g_strdup(filename));
994         } else {
995                 debug_print("no event\n");
996                 return NULL;
997         }
998
999         debug_print("feed item dump to %s\n", filename);
1000         return filename;
1001 }
1002
1003 static gchar *vcal_fetch_msg(Folder * folder, FolderItem * item,
1004                                 gint num)
1005 {
1006         gchar *filename = NULL;
1007         const gchar *uid = NULL;
1008
1009         debug_print(" fetch for %s %d\n", (((VCalFolderItem *)item)->uri ? ((VCalFolderItem *)item)->uri:"(null)"), num);
1010         if (((VCalFolderItem *)item)->uri) 
1011                 return feed_fetch_item(item, num);
1012
1013         if (!uid) {
1014                 if (!hash_uids)
1015                         folder_item_scan_full(item, FALSE);
1016                 uid = g_hash_table_lookup(hash_uids, GINT_TO_POINTER(num));
1017         }
1018         if (uid && 
1019             (!strcmp(uid, EVENT_PAST_ID) ||
1020              !strcmp(uid, EVENT_TODAY_ID) ||
1021              !strcmp(uid, EVENT_TOMORROW_ID) ||
1022              !strcmp(uid, EVENT_THISWEEK_ID) ||
1023              !strcmp(uid, EVENT_LATER_ID))) {
1024                 filename = vcal_manager_dateevent_dump(uid, item);
1025         } else if (uid) {
1026                 VCalEvent *event = NULL;
1027                 event = vcal_manager_load_event(uid);
1028                 if (event)
1029                         filename = vcal_manager_event_dump(event, FALSE, TRUE, NULL, FALSE);
1030
1031                 if (filename) {
1032                         created_files = g_slist_prepend(created_files, g_strdup(filename));
1033                 }
1034
1035                 vcal_manager_free_event(event);
1036         } 
1037                 
1038         return filename;
1039 }
1040
1041 static gint vcal_add_msg(Folder *folder, FolderItem *_dest, const gchar *file, MsgFlags *flags)
1042 {
1043         gchar *contents = file_read_to_str(file);
1044         if (contents) {
1045                 vcal_add_event(contents);               
1046         }
1047         g_free(contents);
1048         return 0;
1049 }
1050
1051 static void vcal_remove_event (Folder *folder, MsgInfo *msginfo);
1052
1053 static gint vcal_remove_msg(Folder *folder, FolderItem *_item, gint num)
1054 {
1055         MsgInfo *msginfo = folder_item_get_msginfo(_item, num);
1056
1057         if (!msginfo)
1058                 return 0;
1059
1060         if (_item == folder->inbox)
1061                 vcal_remove_event(folder, msginfo);
1062
1063         procmsg_msginfo_free(&msginfo);
1064         return 0;
1065 }
1066
1067 static FolderItem *vcal_create_folder(Folder * folder,
1068                                          FolderItem * parent,
1069                                          const gchar * name)
1070 {
1071         gchar *path = NULL;
1072         FolderItem *newitem = NULL;
1073         debug_print("creating new vcal folder\n");
1074
1075         path = g_strconcat((parent->path != NULL) ? parent->path : "", ".", name, NULL);
1076         newitem = folder_item_new(folder, name, path);
1077         folder_item_append(parent, newitem);
1078         g_free(path);
1079
1080         return newitem;
1081 }
1082
1083 static gint vcal_create_tree(Folder *folder)
1084 {
1085         FolderItem *rootitem, *inboxitem;
1086         GNode *rootnode, *inboxnode;
1087
1088         if (!folder->node) {
1089                 rootitem = folder_item_new(folder, folder->name, NULL);
1090                 rootitem->folder = folder;
1091                 rootnode = g_node_new(rootitem);
1092                 folder->node = rootnode;
1093                 rootitem->node = rootnode;
1094         } else {
1095                 rootitem = FOLDER_ITEM(folder->node->data);
1096                 rootnode = folder->node;
1097         }
1098
1099         /* Add inbox folder */
1100         if (!folder->inbox) {
1101                 inboxitem = folder_item_new(folder, _("Meetings"), ".meetings");
1102                 inboxitem->folder = folder;
1103                 inboxitem->stype = F_INBOX;
1104                 inboxnode = g_node_new(inboxitem);
1105                 inboxitem->node = inboxnode;
1106                 folder->inbox = inboxitem;
1107                 g_node_append(rootnode, inboxnode);     
1108         } else {
1109                 g_free(folder->inbox->path);
1110                 folder->inbox->path = g_strdup(".meetings");
1111         }
1112
1113         debug_print("created new vcal tree\n");
1114         return 0;
1115 }
1116
1117 static gint vcal_remove_folder(Folder *folder, FolderItem *fitem)
1118 {
1119         VCalFolderItem *item = (VCalFolderItem *)fitem;
1120         if (!item->uri)
1121                 return -1;
1122         else {
1123                 if (item->feed)
1124                         g_free(item->feed);
1125                 if (item->uri)
1126                         g_free(item->uri);
1127                 item->feed = NULL;
1128                 item->uri = NULL;
1129                 folder_item_remove(fitem);
1130                 return 0;
1131         }
1132 }
1133
1134 static gboolean vcal_scan_required(Folder *folder, FolderItem *item)
1135 {
1136         GStatBuf s;
1137         VCalFolderItem *vitem = (VCalFolderItem *)item;
1138
1139         g_return_val_if_fail(item != NULL, FALSE);
1140
1141         if (vitem->uri) {
1142                 return TRUE;
1143         } else if (g_stat(vcal_manager_get_event_path(), &s) < 0) {
1144                 return TRUE;
1145         } else if ((s.st_mtime > item->mtime) &&
1146                 (s.st_mtime - 3600 != item->mtime)) {
1147                 return TRUE;
1148         }
1149         return FALSE;
1150 }
1151
1152 static gint vcal_folder_lock_count = 0;
1153
1154 static void vcal_set_mtime(Folder *folder, FolderItem *item)
1155 {
1156         GStatBuf s;
1157         gchar *path = folder_item_get_path(item);
1158
1159         if (folder->inbox != item)
1160                 return;
1161
1162         g_return_if_fail(path != NULL);
1163
1164         if (g_stat(path, &s) < 0) {
1165                 FILE_OP_ERROR(path, "stat");
1166                 g_free(path);
1167                 return;
1168         }
1169
1170         item->mtime = s.st_mtime;
1171         debug_print("VCAL: forced mtime of %s to %lld\n",
1172                         item->name?item->name:"(null)", (long long)item->mtime);
1173         g_free(path);
1174 }
1175
1176 void vcal_folder_export(Folder *folder)
1177 {       
1178         FolderItem *item = folder?folder->inbox:NULL;
1179         gboolean need_scan = folder?vcal_scan_required(folder, item):TRUE;
1180         gchar *export_pass = NULL;
1181         gchar *export_freebusy_pass = NULL;
1182
1183         if (vcal_folder_lock_count) /* blocked */
1184                 return;
1185         vcal_folder_lock_count++;
1186         
1187         export_pass = vcal_passwd_get("export");
1188         export_freebusy_pass = vcal_passwd_get("export_freebusy");
1189
1190         if (vcal_meeting_export_calendar(vcalprefs.export_path, 
1191                         vcalprefs.export_user, 
1192                         export_pass,
1193                         TRUE)) {
1194                 debug_print("exporting calendar\n");
1195                 if (vcalprefs.export_enable &&
1196                     vcalprefs.export_command &&
1197                     strlen(vcalprefs.export_command))
1198                         execute_command_line(
1199                                 vcalprefs.export_command, TRUE, NULL);
1200         }
1201         if (export_pass != NULL) {
1202                 memset(export_pass, 0, strlen(export_pass));
1203         }
1204         g_free(export_pass);
1205         if (vcal_meeting_export_freebusy(vcalprefs.export_freebusy_path,
1206                         vcalprefs.export_freebusy_user,
1207                         export_freebusy_pass)) {
1208                 debug_print("exporting freebusy\n");
1209                 if (vcalprefs.export_freebusy_enable &&
1210                     vcalprefs.export_freebusy_command &&
1211                     strlen(vcalprefs.export_freebusy_command))
1212                         execute_command_line(
1213                                 vcalprefs.export_freebusy_command, TRUE, NULL);
1214         }
1215         if (export_freebusy_pass != NULL) {
1216                 memset(export_freebusy_pass, 0, strlen(export_freebusy_pass));
1217         }
1218         g_free(export_freebusy_pass);
1219         vcal_folder_lock_count--;
1220         if (!need_scan && folder) {
1221                 vcal_set_mtime(folder, folder->inbox);
1222         }
1223 }
1224
1225 static void vcal_remove_event (Folder *folder, MsgInfo *msginfo)
1226 {
1227         const gchar *uid = msginfo->msgid;
1228         VCalFolderItem *item = (VCalFolderItem *)msginfo->folder;
1229
1230         if (uid) {
1231                 gchar *file = vcal_manager_get_event_file(uid);
1232                 g_unlink(file);
1233                 g_free(file);
1234         }
1235         
1236         if (!item || !item->batching)
1237                 vcal_folder_export(folder);
1238         else if (item) {
1239                 item->dirty = TRUE;
1240         }
1241 }
1242
1243 static void vcal_change_flags(Folder *folder, FolderItem *_item, MsgInfo *msginfo, MsgPermFlags newflags)
1244 {
1245         EventTime date;
1246
1247         if (newflags & MSG_DELETED) {
1248                 /* delete the stuff */
1249                 msginfo->flags.perm_flags |= MSG_DELETED;
1250                 vcal_remove_event(folder, msginfo);
1251                 return;
1252         }
1253
1254         /* accept the rest */
1255         msginfo->flags.perm_flags = newflags;
1256
1257         /* but not color */
1258         msginfo->flags.perm_flags &= ~MSG_CLABEL_FLAG_MASK;
1259         
1260         date = event_to_today(NULL, msginfo->date_t);
1261         switch(date) {
1262         case EVENT_PAST:
1263                 break;
1264         case EVENT_TODAY:
1265                 msginfo->flags.perm_flags |= MSG_COLORLABEL_TO_FLAGS(2); /* Red */
1266                 break;
1267         case EVENT_TOMORROW:
1268                 break;
1269         case EVENT_THISWEEK:
1270                 break;
1271         case EVENT_LATER:
1272                 break;
1273         }
1274         if (msginfo->msgid) {
1275                 if (!strcmp(msginfo->msgid, EVENT_TODAY_ID) ||
1276                     !strcmp(msginfo->msgid, EVENT_TOMORROW_ID))
1277                 msginfo->flags.perm_flags |= MSG_MARKED;
1278         }
1279 }
1280
1281 void vcal_folder_gtk_init(void)
1282 {
1283         vcal_fill_popup_menu_labels();
1284
1285         folderview_register_popup(&vcal_popup);
1286 }
1287
1288 void vcal_folder_gtk_done(void)
1289 {
1290         GSList *cur = created_files;
1291         while (cur) {
1292                 gchar *file = (gchar *)cur->data;
1293                 cur = cur->next;
1294                 if (!file)                      
1295                         continue;
1296                 debug_print("removing %s\n", file);
1297                 g_unlink(file);
1298                 g_free(file);
1299         }
1300         g_slist_free(created_files);
1301         folderview_unregister_popup(&vcal_popup);
1302 }
1303
1304 static void add_menuitems(GtkUIManager *ui_manager, FolderItem *item)
1305 {
1306         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "NewMeeting", "FolderViewPopup/NewMeeting", GTK_UI_MANAGER_MENUITEM)
1307         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "ExportCal", "FolderViewPopup/ExportCal", GTK_UI_MANAGER_MENUITEM)
1308         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorVcal1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1309         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SubscribeCal", "FolderViewPopup/SubscribeCal", GTK_UI_MANAGER_MENUITEM)
1310         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "UnsubscribeCal", "FolderViewPopup/UnsubscribeCal", GTK_UI_MANAGER_MENUITEM)
1311         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorVcal2", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1312         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "RenameFolder", "FolderViewPopup/RenameFolder", GTK_UI_MANAGER_MENUITEM)
1313         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorVcal3", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1314         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "CheckSubs", "FolderViewPopup/CheckSubs", GTK_UI_MANAGER_MENUITEM)
1315         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorVcal4", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1316         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "ListView", "FolderViewPopup/ListView", GTK_UI_MANAGER_MENUITEM)
1317         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "WeekView", "FolderViewPopup/WeekView", GTK_UI_MANAGER_MENUITEM)
1318         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "MonthView", "FolderViewPopup/MonthView", GTK_UI_MANAGER_MENUITEM)
1319         MENUITEM_ADDUI_MANAGER(ui_manager, "/Popup/FolderViewPopup", "SeparatorVcal5", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR)
1320 }
1321
1322 static gboolean setting_sensitivity = FALSE;
1323 static void set_sensitivity(GtkUIManager *ui_manager, FolderItem *fitem)
1324 {
1325         VCalFolderItem *item = (VCalFolderItem *)fitem;
1326
1327 #define SET_SENS(name, sens) \
1328         cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1329
1330         setting_sensitivity = TRUE;
1331
1332         cm_toggle_menu_set_active_full(ui_manager, "Popup/FolderViewPopup/ListView", (item->use_cal_view == 0));
1333         cm_toggle_menu_set_active_full(ui_manager, "Popup/FolderViewPopup/WeekView", (item->use_cal_view == 1));
1334         cm_toggle_menu_set_active_full(ui_manager, "Popup/FolderViewPopup/MonthView", (item->use_cal_view == 2));
1335         SET_SENS("FolderViewPopup/NewMeeting",   item->uri == NULL);
1336         SET_SENS("FolderViewPopup/ExportCal", TRUE);
1337         SET_SENS("FolderViewPopup/SubscribeCal", item->uri == NULL);
1338         SET_SENS("FolderViewPopup/UnsubscribeCal", item->uri != NULL);
1339         SET_SENS("FolderViewPopup/RenameFolder", folder_item_parent(fitem) != NULL);
1340         SET_SENS("FolderViewPopup/CheckSubs", TRUE);
1341         SET_SENS("FolderViewPopup/ListView", folder_item_parent(fitem) != NULL);
1342         SET_SENS("FolderViewPopup/WeekView", folder_item_parent(fitem) != NULL);
1343         SET_SENS("FolderViewPopup/MonthView", folder_item_parent(fitem) != NULL);
1344         setting_sensitivity = FALSE;
1345 #undef SET_SENS
1346 }
1347
1348 static void new_meeting_cb(GtkAction *action, gpointer data)
1349 {
1350         debug_print("new_meeting_cb\n");
1351         vcal_meeting_create(NULL);
1352 }
1353
1354 GSList * vcal_folder_get_waiting_events(void)
1355 {
1356         Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
1357         return vcal_get_events_list(folder->inbox);
1358 }
1359
1360 typedef struct _get_webcal_data {
1361         GSList *list;
1362         FolderItem *item;
1363 } GetWebcalData;
1364
1365 static gboolean get_webcal_events_func(GNode *node, gpointer user_data)
1366 {
1367         FolderItem *item = node->data;
1368         GetWebcalData *data = user_data;
1369         gboolean dummy = FALSE;
1370         GSList *list = NULL, *cur = NULL;
1371
1372         if (data->item && data->item != item)
1373                 return FALSE;
1374
1375         feed_fetch(item, &list, &dummy);
1376
1377         g_slist_free(list);
1378
1379         for (cur = ((VCalFolderItem *)item)->evtlist; cur; cur = cur->next) {
1380                 IcalFeedData *fdata = (IcalFeedData *)cur->data;
1381                 if (fdata->event)
1382                         data->list = g_slist_prepend(data->list, fdata->event);
1383         }
1384         return FALSE;
1385 }
1386
1387 GSList * vcal_folder_get_webcal_events(void)
1388 {
1389         GetWebcalData *data = g_new0(GetWebcalData, 1);
1390         Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
1391         GSList *list = NULL;
1392         data->item = NULL;
1393         g_node_traverse(folder->node, G_PRE_ORDER,
1394                         G_TRAVERSE_ALL, -1, get_webcal_events_func, data);
1395
1396         list = data->list;
1397         g_free(data);
1398
1399         return g_slist_reverse(list);
1400 }
1401
1402 static gboolean vcal_free_data_func(GNode *node, gpointer user_data)
1403 {
1404         VCalFolderItem *item = node->data;
1405
1406         if (item->cal) {
1407                 icalcomponent_free(item->cal);
1408                 item->cal = NULL;
1409         }
1410         if (item->numlist) {
1411                 g_slist_free(item->numlist);
1412                 item->numlist = NULL;
1413         }
1414
1415         if (item->evtlist) {
1416                 slist_free_icalfeeddata(item->evtlist);
1417                 g_slist_free(item->evtlist);
1418                 item->evtlist = NULL;
1419         }
1420
1421         return FALSE;
1422 }
1423
1424 void vcal_folder_free_data(void)
1425 {
1426         Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
1427
1428         g_node_traverse(folder->node, G_PRE_ORDER,
1429                         G_TRAVERSE_ALL, -1, vcal_free_data_func, NULL);
1430 }
1431
1432 GSList * vcal_folder_get_webcal_events_for_folder(FolderItem *item)
1433 {
1434         GetWebcalData *data = g_new0(GetWebcalData, 1);
1435         Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
1436         GSList *list = NULL;
1437         data->item = item;
1438         g_node_traverse(folder->node, G_PRE_ORDER,
1439                         G_TRAVERSE_ALL, -1, get_webcal_events_func, data);
1440
1441         list = data->list;
1442         g_free(data);
1443
1444         return g_slist_reverse(list);
1445 }
1446
1447 gchar* get_item_event_list_for_date(FolderItem *item, EventTime date)
1448 {
1449         GSList *strs = NULL;
1450         GSList *cur;
1451         gchar *result = NULL;
1452         gchar *datestr = NULL;
1453
1454         if (((VCalFolderItem *)item)->uri) {
1455                 for (cur = ((VCalFolderItem *)item)->evtlist; cur; cur = cur->next) {
1456                         IcalFeedData *fdata = (IcalFeedData *)cur->data;
1457                         icalproperty *prop;
1458                         struct icaltimetype itt;
1459                         gchar *summary = NULL;
1460                         EventTime days;
1461                         if (!fdata->event)
1462                                 continue;
1463                         prop = icalcomponent_get_first_property((icalcomponent *)fdata->event, ICAL_DTSTART_PROPERTY);
1464                         
1465                         if (!prop)
1466                                 continue;
1467                         itt = icalproperty_get_dtstart(prop);
1468                         days = event_to_today(NULL, icaltime_as_timet(itt));
1469                         if (days != date)
1470                                 continue;
1471                         prop = icalcomponent_get_first_property((icalcomponent *)fdata->event, ICAL_SUMMARY_PROPERTY);
1472                         if (prop) {
1473                                 if (!g_utf8_validate(icalproperty_get_summary(prop), -1, NULL))
1474                                         summary = conv_codeset_strdup(icalproperty_get_summary(prop), 
1475                                                 conv_get_locale_charset_str(), CS_UTF_8);
1476                                 else
1477                                         summary = g_strdup(icalproperty_get_summary(prop));
1478                         } else
1479                                 summary = g_strdup("-");
1480
1481                         strs = g_slist_prepend(strs, summary);
1482                 }
1483         } else {
1484                 GSList *evtlist = vcal_folder_get_waiting_events();
1485                 for (cur = evtlist; cur; cur = cur->next) {
1486                         VCalEvent *event = (VCalEvent *)cur->data;
1487                         EventTime days;
1488                         days = event_to_today(event, 0);
1489                         gchar *summary = NULL;
1490                         if (days == date) {
1491                                 summary = g_strdup(event->summary);
1492                                 strs = g_slist_prepend(strs, summary);
1493                         }
1494                         vcal_manager_free_event(event);
1495                 }
1496         }
1497         
1498         switch(date) {
1499         case EVENT_PAST:
1500                 datestr=_("in the past");
1501                 break;
1502         case EVENT_TODAY:
1503                 datestr=_("today");
1504                 break;
1505         case EVENT_TOMORROW:
1506                 datestr=_("tomorrow");
1507                 break;
1508         case EVENT_THISWEEK:
1509                 datestr=_("this week");
1510                 break;
1511         case EVENT_LATER:
1512                 datestr=_("later");
1513                 break;
1514         }
1515         
1516         result = g_strdup_printf(_("\nThese are the events planned %s:\n"),
1517                         datestr?datestr:"never");
1518         
1519         strs = g_slist_reverse(strs);
1520         for (cur = strs; cur; cur = cur->next) {
1521                 int e_len = strlen(result);
1522                 int n_len = strlen((gchar *)cur->data);
1523                 if (e_len) {
1524                         result = g_realloc(result, e_len+n_len+4);
1525                         *(result+e_len) = '\n';
1526                         strcpy(result+e_len+1, "- ");
1527                         strcpy(result+e_len+3, (gchar *)cur->data);
1528                 } else {
1529                         result = g_realloc(result, e_len+n_len+3);
1530                         strcpy(result+e_len, "- ");
1531                         strcpy(result+e_len+2, (gchar *)cur->data);
1532                 }
1533         }
1534         slist_free_strings(strs);
1535         g_slist_free(strs);
1536         return result;
1537 }
1538
1539 static void export_cal_cb(GtkAction *action, gpointer data)
1540 {
1541         vcal_meeting_export_calendar(NULL, NULL, NULL, FALSE);
1542 }
1543
1544 struct CBuf {
1545         gchar *str;
1546 };
1547
1548 static size_t curl_recv(void *buf, size_t size, size_t nmemb, void *stream)
1549 {
1550         struct CBuf *buffer = (struct CBuf *)stream;
1551         gchar *tmp = NULL;
1552         gchar tmpbuf[size*nmemb + 1];
1553
1554         memcpy(tmpbuf, buf, size*nmemb);
1555         tmpbuf[size*nmemb] = '\0';
1556
1557         if (buffer->str) {
1558                 tmp = g_strconcat(buffer->str, tmpbuf, NULL);
1559                 g_free(buffer->str);
1560                 buffer->str = tmp;
1561         } else {
1562                 buffer->str = g_strdup(tmpbuf);
1563         }
1564
1565         return size*nmemb;
1566 }
1567
1568 void *url_read_thread(void *data)
1569 {
1570         thread_data *td = (thread_data *)data;
1571         CURLcode res;
1572         CURL *curl_ctx = NULL;
1573         long response_code;
1574         struct CBuf buffer = { NULL };
1575         gchar *t_url = (gchar *)td->url;
1576
1577         while (*t_url == ' ')
1578                 t_url++;
1579         if (strchr(t_url, ' '))
1580                 *(strchr(t_url, ' ')) = '\0';
1581
1582 #ifdef USE_PTHREAD
1583         pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
1584         pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
1585 #endif
1586         
1587         curl_ctx = curl_easy_init();
1588         
1589         curl_easy_setopt(curl_ctx, CURLOPT_URL, t_url);
1590         curl_easy_setopt(curl_ctx, CURLOPT_WRITEFUNCTION, curl_recv);
1591         curl_easy_setopt(curl_ctx, CURLOPT_WRITEDATA, &buffer);
1592         curl_easy_setopt(curl_ctx, CURLOPT_TIMEOUT, prefs_common_get_prefs()->io_timeout_secs);
1593         curl_easy_setopt(curl_ctx, CURLOPT_NOSIGNAL, 1);
1594 #if LIBCURL_VERSION_NUM >= 0x070a00
1595         if(vcalprefs.ssl_verify_peer == FALSE) {
1596                 curl_easy_setopt(curl_ctx, CURLOPT_SSL_VERIFYPEER, 0);
1597                 curl_easy_setopt(curl_ctx, CURLOPT_SSL_VERIFYHOST, 0);
1598         }
1599 #endif
1600         curl_easy_setopt(curl_ctx, CURLOPT_USERAGENT, 
1601                 "Claws Mail vCalendar plugin "
1602                 "(" PLUGINS_URI ")");
1603         curl_easy_setopt(curl_ctx, CURLOPT_FOLLOWLOCATION, 1);
1604         res = curl_easy_perform(curl_ctx);
1605         
1606         if (res != 0) {
1607                 debug_print("res %d %s\n", res, curl_easy_strerror(res));
1608                 td->error = g_strdup(curl_easy_strerror(res));
1609                 
1610                 if(res == CURLE_OPERATION_TIMEOUTED)
1611                         log_error(LOG_PROTOCOL, _("Timeout (%d seconds) connecting to %s\n"),
1612                                 prefs_common_get_prefs()->io_timeout_secs, t_url);
1613         }
1614
1615         curl_easy_getinfo(curl_ctx, CURLINFO_RESPONSE_CODE, &response_code);
1616         if( response_code >= 400 && response_code < 500 ) {
1617                 debug_print("VCalendar: got %ld\n", response_code);
1618                 switch(response_code) {
1619                         case 401: 
1620                                 td->error = g_strdup(_("401 (Authorisation required)"));
1621                                 break;
1622                         case 403:
1623                                 td->error = g_strdup(_("403 (Unauthorised)"));
1624                                 break;
1625                         case 404:
1626                                 td->error = g_strdup(_("404 (Not found)"));
1627                                 break;
1628                         default:
1629                                 td->error = g_strdup_printf(_("Error %ld"), response_code);
1630                                 break;
1631                 }
1632         }
1633         curl_easy_cleanup(curl_ctx);
1634         if (buffer.str) {
1635                 td->result = g_strdup(buffer.str);
1636                 g_free(buffer.str);
1637         }
1638
1639         td->done = TRUE; /* let the caller thread join() */
1640         return GINT_TO_POINTER(0);
1641 }
1642
1643 gchar *vcal_curl_read(const char *url, const gchar *label, gboolean verbose, 
1644         void (*callback)(const gchar *url, gchar *data, gboolean verbose, gchar *error))
1645 {
1646         gchar *result;
1647         thread_data *td;
1648 #ifdef USE_PTHREAD
1649         pthread_t pt;
1650         pthread_attr_t pta;
1651 #endif
1652         void *res;
1653         gchar *error = NULL;
1654         result = NULL;
1655         td = g_new0(thread_data, 1);
1656         res = NULL;
1657
1658         td->url  = url;
1659         td->result  = NULL;
1660         td->done = FALSE;
1661
1662         STATUSBAR_PUSH(mainwindow_get_mainwindow(), label);
1663
1664 #ifdef USE_PTHREAD
1665         if (pthread_attr_init(&pta) != 0 ||
1666             pthread_attr_setdetachstate(&pta, PTHREAD_CREATE_JOINABLE) != 0 ||
1667             pthread_create(&pt, &pta, 
1668                         url_read_thread, td) != 0) {
1669                 url_read_thread(td);    
1670         }
1671         while (!td->done)  {
1672                 claws_do_idle();
1673         }
1674  
1675         pthread_join(pt, &res);
1676 #else
1677         url_read_thread(td);
1678 #endif
1679         
1680         result = td->result;
1681         error = td->error;
1682         g_free(td);
1683         
1684         STATUSBAR_POP(mainwindow_get_mainwindow());
1685
1686         if (callback) {
1687                 callback(url, result, verbose, error);
1688                 return NULL;
1689         } else {
1690                 if (error)
1691                         g_free(error);
1692                 return result;
1693         }
1694 }
1695
1696 gboolean vcal_curl_put(gchar *url, FILE *fp, gint filesize, const gchar *user, const gchar *pass)
1697 {
1698         gboolean res = TRUE;
1699         CURL *curl_ctx = curl_easy_init();
1700         long response_code = 0;
1701         gchar *t_url = url;
1702         gchar *userpwd = NULL;
1703
1704         struct curl_slist * headers = curl_slist_append(NULL, 
1705                 "Content-Type: text/calendar; charset=\"utf-8\"" );
1706
1707         while (*t_url == ' ')
1708                 t_url++;
1709         if (strchr(t_url, ' '))
1710                 *(strchr(t_url, ' ')) = '\0';
1711
1712         if (user && pass && *user && *pass) {
1713                 userpwd = g_strdup_printf("%s:%s",user,pass);
1714                 curl_easy_setopt(curl_ctx, CURLOPT_USERPWD, userpwd);
1715         }
1716         curl_easy_setopt(curl_ctx, CURLOPT_URL, t_url);
1717         curl_easy_setopt(curl_ctx, CURLOPT_UPLOAD, 1);
1718         curl_easy_setopt(curl_ctx, CURLOPT_READFUNCTION, NULL);
1719         curl_easy_setopt(curl_ctx, CURLOPT_READDATA, fp);
1720         curl_easy_setopt(curl_ctx, CURLOPT_HTTPHEADER, headers);
1721 #if LIBCURL_VERSION_NUM >= 0x070a00
1722         if(vcalprefs.ssl_verify_peer == FALSE) {
1723                 curl_easy_setopt(curl_ctx, CURLOPT_SSL_VERIFYPEER, 0);
1724                 curl_easy_setopt(curl_ctx, CURLOPT_SSL_VERIFYHOST, 0);
1725         }
1726 #endif
1727         curl_easy_setopt(curl_ctx, CURLOPT_USERAGENT, 
1728                 "Claws Mail vCalendar plugin "
1729                 "(" PLUGINS_URI ")");
1730         curl_easy_setopt(curl_ctx, CURLOPT_INFILESIZE, filesize);
1731         res = curl_easy_perform(curl_ctx);
1732         g_free(userpwd);
1733
1734         if (res != 0) {
1735                 debug_print("res %d %s\n", res, curl_easy_strerror(res));
1736         } else {
1737                 res = TRUE;
1738         }
1739
1740         curl_easy_getinfo(curl_ctx, CURLINFO_RESPONSE_CODE, &response_code);
1741         if (response_code < 200 || response_code >= 300) {
1742                 g_warning("Can't export calendar, got code %ld", response_code);
1743                 res = FALSE;
1744         }
1745         curl_easy_cleanup(curl_ctx);
1746         curl_slist_free_all(headers);
1747         return res;
1748 }
1749
1750 static gboolean folder_item_find_func(GNode *node, gpointer data)
1751 {
1752         FolderItem *item = node->data;
1753         gpointer *d = data;
1754         const gchar *uri = d[0];
1755
1756         if (!uri || !((VCalFolderItem *)item)->uri
1757         ||  strcmp(uri, ((VCalFolderItem *)item)->uri))
1758                 return FALSE;
1759
1760         d[1] = item;
1761
1762         return TRUE;
1763 }
1764
1765 static FolderItem *get_folder_item_for_uri(const gchar *uri)
1766 {
1767         Folder *root = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
1768         gpointer d[2];
1769         
1770         if (root == NULL)
1771                 return NULL;
1772         
1773         d[0] = (gpointer)uri;
1774         d[1] = NULL;
1775         g_node_traverse(root->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1776                         folder_item_find_func, d);
1777         return d[1];
1778 }
1779
1780 static gchar *feed_get_title(const gchar *str)
1781 {
1782         gchar *title = NULL;
1783         if (strstr(str, "X-WR-CALNAME:")) {
1784                 title = g_strdup(strstr(str, "X-WR-CALNAME:")+strlen("X-WR-CALNAME:"));
1785                 if (strstr(title, "\n"))
1786                         *(strstr(title, "\n")) = '\0';
1787                 if (strstr(title, "\r"))
1788                         *(strstr(title, "\r")) = '\0';          
1789         } else if (strstr(str, "X-WR-CALDESC:")) {
1790                 title = g_strdup(strstr(str, "X-WR-CALDESC:")+strlen("X-WR-CALDESC:"));
1791                 if (strstr(title, "\n"))
1792                         *(strstr(title, "\n")) = '\0';
1793                 if (strstr(title, "\r"))
1794                         *(strstr(title, "\r")) = '\0';          
1795         }
1796         
1797         return title;
1798 }
1799
1800 static void update_subscription_finish(const gchar *uri, gchar *feed, gboolean verbose, gchar *error)
1801 {
1802         Folder *root = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
1803         FolderItem *item = NULL;
1804         icalcomponent *cal = NULL;
1805         
1806         if (root == NULL) {
1807                 g_warning("can't get root folder");
1808                 g_free(feed);
1809                 if (error)
1810                         g_free(error);
1811                 return;
1812         }
1813
1814         if (feed == NULL) {
1815                 gchar *err_msg = _("Could not retrieve the Webcal URL:\n%s:\n\n%s");
1816
1817                 if (verbose && manual_update) {
1818                         gchar *tmp = g_strdup(uri);
1819                         if (strlen(uri) > 61) {
1820                                 tmp[55]='[';
1821                                 tmp[56]='.';
1822                                 tmp[57]='.';
1823                                 tmp[58]='.';
1824                                 tmp[59]=']';
1825                                 tmp[60]='\0';
1826                         } 
1827                         alertpanel_error(err_msg, tmp, error ? error:_("Unknown error"));
1828                         g_free(tmp);
1829                 } else  {
1830                         gchar *msg = g_strdup_printf("%s\n", err_msg);
1831                         log_error(LOG_PROTOCOL, msg, uri, error ? error:_("Unknown error"));
1832                         g_free(msg);
1833                 }
1834                 main_window_cursor_normal(mainwindow_get_mainwindow());
1835                 g_free(feed);
1836                 if (error)
1837                         g_free(error);
1838                 return;
1839         }
1840         if (strncmp(feed, "BEGIN:VCALENDAR", strlen("BEGIN:VCALENDAR"))) {
1841                 gchar *err_msg = _("This URL does not look like a Webcal URL:\n%s\n%s");
1842
1843                 if (verbose && manual_update) {
1844                         alertpanel_error(err_msg, uri, error ? error:_("Unknown error"));
1845                 } else  {
1846                         gchar *msg = g_strdup_printf("%s\n", err_msg);
1847                         log_error(LOG_PROTOCOL, msg, uri, error ? error:_("Unknown error"));
1848                         g_free(msg);
1849                 }
1850                 g_free(feed);
1851                 main_window_cursor_normal(mainwindow_get_mainwindow());
1852                 if (error)
1853                         g_free(error);
1854                 return;
1855         }
1856         
1857         if (error)
1858                 g_free(error);
1859         item = get_folder_item_for_uri(uri);
1860         if (item == NULL) {
1861                 gchar *title = feed_get_title(feed);
1862                 if (title == NULL) {
1863                         if (strstr(uri, "://"))
1864                                 title = g_path_get_basename(strstr(uri,"://")+3);
1865                         else
1866                                 title = g_strdup(uri);
1867                         subst_for_filename(title);
1868                 }
1869                 item = folder_create_folder(root->node->data, title);
1870                 if (!item) {
1871                         if (verbose && manual_update) {
1872                                 alertpanel_error(_("Could not create directory %s"),
1873                                         title);
1874                         } else  {
1875                                 log_error(LOG_PROTOCOL, _("Could not create directory %s"),
1876                                         title);
1877                         }
1878                         g_free(feed);
1879                         g_free(title);
1880                         main_window_cursor_normal(mainwindow_get_mainwindow());
1881                         return;
1882                 }
1883                 debug_print("item done %s\n", title);
1884                 ((VCalFolderItem *)item)->uri = g_strdup(uri);
1885                 ((VCalFolderItem *)item)->feed = feed;
1886                 g_free(title);
1887         } else {
1888                 if (((VCalFolderItem *)item)->feed)
1889                         g_free(((VCalFolderItem *)item)->feed);
1890
1891                 ((VCalFolderItem *)item)->feed = feed;
1892                 /* if title differs, update it */
1893         }
1894         cal = icalparser_parse_string(feed);
1895
1896         convert_to_utc(cal);
1897         
1898         if (((VCalFolderItem *)item)->cal)
1899                 icalcomponent_free(((VCalFolderItem *)item)->cal);
1900
1901         ((VCalFolderItem *)item)->cal = cal;
1902         
1903         main_window_cursor_normal(mainwindow_get_mainwindow());
1904         ((VCalFolderItem *)item)->last_fetch = time(NULL);
1905 }
1906
1907 static void update_subscription(const gchar *uri, gboolean verbose)
1908 {
1909         FolderItem *item = get_folder_item_for_uri(uri);
1910         gchar *label;
1911
1912         if (prefs_common_get_prefs()->work_offline) {
1913                 if (!verbose || 
1914                 !inc_offline_should_override(TRUE,
1915                    _("Claws Mail needs network access in order "
1916                      "to update the Webcal feed.")))
1917                         return;
1918         }
1919         if (item) {
1920                 if (time(NULL) - ((VCalFolderItem *)(item))->last_fetch < 60 && 
1921                     ((VCalFolderItem *)(item))->cal)
1922                         return;
1923         }
1924         main_window_cursor_wait(mainwindow_get_mainwindow());
1925
1926         label = g_strdup_printf(_("Fetching calendar for %s..."), 
1927                         item && item->name ? item->name : _("new subscription"));
1928         vcal_curl_read(uri, label, verbose, update_subscription_finish);
1929         g_free(label);
1930 }
1931
1932 static void check_subs_cb(GtkAction *action, gpointer data)
1933 {
1934         Folder *root = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
1935
1936         if (prefs_common_get_prefs()->work_offline && 
1937             !inc_offline_should_override(TRUE,
1938                      _("Claws Mail needs network access in order "
1939                      "to update the subscription.")))
1940                 return;
1941
1942         folderview_check_new(root);
1943 }
1944
1945 static void subscribe_cal_cb(GtkAction *action, gpointer data)
1946 {
1947         gchar *uri = NULL;
1948         gchar *tmp = NULL;
1949
1950         tmp = input_dialog(_("Subscribe to Webcal"), _("Enter the WebCal URL:"), NULL);
1951         if (tmp == NULL)
1952                 return;
1953         
1954         if (!strncmp(tmp, "http", 4)) {
1955                 uri = tmp;
1956         } else if (!strncmp(tmp, "file://", 7)) {
1957                 uri = tmp;
1958         } else if (!strncmp(tmp, "webcal", 6)) {
1959                 uri = g_strconcat("http", tmp+6, NULL);
1960                 g_free(tmp);
1961         } else {
1962                 alertpanel_error(_("Could not parse the URL."));
1963                 g_free(tmp);
1964                 return;
1965         }
1966         debug_print("uri %s\n", uri);
1967         
1968         update_subscription(uri, TRUE); 
1969         folder_write_list();
1970         g_free(uri);
1971 }
1972
1973 static void unsubscribe_cal_cb(GtkAction *action, gpointer data)
1974 {
1975         FolderView *folderview = (FolderView *)data;
1976         FolderItem *item, *opened;
1977         gchar *message;
1978         AlertValue avalue;
1979         gchar *old_id;
1980
1981         if (!folderview->selected) return;
1982
1983         item = folderview_get_selected_item(folderview);
1984         g_return_if_fail(item != NULL);
1985         g_return_if_fail(item->path != NULL);
1986         g_return_if_fail(item->folder != NULL);
1987         opened = folderview_get_opened_item(folderview);
1988
1989         message = g_strdup_printf
1990                 (_("Do you really want to unsubscribe?"));
1991         avalue = alertpanel_full(_("Delete subscription"), message,
1992                                  GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL, 
1993                                  FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
1994         g_free(message);
1995         if (avalue != G_ALERTALTERNATE) return;
1996
1997         old_id = folder_item_get_identifier(item);
1998
1999         vcal_item_closed(item);
2000
2001         if (item == opened ||
2002                         folder_is_child_of(item, opened)) {
2003                 summary_clear_all(folderview->summaryview);
2004                 folderview_close_opened(folderview, TRUE);
2005         }
2006
2007         if (item->folder->klass->remove_folder(item->folder, item) < 0) {
2008                 folder_item_scan(item);
2009                 alertpanel_error(_("Can't remove the folder '%s'."), item->name);
2010                 g_free(old_id);
2011                 return;
2012         }
2013
2014         folder_write_list();
2015
2016         prefs_filtering_delete_path(old_id);
2017         g_free(old_id);
2018 }
2019
2020 gboolean vcal_subscribe_uri(Folder *folder, const gchar *uri)
2021 {
2022         gchar *tmp = NULL;
2023         if (folder->klass != vcal_folder_get_class())
2024                 return FALSE;
2025
2026         if (uri == NULL)
2027                 return FALSE;
2028
2029         if (!strncmp(uri, "webcal", 6)) {
2030                 tmp = g_strconcat("http", uri+6, NULL);
2031         } else {
2032                 return FALSE;
2033         }
2034         debug_print("uri %s\n", tmp);
2035         
2036         update_subscription(tmp, FALSE);
2037         folder_write_list();
2038         return TRUE;
2039 }
2040
2041 static void rename_cb(GtkAction *action, gpointer data)
2042 {
2043         FolderView *folderview = (FolderView *)data;
2044         FolderItem *item;
2045         gchar *new_folder;
2046         gchar *name;
2047         gchar *message;
2048
2049         item = folderview_get_selected_item(folderview);
2050         g_return_if_fail(item != NULL);
2051         g_return_if_fail(item->path != NULL);
2052         g_return_if_fail(item->folder != NULL);
2053
2054         name = trim_string(item->name, 32);
2055         message = g_strdup_printf(_("Input new name for '%s':"), name);
2056         new_folder = input_dialog(_("Rename folder"), message, name);
2057         g_free(message);
2058         g_free(name);
2059         if (!new_folder) return;
2060         AUTORELEASE_STR(new_folder, {g_free(new_folder); return;});
2061
2062         if (strchr(new_folder, G_DIR_SEPARATOR) != NULL) {
2063                 alertpanel_error(_("'%c' can't be included in folder name."),
2064                                  G_DIR_SEPARATOR);
2065                 return;
2066         }
2067
2068         if (folder_find_child_item_by_name(folder_item_parent(item), new_folder)) {
2069                 name = trim_string(new_folder, 32);
2070                 alertpanel_error(_("The folder '%s' already exists."), name);
2071                 g_free(name);
2072                 return;
2073         }
2074
2075         if (folder_item_rename(item, new_folder) < 0) {
2076                 alertpanel_error(_("The folder could not be renamed.\n"
2077                                    "The new folder name is not allowed."));
2078                 return;
2079         }
2080
2081         folder_item_prefs_save_config_recursive(item);
2082         folder_write_list();
2083 }
2084
2085 static void set_view_cb(GtkAction *gaction, GtkRadioAction *current, gpointer data)
2086 {
2087         FolderView *folderview = (FolderView *)data;
2088         gint action = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current));
2089         FolderItem *item = NULL, *oitem = NULL;
2090
2091         if (!folderview->selected) return;
2092         if (setting_sensitivity) return;
2093
2094         oitem = folderview_get_opened_item(folderview);
2095         item = folderview_get_selected_item(folderview);
2096
2097         if (!item)
2098                 return;
2099
2100         if (((VCalFolderItem *)(item))->use_cal_view == action)
2101                 return;
2102         debug_print("set view %d\n", action);
2103         if (oitem && item == oitem && oitem->folder->klass == vcal_folder_get_class())
2104                 oitem->folder->klass->item_closed(oitem);
2105         ((VCalFolderItem *)(item))->use_cal_view = action;
2106         if (((VCalFolderItem *)(item))->use_cal_view) {
2107                 if (oitem && item == oitem && oitem->folder->klass == vcal_folder_get_class())
2108                         oitem->folder->klass->item_opened(oitem);
2109         }
2110 }
2111
2112 gchar *vcal_get_event_as_ical_str(VCalEvent *event)
2113 {
2114         gchar *ical;
2115         icalcomponent *calendar = icalcomponent_vanew(
2116             ICAL_VCALENDAR_COMPONENT,
2117             icalproperty_new_version("2.0"),
2118             icalproperty_new_prodid(
2119                  "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
2120             icalproperty_new_calscale("GREGORIAN"),
2121             (void*)0);
2122         vcal_manager_event_dump(event, FALSE, FALSE, calendar, FALSE);
2123         ical = g_strdup(icalcomponent_as_ical_string(calendar));
2124         icalcomponent_free(calendar);
2125         
2126         return ical;
2127 }
2128
2129 static gchar *get_name_from_property(icalproperty *p)
2130 {
2131         gchar *tmp = NULL;
2132         
2133         if (p && icalproperty_get_parameter_as_string(p, "CN") != NULL)
2134                 tmp = g_strdup(icalproperty_get_parameter_as_string(p, "CN"));
2135
2136         return tmp;
2137 }
2138
2139 static gchar *get_email_from_property(icalproperty *p)
2140 {
2141         gchar *tmp = NULL;
2142         gchar *email = NULL;
2143         
2144         if (p)
2145                 tmp = g_strdup(icalproperty_get_organizer(p));
2146
2147         if (!tmp) 
2148                 return NULL;
2149
2150         if (!strncasecmp(tmp, "MAILTO:", strlen("MAILTO:")))
2151                 email = g_strdup(tmp+strlen("MAILTO:"));
2152         else
2153                 email = g_strdup(tmp);
2154         g_free(tmp);
2155         
2156         return email;
2157 }
2158
2159 static void convert_to_utc(icalcomponent *calendar)
2160 {
2161         icalcomponent *event;
2162         icaltimezone *tz, *tzutc = icaltimezone_get_utc_timezone();
2163         icalproperty *prop;
2164         icalparameter *tzid;
2165
2166         cm_return_if_fail(calendar != NULL);
2167
2168         for (
2169                         event = icalcomponent_get_first_component(calendar,
2170                                 ICAL_VEVENT_COMPONENT);
2171                         event != NULL;
2172                         event = icalcomponent_get_next_component(calendar,
2173                                 ICAL_VEVENT_COMPONENT)) {
2174
2175                 /* DTSTART */
2176                 if ((prop = icalcomponent_get_first_property(event, ICAL_DTSTART_PROPERTY)) != NULL
2177                                 && (tzid = icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER)) != NULL) {
2178                         /* Event has its DTSTART with a timezone specification, let's convert
2179                          * to UTC and remove the TZID parameter. */
2180
2181                         tz = icalcomponent_get_timezone(calendar, icalparameter_get_iana_value(tzid));
2182                         if (tz != NULL) {
2183                                 debug_print("Converting DTSTART to UTC.\n");
2184                                 icaltimetype t = icalproperty_get_dtstart(prop);
2185                                 icaltimezone_convert_time(&t, tz, tzutc);
2186                                 icalproperty_set_dtstart(prop, t);
2187                                 icalproperty_remove_parameter_by_ref(prop, tzid);
2188                         }
2189                 }
2190
2191                 /* DTEND */
2192                 if ((prop = icalcomponent_get_first_property(event, ICAL_DTEND_PROPERTY)) != NULL
2193                                 && (tzid = icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER)) != NULL) {
2194                         /* Event has its DTEND with a timezone specification, let's convert
2195                          * to UTC and remove the TZID parameter. */
2196
2197                         tz = icalcomponent_get_timezone(calendar, icalparameter_get_iana_value(tzid));
2198                         if (tz != NULL) {
2199                                 debug_print("Converting DTEND to UTC.\n");
2200                                 icaltimetype t = icalproperty_get_dtend(prop);
2201                                 icaltimezone_convert_time(&t, tz, tzutc);
2202                                 icalproperty_set_dtend(prop, t);
2203                                 icalproperty_remove_parameter_by_ref(prop, tzid);
2204                         }
2205                 }
2206         }
2207 }
2208
2209 #define GET_PROP(comp,prop,kind) {                                              \
2210         prop = NULL;                                                            \
2211         if (!(prop = icalcomponent_get_first_property(comp, kind))) {           \
2212                 prop = inner                                                    \
2213                         ? icalcomponent_get_first_property(inner, kind)         \
2214                         : NULL;                                                 \
2215         }                                                                       \
2216 }
2217
2218 #define GET_PROP_LIST(comp,list,kind) {                                         \
2219         list = NULL;                                                            \
2220         if (!(prop = icalcomponent_get_first_property(comp, kind))) {           \
2221                 prop = inner                                                    \
2222                         ? icalcomponent_get_first_property(inner, kind)         \
2223                         : NULL;                                                 \
2224                 if (prop) do {                                                  \
2225                         list = g_slist_prepend(list, prop);                     \
2226                 } while ((prop = icalcomponent_get_next_property(inner, kind)));\
2227         }else do {                                                              \
2228                 list = g_slist_prepend(list, prop);                             \
2229         } while ((prop = icalcomponent_get_next_property(comp, kind)));         \
2230 }
2231
2232 #define TO_UTF8(string) {                                                       \
2233         if (string && !g_utf8_validate(string, -1, NULL)) {                     \
2234                 gchar *tmp = conv_codeset_strdup(string,                        \
2235                                 charset ? charset:conv_get_locale_charset_str(),\
2236                                 CS_UTF_8);                                      \
2237                 g_free(string);                                                 \
2238                 string = tmp;                                                   \
2239         }                                                                       \
2240 }
2241
2242 VCalEvent *vcal_get_event_from_ical(const gchar *ical, const gchar *charset)
2243 {
2244         VCalEvent *event = NULL;
2245         gchar *int_ical = g_strdup(ical);
2246         icalcomponent *comp = icalcomponent_new_from_string(int_ical);
2247         icalcomponent *inner = NULL;
2248         icalproperty *prop = NULL;
2249         GSList *list = NULL, *cur = NULL;
2250         gchar *uid = NULL;
2251         gchar *location = NULL;
2252         gchar *summary = NULL;
2253         gchar *dtstart = NULL;
2254         gchar *dtend = NULL;
2255         gchar *org_email = NULL, *org_name = NULL;
2256         gchar *description = NULL;
2257         gchar *url = NULL;
2258         gchar *tzid = NULL;
2259         gchar *recur = NULL;
2260         int sequence = 0;
2261         enum icalproperty_method method = ICAL_METHOD_REQUEST;
2262         enum icalcomponent_kind type = ICAL_VEVENT_COMPONENT;
2263         GSList *attendees = NULL;
2264         
2265         if (comp == NULL) {
2266                 g_free(int_ical);
2267                 return NULL;
2268         }
2269
2270         if ((inner = icalcomponent_get_inner(comp)) != NULL)
2271             type = icalcomponent_isa(inner);
2272
2273         GET_PROP(comp, prop, ICAL_UID_PROPERTY);
2274         if (prop) {
2275                 uid = g_strdup(icalproperty_get_uid(prop));
2276                 TO_UTF8(uid);
2277                 icalproperty_free(prop);
2278         }
2279         GET_PROP(comp, prop, ICAL_LOCATION_PROPERTY);
2280         if (prop) {
2281                 location = g_strdup(icalproperty_get_location(prop));
2282                 TO_UTF8(location);
2283                 icalproperty_free(prop);
2284         }
2285         GET_PROP(comp, prop, ICAL_SUMMARY_PROPERTY);
2286         if (prop) {
2287                 summary = g_strdup(icalproperty_get_summary(prop));
2288                 TO_UTF8(summary);
2289                 icalproperty_free(prop);
2290         }
2291
2292         convert_to_utc(comp);
2293
2294         GET_PROP(comp, prop, ICAL_DTSTART_PROPERTY);
2295         if (prop) {
2296                 dtstart = g_strdup(icaltime_as_ical_string(icalproperty_get_dtstart(prop)));
2297                 TO_UTF8(dtstart);
2298                 icalproperty_free(prop);
2299         }
2300         GET_PROP(comp, prop, ICAL_DTEND_PROPERTY);
2301         if (prop) {
2302                 dtend = g_strdup(icaltime_as_ical_string(icalproperty_get_dtend(prop)));
2303                 TO_UTF8(dtend);
2304                 icalproperty_free(prop);
2305         } else {
2306                 GET_PROP(comp, prop, ICAL_DURATION_PROPERTY);
2307                 if (prop) {
2308                         struct icaldurationtype duration = icalproperty_get_duration(prop);
2309                         struct icaltimetype itt;
2310                         icalproperty_free(prop);
2311                         GET_PROP(comp, prop, ICAL_DTSTART_PROPERTY);
2312                         if (prop) {
2313                                 itt = icalproperty_get_dtstart(prop);
2314                                 icalproperty_free(prop);
2315                                 dtend = g_strdup(icaltime_as_ical_string(icaltime_add(itt,duration)));
2316                                 TO_UTF8(dtend);
2317                         }
2318                 }
2319         }
2320         GET_PROP(comp, prop, ICAL_SEQUENCE_PROPERTY);
2321         if (prop) {
2322                 sequence = icalproperty_get_sequence(prop);
2323                 icalproperty_free(prop);
2324         }
2325         GET_PROP(comp, prop, ICAL_METHOD_PROPERTY);
2326         if (prop) {
2327                 method = icalproperty_get_method(prop);
2328                 icalproperty_free(prop);
2329         }
2330         GET_PROP(comp, prop, ICAL_ORGANIZER_PROPERTY);
2331         if (prop) {
2332                 org_email = get_email_from_property(prop);
2333                 TO_UTF8(org_email);
2334                 org_name = get_name_from_property(prop);
2335                 TO_UTF8(org_name);
2336                 icalproperty_free(prop);
2337         }
2338         GET_PROP(comp, prop, ICAL_DESCRIPTION_PROPERTY);
2339         if (prop) {
2340                 description = g_strdup(icalproperty_get_description(prop));
2341                 TO_UTF8(description);
2342                 icalproperty_free(prop);
2343         }
2344         GET_PROP(comp, prop, ICAL_URL_PROPERTY);
2345         if (prop) {
2346                 url = g_strdup(icalproperty_get_url(prop));
2347                 TO_UTF8(url);
2348                 icalproperty_free(prop);
2349         }
2350         GET_PROP(comp, prop, ICAL_TZID_PROPERTY);
2351         if (prop) {
2352                 tzid = g_strdup(icalproperty_get_tzid(prop));
2353                 TO_UTF8(tzid);
2354                 icalproperty_free(prop);
2355         }
2356         GET_PROP(comp, prop, ICAL_RRULE_PROPERTY);
2357         if (prop) {
2358                 struct icalrecurrencetype rrule = icalproperty_get_rrule(prop);
2359                 recur = g_strdup(icalrecurrencetype_as_string(&rrule));
2360                 TO_UTF8(recur);
2361                 icalproperty_free(prop);
2362         }
2363         GET_PROP_LIST(comp, list, ICAL_ATTENDEE_PROPERTY);
2364         for (cur = list; cur; cur = cur->next) {
2365                 enum icalparameter_partstat partstat = 0;
2366                 enum icalparameter_cutype cutype = 0;
2367                 icalparameter *param = NULL;
2368                 gchar *email = NULL;
2369                 gchar *name = NULL;
2370                 Answer *answer = NULL;
2371
2372                 prop = (icalproperty *)(cur->data);
2373
2374                 email = get_email_from_property(prop);
2375                 TO_UTF8(email);
2376                 name = get_name_from_property(prop);
2377                 TO_UTF8(name);
2378
2379                 param = icalproperty_get_first_parameter(prop, ICAL_PARTSTAT_PARAMETER);
2380                 if (param)
2381                         partstat = icalparameter_get_partstat(param);
2382
2383                 param = icalproperty_get_first_parameter(prop, ICAL_CUTYPE_PARAMETER);
2384                 if (param)
2385                         cutype= icalparameter_get_cutype(param);
2386                 
2387                 if (!partstat)
2388                         partstat = ICAL_PARTSTAT_NEEDSACTION;
2389                 if (!cutype)
2390                         cutype = ICAL_CUTYPE_INDIVIDUAL;
2391                 answer = answer_new(email, name, partstat, cutype);
2392                 attendees = g_slist_prepend(attendees, answer);
2393                 g_free(email);
2394                 g_free(name);
2395                 icalproperty_free(prop);
2396         }
2397         g_slist_free(list);
2398         
2399         event = vcal_manager_new_event  (uid, org_email, org_name,
2400                                          location, summary, description,
2401                                          dtstart, dtend, recur,
2402                                          tzid, url,
2403                                          method, sequence, type);
2404         event->answers = attendees;
2405         g_free(uid);
2406         g_free(location);
2407         g_free(summary);
2408         g_free(dtstart);
2409         g_free(dtend);
2410         g_free(org_email);
2411         g_free(org_name);
2412         g_free(description);
2413         g_free(url);
2414         g_free(tzid);
2415         g_free(recur);
2416         g_free(int_ical);
2417         icalcomponent_free(comp);
2418         return event;
2419 }
2420
2421 gboolean vcal_event_exists(const gchar *id)
2422 {
2423         MsgInfo *info = NULL;
2424         Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
2425         if (!folder)
2426                 return FALSE;
2427
2428         info = folder_item_get_msginfo_by_msgid(folder->inbox, id);
2429         if (info != NULL) {
2430                 procmsg_msginfo_free(&info);
2431                 return TRUE;
2432         }
2433         return FALSE;
2434 }
2435
2436 void vcal_foreach_event(gboolean (*cb_func)(const gchar *vevent))
2437 {
2438         GSList *list = vcal_folder_get_waiting_events();
2439         GSList *cur = NULL;
2440         if (!cb_func)
2441                 return;
2442         debug_print("calling cb_func...\n");
2443         for (cur = list; cur; cur = cur->next) {
2444                 VCalEvent *event = (VCalEvent *)cur->data;
2445                 gchar *tmp = vcal_get_event_as_ical_str(event);
2446                 if (tmp) {
2447                         debug_print(" ...for event %s\n", event->uid);
2448                         cb_func(tmp);
2449                 }
2450                 vcal_manager_free_event(event);
2451                 g_free(tmp);
2452         }
2453 }
2454
2455 /* please call vcalendar_refresh_folder_contents() after one or more 
2456  * calls to this function */
2457 gboolean vcal_delete_event(const gchar *id)
2458 {
2459         MsgInfo *info = NULL;
2460         Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
2461         if (!folder)
2462                 return FALSE;
2463
2464         info = folder_item_get_msginfo_by_msgid(folder->inbox, id);
2465         if (info != NULL) {
2466                 debug_print("removing event %s\n", id);
2467                 vcal_remove_event(folder, info);
2468                 procmsg_msginfo_free(&info);
2469                 folder_item_scan(folder->inbox);
2470                 return TRUE;
2471         }
2472         debug_print("not removing unexisting event %s\n", id);
2473         return FALSE;
2474 }
2475
2476 /* please call vcalendar_refresh_folder_contents() after one or more 
2477  * calls to this function */
2478 gchar* vcal_add_event(const gchar *vevent)
2479 {
2480         VCalEvent *event = vcal_get_event_from_ical(vevent, NULL);
2481         gchar *retVal = NULL;
2482         Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
2483         if (!folder)
2484                 return NULL;
2485
2486         if (event) {
2487                 if (vcal_event_exists(event->uid)) {
2488                         debug_print("event %s already exists\n", event->uid);
2489                         vcal_manager_free_event(event);
2490                         return retVal;
2491                 }
2492                 debug_print("adding event %s\n", event->uid);
2493                 if (!account_find_from_address(event->organizer, FALSE) &&
2494                     !vcal_manager_get_account_from_event(event)) {
2495                         PrefsAccount *account = account_get_default();
2496                         vcal_manager_update_answer(event, account->address, 
2497                                         account->name,
2498                                         ICAL_PARTSTAT_ACCEPTED, 
2499                                         ICAL_CUTYPE_INDIVIDUAL);
2500                         debug_print("can't find our accounts in event, adding default\n");
2501                 }
2502                 vcal_manager_save_event(event, TRUE);
2503                 folder_item_scan(folder->inbox);
2504                 retVal = vcal_get_event_as_ical_str(event);
2505                 vcal_manager_free_event(event);
2506         }
2507
2508         return retVal;
2509 }
2510
2511 /* please call vcalendar_refresh_folder_contents() after one or more 
2512  * calls to this function */
2513 gchar* vcal_update_event(const gchar *vevent)
2514 {
2515         VCalEvent *event = vcal_get_event_from_ical(vevent, NULL);
2516         gboolean r = FALSE;
2517         if (event) {
2518                 r = vcal_delete_event(event->uid);
2519                 vcal_manager_free_event(event);
2520                 if (r)
2521                         return vcal_add_event(vevent);
2522         }
2523         return NULL;
2524 }