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