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