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