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