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