2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2022 Colin Leroy <colin@colino.net> and
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.
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.
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/>.
22 #include "claws-features.h"
27 #include <glib/gi18n.h>
31 #include <sys/types.h>
35 #include <curl/curl.h>
36 #include <curl/curlver.h>
40 #include "file-utils.h"
43 #include "procheader.h"
45 #include "folderview.h"
46 #include "folder_item_prefs.h"
47 #include "vcalendar.h"
48 #include "vcal_folder.h"
49 #include "vcal_prefs.h"
50 #include "vcal_manager.h"
51 #include "vcal_meeting_gtk.h"
52 #include "vcal_interface.h"
53 #include "prefs_account.h"
54 #include "prefs_common.h"
57 #include "inputdialog.h"
60 #include "alertpanel.h"
62 #include "mainwindow.h"
63 #include "statusbar.h"
65 #include "passwordstore.h"
67 #include "messageview.h"
76 typedef struct _thread_data
{
83 typedef struct _IcalFeedData
{
85 gchar
*pseudoevent_id
;
88 typedef struct _VCalFolder VCalFolder
;
89 typedef struct _VCalFolderItem VCalFolderItem
;
91 static Folder
*vcal_folder_new(const gchar
* name
,
92 const gchar
* folder
);
93 static void vcal_folder_destroy(Folder
* folder
);
94 static void vcal_item_destroy(Folder
*folder
, FolderItem
*_item
);
95 static gchar
*vcal_item_get_path(Folder
*folder
, FolderItem
*item
);
97 static gint
vcal_scan_tree(Folder
* folder
);
98 static FolderItem
*vcal_item_new(Folder
* folder
);
99 static gint
vcal_get_num_list(Folder
* folder
, FolderItem
* item
,
100 MsgNumberList
** list
,
101 gboolean
* old_uids_valid
);
102 static MsgInfo
*vcal_get_msginfo(Folder
* folder
, FolderItem
* item
,
104 static gchar
*vcal_fetch_msg(Folder
* folder
, FolderItem
* item
,
106 static gint
vcal_add_msg(Folder
* folder
, FolderItem
* _dest
,
107 const gchar
* file
, MsgFlags
* flags
);
108 static gint
vcal_remove_msg(Folder
* folder
, FolderItem
* _item
,
110 static FolderItem
*vcal_create_folder(Folder
* folder
,
113 static gint
vcal_create_tree(Folder
*folder
);
114 static gint
vcal_remove_folder(Folder
*folder
, FolderItem
*item
);
115 static gboolean
vcal_scan_required(Folder
*folder
, FolderItem
*item
);
116 static void vcal_set_mtime(Folder
*folder
, FolderItem
*item
);
117 static void vcal_change_flags(Folder
*folder
, FolderItem
*_item
, MsgInfo
*msginfo
, MsgPermFlags newflags
);
119 static void new_meeting_cb(GtkAction
*action
, gpointer data
);
120 static void export_cal_cb(GtkAction
*action
, gpointer data
);
121 static void subscribe_cal_cb(GtkAction
*action
, gpointer data
);
122 static void check_subs_cb(GtkAction
*action
, gpointer data
);
123 static void unsubscribe_cal_cb(GtkAction
*action
, gpointer data
);
124 static void rename_cb(GtkAction
*action
, gpointer data
);
125 static void set_view_cb(GtkAction
*action
, GtkRadioAction
*current
, gpointer data
);
127 static void add_menuitems(GtkUIManager
*ui_manager
, FolderItem
*item
);
128 static void set_sensitivity(GtkUIManager
*ui_manager
, FolderItem
*item
);
130 static void update_subscription(const gchar
*uri
, gboolean verbose
);
131 static void vcal_folder_set_batch (Folder
*folder
,
134 static void convert_to_utc(icalcomponent
*calendar
);
136 gboolean
vcal_subscribe_uri(Folder
*folder
, const gchar
*uri
);
138 FolderClass vcal_class
;
140 static GSList
*created_files
= NULL
;
141 static GHashTable
*hash_uids
= NULL
;
148 struct _VCalFolderItem
164 static char *vcal_popup_labels
[] =
166 N_("_New meeting..."),
167 N_("_Export calendar..."),
168 N_("_Subscribe to Webcal..."),
169 N_("_Unsubscribe..."),
171 N_("U_pdate subscriptions"),
178 static GtkActionEntry vcal_popup_entries
[] =
180 {"FolderViewPopup/NewMeeting", NULL
, NULL
, NULL
, NULL
, G_CALLBACK(new_meeting_cb
) },
181 {"FolderViewPopup/ExportCal", NULL
, NULL
, NULL
, NULL
, G_CALLBACK(export_cal_cb
) },
183 {"FolderViewPopup/SubscribeCal", NULL
, NULL
, NULL
, NULL
, G_CALLBACK(subscribe_cal_cb
) },
184 {"FolderViewPopup/UnsubscribeCal", NULL
, NULL
, NULL
, NULL
, G_CALLBACK(unsubscribe_cal_cb
) },
186 {"FolderViewPopup/RenameFolder", NULL
, NULL
, NULL
, NULL
, G_CALLBACK(rename_cb
) },
188 {"FolderViewPopup/CheckSubs", NULL
, NULL
, NULL
, NULL
, G_CALLBACK(check_subs_cb
) },
192 static GtkRadioActionEntry vcal_popup_radio_entries
[] = { /* set_view_cb */
193 {"FolderViewPopup/ListView", NULL
, NULL
, NULL
, NULL
, 0 },
194 {"FolderViewPopup/WeekView", NULL
, NULL
, NULL
, NULL
, 1 },
195 {"FolderViewPopup/MonthView", NULL
, NULL
, NULL
, NULL
, 2 },
198 static IcalFeedData
*icalfeeddata_new(icalcomponent
*evt
, gchar
*str
)
200 IcalFeedData
*data
= g_new0(IcalFeedData
, 1);
202 data
->pseudoevent_id
= g_strdup(str
);
207 static void icalfeeddata_free(IcalFeedData
*data
)
209 g_free(data
->pseudoevent_id
);
211 icalcomponent_free(data
->event
);
215 static void slist_free_icalfeeddata(GSList
*list
)
218 IcalFeedData
*data
= (IcalFeedData
*)list
->data
;
219 icalfeeddata_free(data
);
224 static void vcal_fill_popup_menu_labels(void)
226 vcal_popup_entries
[0].label
= _(vcal_popup_labels
[0]);
227 vcal_popup_entries
[1].label
= _(vcal_popup_labels
[1]);
228 vcal_popup_entries
[2].label
= _(vcal_popup_labels
[2]);
229 vcal_popup_entries
[3].label
= _(vcal_popup_labels
[3]);
230 vcal_popup_entries
[4].label
= _(vcal_popup_labels
[4]);
231 vcal_popup_entries
[5].label
= _(vcal_popup_labels
[5]);
232 vcal_popup_radio_entries
[0].label
= _(vcal_popup_labels
[6]);
233 vcal_popup_radio_entries
[1].label
= _(vcal_popup_labels
[7]);
234 vcal_popup_radio_entries
[2].label
= _(vcal_popup_labels
[8]);
237 static FolderViewPopup vcal_popup
=
242 G_N_ELEMENTS(vcal_popup_entries
),
244 vcal_popup_radio_entries
,
245 G_N_ELEMENTS(vcal_popup_radio_entries
), 1, set_view_cb
,
250 static void vcal_item_set_xml(Folder
*folder
, FolderItem
*item
, XMLTag
*tag
)
253 folder_item_set_xml(folder
, item
, tag
);
254 gboolean found_cal_view_setting
= FALSE
;
256 for (cur
= tag
->attr
; cur
!= NULL
; cur
= g_list_next(cur
)) {
257 XMLAttr
*attr
= (XMLAttr
*) cur
->data
;
259 if (!attr
|| !attr
->name
|| !attr
->value
) continue;
260 if (!strcmp(attr
->name
, "uri")) {
261 if (((VCalFolderItem
*)item
)->uri
!= NULL
)
262 g_free(((VCalFolderItem
*)item
)->uri
);
263 ((VCalFolderItem
*)item
)->uri
= g_strdup(attr
->value
);
265 if (!strcmp(attr
->name
, "use_cal_view")) {
266 found_cal_view_setting
= TRUE
;
267 ((VCalFolderItem
*)item
)->use_cal_view
= atoi(attr
->value
);
270 if (((VCalFolderItem
*)item
)->uri
== NULL
) {
271 /* give a path to inbox */
273 item
->path
= g_strdup(".meetings");
275 if (!found_cal_view_setting
)
276 ((VCalFolderItem
*)item
)->use_cal_view
= 1; /*week view */
280 static XMLTag
*vcal_item_get_xml(Folder
*folder
, FolderItem
*item
)
284 tag
= folder_item_get_xml(folder
, item
);
286 if (((VCalFolderItem
*)item
)->uri
)
287 xml_tag_add_attr(tag
, xml_attr_new("uri", ((VCalFolderItem
*)item
)->uri
));
289 xml_tag_add_attr(tag
, xml_attr_new_int("use_cal_view", ((VCalFolderItem
*)item
)->use_cal_view
));
294 static gint
vcal_rename_folder(Folder
*folder
, FolderItem
*item
,
300 item
->name
= g_strdup(name
);
304 static void vcal_get_sort_type(Folder
*folder
, FolderSortKey
*sort_key
,
305 FolderSortType
*sort_type
)
308 *sort_key
= SORT_BY_DATE
;
311 static void vcal_item_opened(FolderItem
*item
)
314 time_t t
= time(NULL
);
316 localtime_r(&t
, &tmdate
);
320 tmdate
= *localtime(&t
);
322 if (!((VCalFolderItem
*)(item
))->dw
323 && ((VCalFolderItem
*)(item
))->use_cal_view
== 1)
324 ((VCalFolderItem
*)(item
))->dw
= create_day_win(item
, tmdate
);
325 else if (!((VCalFolderItem
*)(item
))->mw
326 && ((VCalFolderItem
*)(item
))->use_cal_view
== 2)
327 ((VCalFolderItem
*)(item
))->mw
= create_month_win(item
, tmdate
);
328 else if (((VCalFolderItem
*)(item
))->use_cal_view
!= 0)
329 vcal_folder_refresh_cal(item
);
332 void vcal_folder_refresh_cal(FolderItem
*item
)
334 Folder
*folder
= folder_find_from_name (PLUGIN_NAME
, vcal_folder_get_class());
335 if (item
->folder
!= folder
)
337 if (((VCalFolderItem
*)(item
))->dw
)
338 refresh_day_win(((VCalFolderItem
*)(item
))->dw
);
339 if (((VCalFolderItem
*)(item
))->mw
)
340 refresh_month_win(((VCalFolderItem
*)(item
))->mw
);
343 static void vcal_item_closed(FolderItem
*item
)
345 if (((VCalFolderItem
*)(item
))->dw
) {
346 dw_close_window(((VCalFolderItem
*)(item
))->dw
);
347 ((VCalFolderItem
*)(item
))->dw
= NULL
;
349 if (((VCalFolderItem
*)(item
))->mw
) {
350 mw_close_window(((VCalFolderItem
*)(item
))->mw
);
351 ((VCalFolderItem
*)(item
))->mw
= NULL
;
355 FolderClass
*vcal_folder_get_class()
357 if (vcal_class
.idstr
== NULL
) {
358 debug_print("register class\n");
359 vcal_class
.type
= F_UNKNOWN
;
360 vcal_class
.idstr
= PLUGIN_NAME
;
361 vcal_class
.uistr
= PLUGIN_NAME
;
363 /* Folder functions */
364 vcal_class
.new_folder
= vcal_folder_new
;
365 vcal_class
.destroy_folder
= vcal_folder_destroy
;
366 vcal_class
.set_xml
= folder_set_xml
;
367 vcal_class
.get_xml
= folder_get_xml
;
368 vcal_class
.item_set_xml
= vcal_item_set_xml
;
369 vcal_class
.item_get_xml
= vcal_item_get_xml
;
370 vcal_class
.scan_tree
= vcal_scan_tree
;
371 vcal_class
.create_tree
= vcal_create_tree
;
372 vcal_class
.get_sort_type
= vcal_get_sort_type
;
374 /* FolderItem functions */
375 vcal_class
.item_new
= vcal_item_new
;
376 vcal_class
.item_destroy
= vcal_item_destroy
;
377 vcal_class
.item_get_path
= vcal_item_get_path
;
378 vcal_class
.create_folder
= vcal_create_folder
;
379 vcal_class
.remove_folder
= vcal_remove_folder
;
380 vcal_class
.rename_folder
= vcal_rename_folder
;
381 vcal_class
.scan_required
= vcal_scan_required
;
382 vcal_class
.set_mtime
= vcal_set_mtime
;
383 vcal_class
.get_num_list
= vcal_get_num_list
;
384 vcal_class
.set_batch
= vcal_folder_set_batch
;
386 /* Message functions */
387 vcal_class
.get_msginfo
= vcal_get_msginfo
;
388 vcal_class
.fetch_msg
= vcal_fetch_msg
;
389 vcal_class
.add_msg
= vcal_add_msg
;
390 vcal_class
.copy_msg
= NULL
;
391 vcal_class
.remove_msg
= vcal_remove_msg
;
392 vcal_class
.change_flags
= vcal_change_flags
;
393 vcal_class
.subscribe
= vcal_subscribe_uri
;
395 /* FolderView functions */
396 vcal_class
.item_opened
= vcal_item_opened
;
397 vcal_class
.item_closed
= vcal_item_closed
;
398 debug_print("registered class for real\n");
404 static void vcal_folder_set_batch (Folder
*folder
,
408 VCalFolderItem
*item
= (VCalFolderItem
*)_item
;
410 g_return_if_fail(item
!= NULL
);
412 if (item
->batching
== batch
)
416 item
->batching
= TRUE
;
417 debug_print("vcal switching to batch mode\n");
419 debug_print("vcal switching away from batch mode\n");
421 item
->batching
= FALSE
;
423 vcal_folder_export(folder
);
428 static Folder
*vcal_folder_new(const gchar
* name
,
433 debug_print("vcal_folder_new\n");
434 folder
= g_new0(VCalFolder
, 1);
435 FOLDER(folder
)->klass
= &vcal_class
;
436 folder_init(FOLDER(folder
), name
);
438 return FOLDER(folder
);
441 static void vcal_folder_destroy(Folder
*_folder
)
445 static FolderItem
*vcal_item_new(Folder
*folder
)
447 VCalFolderItem
*item
;
448 item
= g_new0(VCalFolderItem
, 1);
449 item
->use_cal_view
= 1;
450 return (FolderItem
*) item
;
454 static void vcal_item_destroy(Folder
*folder
, FolderItem
*_item
)
456 VCalFolderItem
*item
= (VCalFolderItem
*)_item
;
457 g_return_if_fail(item
!= NULL
);
461 static gchar
*vcal_item_get_path(Folder
*folder
, FolderItem
*item
)
463 VCalFolderItem
*fitem
= (VCalFolderItem
*)item
;
464 if (fitem
->uri
== NULL
)
465 return g_strdup(vcal_manager_get_event_path());
467 gchar
*path
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
468 "vcalendar", G_DIR_SEPARATOR_S
,
474 static gint
vcal_scan_tree(Folder
*folder
)
476 g_return_val_if_fail(folder
!= NULL
, -1);
478 folder
->outbox
= NULL
;
479 folder
->draft
= NULL
;
480 folder
->queue
= NULL
;
481 folder
->trash
= NULL
;
483 debug_print("scanning tree\n");
484 vcal_create_tree(folder
);
489 gboolean manual_update
= TRUE
;
491 static gint
feed_fetch(FolderItem
*fitem
, MsgNumberList
** list
, gboolean
*old_uids_valid
)
493 VCalFolderItem
*item
= (VCalFolderItem
*)fitem
;
494 icalcomponent
*evt
= NULL
;
495 icalcomponent_kind type
= ICAL_VEVENT_COMPONENT
;
497 gint past_msg
= -1, today_msg
= -1, tomorrow_msg
= -1,
498 thisweek_msg
= -1, later_msg
= -1;
500 debug_print("fetching\n");
503 debug_print("no uri!\n");
507 update_subscription(item
->uri
, TRUE
);
509 *old_uids_valid
= FALSE
;
513 evt
= icalcomponent_get_first_component(
514 item
->cal
, ICAL_VEVENT_COMPONENT
);
516 evt
= icalcomponent_get_first_component(
517 item
->cal
, ICAL_VTODO_COMPONENT
);
519 type
= ICAL_VTODO_COMPONENT
;
522 debug_print("no cal!\n");
525 debug_print("no event\n");
528 g_slist_free(item
->numlist
);
529 item
->numlist
= NULL
;
533 slist_free_icalfeeddata(item
->evtlist
);
534 g_slist_free(item
->evtlist
);
535 item
->evtlist
= NULL
;
540 icalproperty
*rprop
= icalcomponent_get_first_property(evt
, ICAL_RRULE_PROPERTY
);
541 struct icalrecurrencetype recur
;
542 struct icaltimetype next
;
543 icalrecur_iterator
* ritr
= NULL
;
547 icalproperty
*rprop2
;
548 recur
= icalproperty_get_rrule(rprop
);
549 rprop2
= icalproperty_new_rrule(recur
);
550 prop
= icalcomponent_get_first_property(evt
, ICAL_DTSTART_PROPERTY
);
552 ritr
= icalrecur_iterator_new(recur
, icalproperty_get_dtstart(prop
));
553 next
= icalrecur_iterator_next(ritr
); /* skip first one */
559 prop
= icalcomponent_get_first_property(evt
, ICAL_UID_PROPERTY
);
561 gchar
*orig_uid
= NULL
;
562 gchar
*uid
= g_strdup(icalproperty_get_uid(prop
));
563 IcalFeedData
*data
= icalfeeddata_new(
564 icalcomponent_new_clone(evt
), NULL
);
566 orig_uid
= g_strdup(uid
);
569 item
->numlist
= g_slist_prepend(item
->numlist
, GINT_TO_POINTER(num
));
570 item
->evtlist
= g_slist_prepend(item
->evtlist
, data
);
572 debug_print("add %d : %s\n", num
, uid
);
575 prop
= icalcomponent_get_first_property(evt
, ICAL_DTSTART_PROPERTY
);
577 struct icaltimetype itt
= icalproperty_get_dtstart(prop
);
578 days
= event_to_today(NULL
, icaltime_as_timet(itt
));
579 if (days
== EVENT_PAST
&& past_msg
== -1) {
580 item
->numlist
= g_slist_prepend(item
->numlist
, GINT_TO_POINTER(num
));
581 data
= icalfeeddata_new(NULL
, EVENT_PAST_ID
);
583 } else if (days
== EVENT_TODAY
&& today_msg
== -1) {
584 item
->numlist
= g_slist_prepend(item
->numlist
, GINT_TO_POINTER(num
));
585 data
= icalfeeddata_new(NULL
, EVENT_TODAY_ID
);
587 } else if (days
== EVENT_TOMORROW
&& tomorrow_msg
== -1) {
588 item
->numlist
= g_slist_prepend(item
->numlist
, GINT_TO_POINTER(num
));
589 data
= icalfeeddata_new(NULL
, EVENT_TOMORROW_ID
);
590 tomorrow_msg
= num
++;
591 } else if (days
== EVENT_THISWEEK
&& thisweek_msg
== -1) {
592 item
->numlist
= g_slist_prepend(item
->numlist
, GINT_TO_POINTER(num
));
593 data
= icalfeeddata_new(NULL
, EVENT_THISWEEK_ID
);
594 thisweek_msg
= num
++;
595 } else if (days
== EVENT_LATER
&& later_msg
== -1) {
596 item
->numlist
= g_slist_prepend(item
->numlist
, GINT_TO_POINTER(num
));
597 data
= icalfeeddata_new(NULL
, EVENT_LATER_ID
);
601 if (past_msg
== -1) {
602 item
->numlist
= g_slist_prepend(item
->numlist
, GINT_TO_POINTER(num
));
603 data
= icalfeeddata_new(NULL
, EVENT_PAST_ID
);
608 item
->evtlist
= g_slist_prepend(item
->evtlist
, data
);
612 struct icaldurationtype ical_dur
;
613 struct icaltimetype dtstart
= icaltime_null_time();
614 struct icaltimetype dtend
= icaltime_null_time();
615 evt
= icalcomponent_new_clone(evt
);
616 prop
= icalcomponent_get_first_property(evt
, ICAL_RRULE_PROPERTY
);
618 icalcomponent_remove_property(evt
, prop
);
619 icalproperty_free(prop
);
621 prop
= icalcomponent_get_first_property(evt
, ICAL_DTSTART_PROPERTY
);
623 dtstart
= icalproperty_get_dtstart(prop
);
625 debug_print("event has no DTSTART!\n");
626 prop
= icalcomponent_get_first_property(evt
, ICAL_DTEND_PROPERTY
);
628 dtend
= icalproperty_get_dtend(prop
);
630 debug_print("event has no DTEND!\n");
631 ical_dur
= icaltime_subtract(dtend
, dtstart
);
632 next
= icalrecur_iterator_next(ritr
);
633 if (!icaltime_is_null_time(next
) &&
634 !icaltime_is_null_time(dtstart
) && i
< 100) {
635 prop
= icalcomponent_get_first_property(evt
, ICAL_DTSTART_PROPERTY
);
636 icalproperty_set_dtstart(prop
, next
);
638 prop
= icalcomponent_get_first_property(evt
, ICAL_DTEND_PROPERTY
);
640 icalproperty_set_dtend(prop
, icaltime_add(next
, ical_dur
));
642 prop
= icalcomponent_get_first_property(evt
, ICAL_UID_PROPERTY
);
643 uid
= g_strdup_printf("%s-%d", orig_uid
, i
);
644 icalproperty_set_uid(prop
, uid
);
645 /* dont free uid, used after (add_new) */
646 data
= icalfeeddata_new(evt
, NULL
);
650 icalcomponent_free(evt
);
656 debug_print("no uid!\n");
659 icalproperty_free(rprop
);
662 icalrecur_iterator_free(ritr
);
665 evt
= icalcomponent_get_next_component(
668 if (today_msg
== -1) {
669 IcalFeedData
*data
= icalfeeddata_new(NULL
, EVENT_TODAY_ID
);
670 item
->numlist
= g_slist_prepend(item
->numlist
, GINT_TO_POINTER(num
));
672 item
->evtlist
= g_slist_prepend(item
->evtlist
, data
);
674 item
->numlist
= g_slist_reverse(item
->numlist
);
675 item
->evtlist
= g_slist_reverse(item
->evtlist
);
677 *list
= item
->numlist
? g_slist_copy(item
->numlist
) : NULL
;
678 debug_print("return %d\n", num
);
682 #define VCAL_FOLDER_ADD_EVENT(event) \
685 *list = g_slist_prepend(*list, GINT_TO_POINTER(n_msg)); \
686 debug_print("add %d %s\n", n_msg, event->uid); \
688 days = event_to_today(event, 0); \
690 if (days == EVENT_PAST && past_msg == -1) { \
691 *list = g_slist_prepend(*list, GINT_TO_POINTER(n_msg)); \
692 past_msg = n_msg++; \
693 g_hash_table_insert(hash_uids, GINT_TO_POINTER(past_msg), g_strdup(EVENT_PAST_ID)); \
694 } else if (days == EVENT_TODAY && today_msg == -1) { \
695 *list = g_slist_prepend(*list, GINT_TO_POINTER(n_msg)); \
696 today_msg = n_msg++; \
697 g_hash_table_insert(hash_uids, GINT_TO_POINTER(today_msg), g_strdup(EVENT_TODAY_ID)); \
698 } else if (days == EVENT_TOMORROW && tomorrow_msg == -1) { \
699 *list = g_slist_prepend(*list, GINT_TO_POINTER(n_msg)); \
700 tomorrow_msg = n_msg++; \
701 g_hash_table_insert(hash_uids, GINT_TO_POINTER(tomorrow_msg), g_strdup(EVENT_TOMORROW_ID)); \
702 } else if (days == EVENT_THISWEEK && thisweek_msg == -1) { \
703 *list = g_slist_prepend(*list, GINT_TO_POINTER(n_msg)); \
704 thisweek_msg = n_msg++; \
705 g_hash_table_insert(hash_uids, GINT_TO_POINTER(thisweek_msg), g_strdup(EVENT_THISWEEK_ID)); \
706 } else if (days == EVENT_LATER && later_msg == -1) { \
707 *list = g_slist_prepend(*list, GINT_TO_POINTER(n_msg)); \
708 later_msg = n_msg++; \
709 g_hash_table_insert(hash_uids, GINT_TO_POINTER(later_msg), g_strdup(EVENT_LATER_ID)); \
713 GSList
*vcal_get_events_list(FolderItem
*item
)
717 GSList
*events
= NULL
;
718 GError
*error
= NULL
;
720 if (item
!= item
->folder
->inbox
) {
721 GSList
*subs
= vcal_folder_get_webcal_events_for_folder(item
);
723 for (cur
= subs
; cur
; cur
= cur
->next
) {
724 /* Don't free that, it's done when subscriptions are
726 icalcomponent
*ical
= (icalcomponent
*)cur
->data
;
727 VCalEvent
*event
= vcal_get_event_from_ical(
728 icalcomponent_as_ical_string(ical
), NULL
);
729 events
= g_slist_prepend(events
, event
);
735 dp
= g_dir_open(vcal_manager_get_event_path(), 0, &error
);
738 debug_print("couldn't open dir '%s': %s (%d)\n",
739 vcal_manager_get_event_path(), error
->message
, error
->code
);
744 while ((d
= g_dir_read_name(dp
)) != NULL
) {
745 VCalEvent
*event
= NULL
;
746 if (d
[0] == '.' || strstr(d
, ".bak")
747 || !strcmp(d
, "internal.ics")
748 || !strcmp(d
, "internal.ifb")
749 || !strcmp(d
, "multisync"))
752 event
= vcal_manager_load_event(d
);
755 if (event
->rec_occurrence
) {
756 vcal_manager_free_event(event
);
761 if (event
&& event
->method
!= ICAL_METHOD_CANCEL
) {
762 PrefsAccount
*account
= vcal_manager_get_account_from_event(event
);
763 enum icalparameter_partstat status
=
764 account
? vcal_manager_get_reply_for_attendee(event
, account
->address
): ICAL_PARTSTAT_NEEDSACTION
;
765 if (status
== ICAL_PARTSTAT_ACCEPTED
766 || status
== ICAL_PARTSTAT_TENTATIVE
) {
767 events
= g_slist_prepend(events
, event
);
769 vcal_manager_free_event(event
);
772 if ((status
== ICAL_PARTSTAT_ACCEPTED
773 || status
== ICAL_PARTSTAT_TENTATIVE
)
774 && event
->recur
&& *(event
->recur
)) {
775 struct icalrecurrencetype recur
;
776 struct icaltimetype dtstart
;
777 struct icaltimetype next
;
778 icalrecur_iterator
* ritr
;
779 time_t duration
= (time_t) NULL
;
780 struct icaldurationtype ical_dur
;
783 debug_print("dumping recurring events from main event %s\n", d
);
784 recur
= icalrecurrencetype_from_string(event
->recur
);
785 dtstart
= icaltime_from_string(event
->dtstart
);
787 duration
= icaltime_as_timet(icaltime_from_string(event
->dtend
))
788 - icaltime_as_timet(icaltime_from_string(event
->dtstart
));
790 ical_dur
= icaldurationtype_from_int(duration
);
792 ritr
= icalrecur_iterator_new(recur
, dtstart
);
794 next
= icalrecur_iterator_next(ritr
); /* skip first one */
795 if (!icaltime_is_null_time(next
))
796 next
= icalrecur_iterator_next(ritr
);
797 debug_print("next time is %snull\n", icaltime_is_null_time(next
)?"":"not ");
798 while (!icaltime_is_null_time(next
) && i
< 100) {
799 const gchar
*new_start
= NULL
, *new_end
= NULL
;
800 VCalEvent
*nevent
= NULL
;
801 gchar
*uid
= g_strdup_printf("%s-%d", event
->uid
, i
);
802 new_start
= icaltime_as_ical_string(next
);
803 new_end
= icaltime_as_ical_string(
804 icaltime_add(next
, ical_dur
));
805 debug_print("adding with start/end %s:%s\n", new_start
, new_end
);
806 nevent
= vcal_manager_new_event(uid
, event
->organizer
, event
->orgname
,
807 event
->location
, event
->summary
, event
->description
,
808 new_start
, new_end
, NULL
,
809 event
->tzid
, event
->url
, event
->method
,
810 event
->sequence
, event
->type
);
812 vcal_manager_copy_attendees(event
, nevent
);
813 nevent
->rec_occurrence
= TRUE
;
814 vcal_manager_save_event(nevent
, FALSE
);
815 account
= vcal_manager_get_account_from_event(event
);
817 account
? vcal_manager_get_reply_for_attendee(event
, account
->address
): ICAL_PARTSTAT_NEEDSACTION
;
818 if (status
== ICAL_PARTSTAT_ACCEPTED
819 || status
== ICAL_PARTSTAT_TENTATIVE
) {
820 events
= g_slist_prepend(events
, nevent
);
822 vcal_manager_free_event(nevent
);
824 next
= icalrecur_iterator_next(ritr
);
825 debug_print("next time is %snull\n", icaltime_is_null_time(next
)?"":"not ");
828 icalrecur_iterator_free(ritr
);
831 vcal_manager_free_event(event
);
835 return g_slist_reverse(events
);
838 static gint
vcal_get_num_list(Folder
*folder
, FolderItem
*item
,
839 MsgNumberList
** list
, gboolean
*old_uids_valid
)
842 gint past_msg
= -1, today_msg
= -1, tomorrow_msg
= -1,
843 thisweek_msg
= -1, later_msg
= -1;
844 GSList
*events
= NULL
, *cur
;
846 g_return_val_if_fail (*list
== NULL
, 0); /* we expect a NULL list */
848 debug_print(" num for %s\n", ((VCalFolderItem
*)item
)->uri
? ((VCalFolderItem
*)item
)->uri
:"(null)");
850 *old_uids_valid
= FALSE
;
852 if (((VCalFolderItem
*)item
)->uri
)
853 return feed_fetch(item
, list
, old_uids_valid
);
855 events
= vcal_get_events_list(item
);
857 debug_print("get_num_list\n");
859 if (hash_uids
!= NULL
)
860 g_hash_table_destroy(hash_uids
);
862 hash_uids
= g_hash_table_new_full(g_direct_hash
, g_direct_equal
,
865 for (cur
= events
; cur
; cur
= cur
->next
) {
866 VCalEvent
*event
= (VCalEvent
*)cur
->data
;
870 g_hash_table_insert(hash_uids
, GINT_TO_POINTER(n_msg
), g_strdup(event
->uid
));
872 if (event
->rec_occurrence
) {
873 vcal_manager_free_event(event
);
877 if (event
->method
!= ICAL_METHOD_CANCEL
) {
879 VCAL_FOLDER_ADD_EVENT(event
);
882 vcal_manager_free_event(event
);
887 if (today_msg
== -1) {
888 *list
= g_slist_prepend(*list
, GINT_TO_POINTER(n_msg
));
890 g_hash_table_insert(hash_uids
, GINT_TO_POINTER(today_msg
), g_strdup(EVENT_TODAY_ID
));
893 g_slist_free(events
);
894 vcal_folder_export(folder
);
896 vcal_set_mtime(folder
, item
);
898 *list
= g_slist_reverse(*list
);
900 return g_slist_length(*list
);
903 static MsgInfo
*vcal_parse_msg(const gchar
*file
, FolderItem
*item
, int num
)
905 MsgInfo
*msginfo
= NULL
;
908 debug_print("parse_msg\n");
910 flags
.perm_flags
= 0;
912 msginfo
= procheader_parse_file(file
, flags
, TRUE
, TRUE
);
914 msginfo
->msgnum
= num
;
915 msginfo
->folder
= item
;
919 static MsgInfo
*vcal_get_msginfo(Folder
* folder
,
920 FolderItem
* item
, gint num
)
922 MsgInfo
*msginfo
= NULL
;
925 debug_print("get_msginfo\n");
927 g_return_val_if_fail(item
!= NULL
, NULL
);
928 g_return_val_if_fail(num
> 0, NULL
);
930 file
= vcal_fetch_msg(folder
, item
, num
);
936 msginfo
= vcal_parse_msg(file
, item
, num
);
939 msginfo
->flags
.perm_flags
= 0;
940 msginfo
->flags
.tmp_flags
= 0;
942 vcal_change_flags(NULL
, NULL
, msginfo
, 0);
944 debug_print(" adding %d\n", num
);
949 debug_print(" got msginfo %p\n", msginfo
);
954 static gchar
*feed_fetch_item(FolderItem
* fitem
, gint num
)
956 gchar
*filename
= NULL
;
957 VCalFolderItem
*item
= (VCalFolderItem
*)fitem
;
960 IcalFeedData
*data
= NULL
;
962 if (!item
->numlist
) {
963 folder_item_scan_full(fitem
, FALSE
);
965 if (!item
->numlist
) {
966 debug_print("numlist null\n");
970 ncur
= item
->numlist
;
971 ecur
= item
->evtlist
;
974 if (!ncur
|| !ecur
) {
975 debug_print("list short end (%d to %d) %d,%d\n", i
, num
, ncur
!=NULL
, ecur
!=NULL
);
983 data
= (IcalFeedData
*)ecur
->data
;
990 filename
= vcal_manager_icalevent_dump(data
->event
, fitem
->name
, NULL
);
991 else if (data
->pseudoevent_id
) {
992 filename
= vcal_manager_dateevent_dump(data
->pseudoevent_id
, fitem
);
993 created_files
= g_slist_prepend(created_files
, g_strdup(filename
));
995 debug_print("no event\n");
999 debug_print("feed item dump to %s\n", filename
);
1003 static gchar
*vcal_fetch_msg(Folder
* folder
, FolderItem
* item
,
1006 gchar
*filename
= NULL
;
1007 const gchar
*uid
= NULL
;
1009 debug_print(" fetch for %s %d\n", (((VCalFolderItem
*)item
)->uri
? ((VCalFolderItem
*)item
)->uri
:"(null)"), num
);
1010 if (((VCalFolderItem
*)item
)->uri
)
1011 return feed_fetch_item(item
, num
);
1015 folder_item_scan_full(item
, FALSE
);
1016 uid
= g_hash_table_lookup(hash_uids
, GINT_TO_POINTER(num
));
1019 (!strcmp(uid
, EVENT_PAST_ID
) ||
1020 !strcmp(uid
, EVENT_TODAY_ID
) ||
1021 !strcmp(uid
, EVENT_TOMORROW_ID
) ||
1022 !strcmp(uid
, EVENT_THISWEEK_ID
) ||
1023 !strcmp(uid
, EVENT_LATER_ID
))) {
1024 filename
= vcal_manager_dateevent_dump(uid
, item
);
1026 VCalEvent
*event
= NULL
;
1027 event
= vcal_manager_load_event(uid
);
1029 filename
= vcal_manager_event_dump(event
, FALSE
, TRUE
, NULL
, FALSE
);
1032 created_files
= g_slist_prepend(created_files
, g_strdup(filename
));
1035 vcal_manager_free_event(event
);
1041 static gint
vcal_add_msg(Folder
*folder
, FolderItem
*_dest
, const gchar
*file
, MsgFlags
*flags
)
1043 gchar
*contents
= file_read_to_str(file
);
1045 vcal_add_event(contents
);
1051 static void vcal_remove_event (Folder
*folder
, MsgInfo
*msginfo
);
1053 static gint
vcal_remove_msg(Folder
*folder
, FolderItem
*_item
, gint num
)
1055 MsgInfo
*msginfo
= folder_item_get_msginfo(_item
, num
);
1060 if (_item
== folder
->inbox
)
1061 vcal_remove_event(folder
, msginfo
);
1063 procmsg_msginfo_free(&msginfo
);
1067 static FolderItem
*vcal_create_folder(Folder
* folder
,
1068 FolderItem
* parent
,
1072 FolderItem
*newitem
= NULL
;
1073 debug_print("creating new vcal folder\n");
1075 path
= g_strconcat((parent
->path
!= NULL
) ? parent
->path
: "", ".", name
, NULL
);
1076 newitem
= folder_item_new(folder
, name
, path
);
1077 folder_item_append(parent
, newitem
);
1083 static gint
vcal_create_tree(Folder
*folder
)
1085 FolderItem
*rootitem
, *inboxitem
;
1086 GNode
*rootnode
, *inboxnode
;
1088 if (!folder
->node
) {
1089 rootitem
= folder_item_new(folder
, folder
->name
, NULL
);
1090 rootitem
->folder
= folder
;
1091 rootnode
= g_node_new(rootitem
);
1092 folder
->node
= rootnode
;
1093 rootitem
->node
= rootnode
;
1095 rootitem
= FOLDER_ITEM(folder
->node
->data
);
1096 rootnode
= folder
->node
;
1099 /* Add inbox folder */
1100 if (!folder
->inbox
) {
1101 inboxitem
= folder_item_new(folder
, _("Meetings"), ".meetings");
1102 inboxitem
->folder
= folder
;
1103 inboxitem
->stype
= F_INBOX
;
1104 inboxnode
= g_node_new(inboxitem
);
1105 inboxitem
->node
= inboxnode
;
1106 folder
->inbox
= inboxitem
;
1107 g_node_append(rootnode
, inboxnode
);
1109 g_free(folder
->inbox
->path
);
1110 folder
->inbox
->path
= g_strdup(".meetings");
1113 debug_print("created new vcal tree\n");
1117 static gint
vcal_remove_folder(Folder
*folder
, FolderItem
*fitem
)
1119 VCalFolderItem
*item
= (VCalFolderItem
*)fitem
;
1129 folder_item_remove(fitem
);
1134 static gboolean
vcal_scan_required(Folder
*folder
, FolderItem
*item
)
1137 VCalFolderItem
*vitem
= (VCalFolderItem
*)item
;
1139 g_return_val_if_fail(item
!= NULL
, FALSE
);
1143 } else if (g_stat(vcal_manager_get_event_path(), &s
) < 0) {
1145 } else if ((s
.st_mtime
> item
->mtime
) &&
1146 (s
.st_mtime
- 3600 != item
->mtime
)) {
1152 static gint vcal_folder_lock_count
= 0;
1154 static void vcal_set_mtime(Folder
*folder
, FolderItem
*item
)
1157 gchar
*path
= folder_item_get_path(item
);
1159 if (folder
->inbox
!= item
)
1162 g_return_if_fail(path
!= NULL
);
1164 if (g_stat(path
, &s
) < 0) {
1165 FILE_OP_ERROR(path
, "stat");
1170 item
->mtime
= s
.st_mtime
;
1171 debug_print("VCAL: forced mtime of %s to %"CM_TIME_FORMAT
"\n",
1172 item
->name
?item
->name
:"(null)", item
->mtime
);
1176 void vcal_folder_export(Folder
*folder
)
1178 FolderItem
*item
= folder
?folder
->inbox
:NULL
;
1179 gboolean need_scan
= folder
?vcal_scan_required(folder
, item
):TRUE
;
1180 gchar
*export_pass
= NULL
;
1181 gchar
*export_freebusy_pass
= NULL
;
1183 if (vcal_folder_lock_count
) /* blocked */
1185 vcal_folder_lock_count
++;
1187 export_pass
= vcal_passwd_get("export");
1188 export_freebusy_pass
= vcal_passwd_get("export_freebusy");
1190 if (vcal_meeting_export_calendar(vcalprefs
.export_path
,
1191 vcalprefs
.export_user
,
1194 debug_print("exporting calendar\n");
1195 if (vcalprefs
.export_enable
&&
1196 vcalprefs
.export_command
&&
1197 strlen(vcalprefs
.export_command
))
1198 execute_command_line(
1199 vcalprefs
.export_command
, TRUE
, NULL
);
1201 if (export_pass
!= NULL
) {
1202 memset(export_pass
, 0, strlen(export_pass
));
1204 g_free(export_pass
);
1205 if (vcal_meeting_export_freebusy(vcalprefs
.export_freebusy_path
,
1206 vcalprefs
.export_freebusy_user
,
1207 export_freebusy_pass
)) {
1208 debug_print("exporting freebusy\n");
1209 if (vcalprefs
.export_freebusy_enable
&&
1210 vcalprefs
.export_freebusy_command
&&
1211 strlen(vcalprefs
.export_freebusy_command
))
1212 execute_command_line(
1213 vcalprefs
.export_freebusy_command
, TRUE
, NULL
);
1215 if (export_freebusy_pass
!= NULL
) {
1216 memset(export_freebusy_pass
, 0, strlen(export_freebusy_pass
));
1218 g_free(export_freebusy_pass
);
1219 vcal_folder_lock_count
--;
1220 if (!need_scan
&& folder
) {
1221 vcal_set_mtime(folder
, folder
->inbox
);
1225 static void vcal_remove_event (Folder
*folder
, MsgInfo
*msginfo
)
1227 const gchar
*uid
= msginfo
->msgid
;
1228 VCalFolderItem
*item
= (VCalFolderItem
*)msginfo
->folder
;
1231 gchar
*file
= vcal_manager_get_event_file(uid
);
1236 if (!item
|| !item
->batching
)
1237 vcal_folder_export(folder
);
1243 static void vcal_change_flags(Folder
*folder
, FolderItem
*_item
, MsgInfo
*msginfo
, MsgPermFlags newflags
)
1247 if (newflags
& MSG_DELETED
) {
1248 /* delete the stuff */
1249 msginfo
->flags
.perm_flags
|= MSG_DELETED
;
1250 vcal_remove_event(folder
, msginfo
);
1254 /* accept the rest */
1255 msginfo
->flags
.perm_flags
= newflags
;
1258 msginfo
->flags
.perm_flags
&= ~MSG_CLABEL_FLAG_MASK
;
1260 date
= event_to_today(NULL
, msginfo
->date_t
);
1265 msginfo
->flags
.perm_flags
|= MSG_COLORLABEL_TO_FLAGS(2); /* Red */
1267 case EVENT_TOMORROW
:
1269 case EVENT_THISWEEK
:
1274 if (msginfo
->msgid
) {
1275 if (!strcmp(msginfo
->msgid
, EVENT_TODAY_ID
) ||
1276 !strcmp(msginfo
->msgid
, EVENT_TOMORROW_ID
))
1277 msginfo
->flags
.perm_flags
|= MSG_MARKED
;
1281 void vcal_folder_gtk_init(void)
1283 vcal_fill_popup_menu_labels();
1285 folderview_register_popup(&vcal_popup
);
1288 void vcal_folder_gtk_done(void)
1290 GSList
*cur
= created_files
;
1292 gchar
*file
= (gchar
*)cur
->data
;
1296 debug_print("removing %s\n", file
);
1297 if (g_unlink(file
) < 0)
1298 FILE_OP_ERROR(file
, "g_unlink");
1301 g_slist_free(created_files
);
1302 folderview_unregister_popup(&vcal_popup
);
1305 static void add_menuitems(GtkUIManager
*ui_manager
, FolderItem
*item
)
1307 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "NewMeeting", "FolderViewPopup/NewMeeting", GTK_UI_MANAGER_MENUITEM
)
1308 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "ExportCal", "FolderViewPopup/ExportCal", GTK_UI_MANAGER_MENUITEM
)
1309 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "SeparatorVcal1", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR
)
1310 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "SubscribeCal", "FolderViewPopup/SubscribeCal", GTK_UI_MANAGER_MENUITEM
)
1311 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "UnsubscribeCal", "FolderViewPopup/UnsubscribeCal", GTK_UI_MANAGER_MENUITEM
)
1312 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "SeparatorVcal2", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR
)
1313 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "RenameFolder", "FolderViewPopup/RenameFolder", GTK_UI_MANAGER_MENUITEM
)
1314 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "SeparatorVcal3", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR
)
1315 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "CheckSubs", "FolderViewPopup/CheckSubs", GTK_UI_MANAGER_MENUITEM
)
1316 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "SeparatorVcal4", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR
)
1317 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "ListView", "FolderViewPopup/ListView", GTK_UI_MANAGER_MENUITEM
)
1318 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "WeekView", "FolderViewPopup/WeekView", GTK_UI_MANAGER_MENUITEM
)
1319 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "MonthView", "FolderViewPopup/MonthView", GTK_UI_MANAGER_MENUITEM
)
1320 MENUITEM_ADDUI_MANAGER(ui_manager
, "/Popup/FolderViewPopup", "SeparatorVcal5", "FolderViewPopup/---", GTK_UI_MANAGER_SEPARATOR
)
1323 static gboolean setting_sensitivity
= FALSE
;
1324 static void set_sensitivity(GtkUIManager
*ui_manager
, FolderItem
*fitem
)
1326 VCalFolderItem
*item
= (VCalFolderItem
*)fitem
;
1328 #define SET_SENS(name, sens) \
1329 cm_menu_set_sensitive_full(ui_manager, "Popup/"name, sens)
1331 setting_sensitivity
= TRUE
;
1333 cm_toggle_menu_set_active_full(ui_manager
, "Popup/FolderViewPopup/ListView", (item
->use_cal_view
== 0));
1334 cm_toggle_menu_set_active_full(ui_manager
, "Popup/FolderViewPopup/WeekView", (item
->use_cal_view
== 1));
1335 cm_toggle_menu_set_active_full(ui_manager
, "Popup/FolderViewPopup/MonthView", (item
->use_cal_view
== 2));
1336 SET_SENS("FolderViewPopup/NewMeeting", item
->uri
== NULL
);
1337 SET_SENS("FolderViewPopup/ExportCal", TRUE
);
1338 SET_SENS("FolderViewPopup/SubscribeCal", item
->uri
== NULL
);
1339 SET_SENS("FolderViewPopup/UnsubscribeCal", item
->uri
!= NULL
);
1340 SET_SENS("FolderViewPopup/RenameFolder", folder_item_parent(fitem
) != NULL
);
1341 SET_SENS("FolderViewPopup/CheckSubs", TRUE
);
1342 SET_SENS("FolderViewPopup/ListView", folder_item_parent(fitem
) != NULL
);
1343 SET_SENS("FolderViewPopup/WeekView", folder_item_parent(fitem
) != NULL
);
1344 SET_SENS("FolderViewPopup/MonthView", folder_item_parent(fitem
) != NULL
);
1345 setting_sensitivity
= FALSE
;
1349 static void new_meeting_cb(GtkAction
*action
, gpointer data
)
1351 debug_print("new_meeting_cb\n");
1352 vcal_meeting_create(NULL
);
1355 GSList
* vcal_folder_get_waiting_events(void)
1357 Folder
*folder
= folder_find_from_name (PLUGIN_NAME
, vcal_folder_get_class());
1358 return vcal_get_events_list(folder
->inbox
);
1361 typedef struct _get_webcal_data
{
1366 static gboolean
get_webcal_events_func(GNode
*node
, gpointer user_data
)
1368 FolderItem
*item
= node
->data
;
1369 GetWebcalData
*data
= user_data
;
1370 gboolean dummy
= FALSE
;
1371 GSList
*list
= NULL
, *cur
= NULL
;
1373 if (data
->item
&& data
->item
!= item
)
1376 feed_fetch(item
, &list
, &dummy
);
1380 for (cur
= ((VCalFolderItem
*)item
)->evtlist
; cur
; cur
= cur
->next
) {
1381 IcalFeedData
*fdata
= (IcalFeedData
*)cur
->data
;
1383 data
->list
= g_slist_prepend(data
->list
, fdata
->event
);
1388 GSList
* vcal_folder_get_webcal_events(void)
1390 GetWebcalData
*data
= g_new0(GetWebcalData
, 1);
1391 Folder
*folder
= folder_find_from_name (PLUGIN_NAME
, vcal_folder_get_class());
1392 GSList
*list
= NULL
;
1394 g_node_traverse(folder
->node
, G_PRE_ORDER
,
1395 G_TRAVERSE_ALL
, -1, get_webcal_events_func
, data
);
1400 return g_slist_reverse(list
);
1403 static gboolean
vcal_free_data_func(GNode
*node
, gpointer user_data
)
1405 VCalFolderItem
*item
= node
->data
;
1408 icalcomponent_free(item
->cal
);
1411 if (item
->numlist
) {
1412 g_slist_free(item
->numlist
);
1413 item
->numlist
= NULL
;
1416 if (item
->evtlist
) {
1417 slist_free_icalfeeddata(item
->evtlist
);
1418 g_slist_free(item
->evtlist
);
1419 item
->evtlist
= NULL
;
1425 void vcal_folder_free_data(void)
1427 Folder
*folder
= folder_find_from_name (PLUGIN_NAME
, vcal_folder_get_class());
1429 g_node_traverse(folder
->node
, G_PRE_ORDER
,
1430 G_TRAVERSE_ALL
, -1, vcal_free_data_func
, NULL
);
1433 GSList
* vcal_folder_get_webcal_events_for_folder(FolderItem
*item
)
1435 GetWebcalData
*data
= g_new0(GetWebcalData
, 1);
1436 Folder
*folder
= folder_find_from_name (PLUGIN_NAME
, vcal_folder_get_class());
1437 GSList
*list
= NULL
;
1439 g_node_traverse(folder
->node
, G_PRE_ORDER
,
1440 G_TRAVERSE_ALL
, -1, get_webcal_events_func
, data
);
1445 return g_slist_reverse(list
);
1448 gchar
* get_item_event_list_for_date(FolderItem
*item
, EventTime date
)
1450 GSList
*strs
= NULL
;
1452 gchar
*result
= NULL
;
1453 gchar
*datestr
= NULL
;
1455 if (((VCalFolderItem
*)item
)->uri
) {
1456 for (cur
= ((VCalFolderItem
*)item
)->evtlist
; cur
; cur
= cur
->next
) {
1457 IcalFeedData
*fdata
= (IcalFeedData
*)cur
->data
;
1459 struct icaltimetype itt
;
1460 gchar
*summary
= NULL
;
1464 prop
= icalcomponent_get_first_property((icalcomponent
*)fdata
->event
, ICAL_DTSTART_PROPERTY
);
1468 itt
= icalproperty_get_dtstart(prop
);
1469 days
= event_to_today(NULL
, icaltime_as_timet(itt
));
1472 prop
= icalcomponent_get_first_property((icalcomponent
*)fdata
->event
, ICAL_SUMMARY_PROPERTY
);
1474 if (!g_utf8_validate(icalproperty_get_summary(prop
), -1, NULL
))
1475 summary
= conv_codeset_strdup(icalproperty_get_summary(prop
),
1476 conv_get_locale_charset_str(), CS_UTF_8
);
1478 summary
= g_strdup(icalproperty_get_summary(prop
));
1480 summary
= g_strdup("-");
1482 strs
= g_slist_prepend(strs
, summary
);
1485 GSList
*evtlist
= vcal_folder_get_waiting_events();
1486 for (cur
= evtlist
; cur
; cur
= cur
->next
) {
1487 VCalEvent
*event
= (VCalEvent
*)cur
->data
;
1489 days
= event_to_today(event
, 0);
1490 gchar
*summary
= NULL
;
1492 summary
= g_strdup(event
->summary
);
1493 strs
= g_slist_prepend(strs
, summary
);
1495 vcal_manager_free_event(event
);
1501 datestr
=_("in the past");
1506 case EVENT_TOMORROW
:
1507 datestr
=_("tomorrow");
1509 case EVENT_THISWEEK
:
1510 datestr
=_("this week");
1517 result
= g_strdup_printf(_("\nThese are the events planned %s:\n"),
1518 datestr
?datestr
:"never");
1520 strs
= g_slist_reverse(strs
);
1521 for (cur
= strs
; cur
; cur
= cur
->next
) {
1522 int e_len
= strlen(result
);
1523 int n_len
= strlen((gchar
*)cur
->data
);
1525 result
= g_realloc(result
, e_len
+n_len
+4);
1526 *(result
+e_len
) = '\n';
1527 strcpy(result
+e_len
+1, "- ");
1528 strcpy(result
+e_len
+3, (gchar
*)cur
->data
);
1530 result
= g_realloc(result
, e_len
+n_len
+3);
1531 strcpy(result
+e_len
, "- ");
1532 strcpy(result
+e_len
+2, (gchar
*)cur
->data
);
1535 slist_free_strings_full(strs
);
1539 static void export_cal_cb(GtkAction
*action
, gpointer data
)
1541 vcal_meeting_export_calendar(NULL
, NULL
, NULL
, FALSE
);
1548 static size_t curl_recv(void *buf
, size_t size
, size_t nmemb
, void *stream
)
1550 struct CBuf
*buffer
= (struct CBuf
*)stream
;
1552 gchar
*tmpbuf
= g_malloc0(size
*nmemb
+ 1);
1554 g_return_val_if_fail(tmpbuf
!= NULL
, 0);
1556 memcpy(tmpbuf
, buf
, size
*nmemb
);
1559 /* If the buffer already has contents, append the new data. */
1560 tmp
= g_strconcat(buffer
->str
, tmpbuf
, NULL
);
1562 g_free(buffer
->str
);
1565 buffer
->str
= tmpbuf
;
1571 void *url_read_thread(void *data
)
1573 thread_data
*td
= (thread_data
*)data
;
1575 CURL
*curl_ctx
= NULL
;
1577 struct CBuf buffer
= { NULL
};
1578 gchar
*t_url
= (gchar
*)td
->url
;
1580 while (*t_url
== ' ')
1582 if (strchr(t_url
, ' '))
1583 *(strchr(t_url
, ' ')) = '\0';
1586 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE
, NULL
);
1587 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS
, NULL
);
1590 curl_ctx
= curl_easy_init();
1592 curl_easy_setopt(curl_ctx
, CURLOPT_URL
, t_url
);
1593 curl_easy_setopt(curl_ctx
, CURLOPT_WRITEFUNCTION
, curl_recv
);
1594 curl_easy_setopt(curl_ctx
, CURLOPT_WRITEDATA
, &buffer
);
1595 curl_easy_setopt(curl_ctx
, CURLOPT_TIMEOUT
, prefs_common_get_prefs()->io_timeout_secs
);
1596 curl_easy_setopt(curl_ctx
, CURLOPT_NOSIGNAL
, 1);
1598 curl_easy_setopt(curl_ctx
, CURLOPT_CAINFO
, claws_ssl_get_cert_file());
1600 #if LIBCURL_VERSION_NUM >= 0x070a00
1601 if(vcalprefs
.ssl_verify_peer
== FALSE
) {
1602 curl_easy_setopt(curl_ctx
, CURLOPT_SSL_VERIFYPEER
, 0);
1603 curl_easy_setopt(curl_ctx
, CURLOPT_SSL_VERIFYHOST
, 0);
1606 curl_easy_setopt(curl_ctx
, CURLOPT_USERAGENT
,
1607 "Claws Mail vCalendar plugin "
1608 "(" PLUGINS_URI
")");
1609 curl_easy_setopt(curl_ctx
, CURLOPT_FOLLOWLOCATION
, 1);
1610 res
= curl_easy_perform(curl_ctx
);
1613 debug_print("res %d %s\n", res
, curl_easy_strerror(res
));
1614 td
->error
= g_strdup(curl_easy_strerror(res
));
1616 if(res
== CURLE_OPERATION_TIMEOUTED
)
1617 log_error(LOG_PROTOCOL
, _("Timeout (%d seconds) connecting to %s\n"),
1618 prefs_common_get_prefs()->io_timeout_secs
, t_url
);
1621 curl_easy_getinfo(curl_ctx
, CURLINFO_RESPONSE_CODE
, &response_code
);
1622 if( response_code
>= 400 && response_code
< 500 ) {
1623 debug_print("VCalendar: got %ld\n", response_code
);
1624 switch(response_code
) {
1626 td
->error
= g_strdup(_("401 (Authorisation required)"));
1629 td
->error
= g_strdup(_("403 (Unauthorised)"));
1632 td
->error
= g_strdup(_("404 (Not found)"));
1635 td
->error
= g_strdup_printf(_("Error %ld"), response_code
);
1639 curl_easy_cleanup(curl_ctx
);
1641 td
->result
= g_strdup(buffer
.str
);
1645 td
->done
= TRUE
; /* let the caller thread join() */
1646 return GINT_TO_POINTER(0);
1649 gchar
*vcal_curl_read(const char *url
, const gchar
*label
, gboolean verbose
,
1650 void (*callback
)(const gchar
*url
, gchar
*data
, gboolean verbose
, gchar
*error
))
1658 gchar
*error
= NULL
;
1660 td
= g_new0(thread_data
, 1);
1667 STATUSBAR_PUSH(mainwindow_get_mainwindow(), label
);
1670 if (pthread_create(&pt
, NULL
, url_read_thread
, td
) != 0) {
1671 url_read_thread(td
);
1677 pthread_join(pt
, &res
);
1679 url_read_thread(td
);
1682 result
= td
->result
;
1686 STATUSBAR_POP(mainwindow_get_mainwindow());
1689 callback(url
, result
, verbose
, error
);
1698 gboolean
vcal_curl_put(gchar
*url
, FILE *fp
, gint filesize
, const gchar
*user
, const gchar
*pass
)
1700 gboolean res
= TRUE
;
1701 CURL
*curl_ctx
= curl_easy_init();
1702 long response_code
= 0;
1704 gchar
*userpwd
= NULL
;
1706 struct curl_slist
* headers
= curl_slist_append(NULL
,
1707 "Content-Type: text/calendar; charset=\"utf-8\"" );
1709 while (*t_url
== ' ')
1711 if (strchr(t_url
, ' '))
1712 *(strchr(t_url
, ' ')) = '\0';
1714 if (user
&& pass
&& *user
&& *pass
) {
1715 userpwd
= g_strdup_printf("%s:%s",user
,pass
);
1716 curl_easy_setopt(curl_ctx
, CURLOPT_USERPWD
, userpwd
);
1718 curl_easy_setopt(curl_ctx
, CURLOPT_URL
, t_url
);
1719 curl_easy_setopt(curl_ctx
, CURLOPT_UPLOAD
, 1);
1720 curl_easy_setopt(curl_ctx
, CURLOPT_READFUNCTION
, NULL
);
1721 curl_easy_setopt(curl_ctx
, CURLOPT_READDATA
, fp
);
1722 curl_easy_setopt(curl_ctx
, CURLOPT_HTTPHEADER
, headers
);
1724 curl_easy_setopt(curl_ctx
, CURLOPT_CAINFO
, claws_ssl_get_cert_file());
1726 #if LIBCURL_VERSION_NUM >= 0x070a00
1727 if(vcalprefs
.ssl_verify_peer
== FALSE
) {
1728 curl_easy_setopt(curl_ctx
, CURLOPT_SSL_VERIFYPEER
, 0);
1729 curl_easy_setopt(curl_ctx
, CURLOPT_SSL_VERIFYHOST
, 0);
1732 curl_easy_setopt(curl_ctx
, CURLOPT_USERAGENT
,
1733 "Claws Mail vCalendar plugin "
1734 "(" PLUGINS_URI
")");
1735 curl_easy_setopt(curl_ctx
, CURLOPT_INFILESIZE
, filesize
);
1736 res
= curl_easy_perform(curl_ctx
);
1740 debug_print("res %d %s\n", res
, curl_easy_strerror(res
));
1745 curl_easy_getinfo(curl_ctx
, CURLINFO_RESPONSE_CODE
, &response_code
);
1746 if (response_code
< 200 || response_code
>= 300) {
1747 g_warning("can't export calendar, got code %ld", response_code
);
1750 curl_easy_cleanup(curl_ctx
);
1751 curl_slist_free_all(headers
);
1755 static gboolean
folder_item_find_func(GNode
*node
, gpointer data
)
1757 FolderItem
*item
= node
->data
;
1759 const gchar
*uri
= d
[0];
1761 if (!uri
|| !((VCalFolderItem
*)item
)->uri
1762 || strcmp(uri
, ((VCalFolderItem
*)item
)->uri
))
1770 static FolderItem
*get_folder_item_for_uri(const gchar
*uri
)
1772 Folder
*root
= folder_find_from_name (PLUGIN_NAME
, vcal_folder_get_class());
1778 d
[0] = (gpointer
)uri
;
1780 g_node_traverse(root
->node
, G_PRE_ORDER
, G_TRAVERSE_ALL
, -1,
1781 folder_item_find_func
, d
);
1785 static gchar
*feed_get_title(const gchar
*str
)
1787 gchar
*title
= NULL
;
1788 if (strstr(str
, "X-WR-CALNAME:")) {
1789 title
= g_strdup(strstr(str
, "X-WR-CALNAME:")+strlen("X-WR-CALNAME:"));
1790 } else if (strstr(str
, "X-WR-CALDESC:")) {
1791 title
= g_strdup(strstr(str
, "X-WR-CALDESC:")+strlen("X-WR-CALDESC:"));
1793 return strcrlftrunc(title
);
1796 static void update_subscription_finish(const gchar
*uri
, gchar
*feed
, gboolean verbose
, gchar
*error
)
1798 Folder
*root
= folder_find_from_name (PLUGIN_NAME
, vcal_folder_get_class());
1799 FolderItem
*item
= NULL
;
1800 icalcomponent
*cal
= NULL
;
1803 g_warning("can't get root folder");
1811 gchar
*err_msg
= _("Could not retrieve the Webcal URL:\n%s:\n\n%s");
1813 if (verbose
&& manual_update
) {
1814 gchar
*tmp
= g_strdup(uri
);
1815 if (strlen(uri
) > 61) {
1823 alertpanel_error(err_msg
, tmp
, error
? error
:_("Unknown error"));
1826 gchar
*msg
= g_strdup_printf("%s\n", err_msg
);
1827 log_error(LOG_PROTOCOL
, msg
, uri
, error
? error
:_("Unknown error"));
1830 main_window_cursor_normal(mainwindow_get_mainwindow());
1838 while (*tmp
&& isspace((unsigned char)*tmp
))
1841 if (strncmp(tmp
, "BEGIN:VCALENDAR", strlen("BEGIN:VCALENDAR"))) {
1842 gchar
*err_msg
= _("This URL does not look like a Webcal URL:\n%s\n%s");
1844 if (verbose
&& manual_update
) {
1845 alertpanel_error(err_msg
, uri
, error
? error
:_("Unknown error"));
1847 gchar
*msg
= g_strdup_printf("%s\n", err_msg
);
1848 log_error(LOG_PROTOCOL
, msg
, uri
, error
? error
:_("Unknown error"));
1852 main_window_cursor_normal(mainwindow_get_mainwindow());
1860 item
= get_folder_item_for_uri(uri
);
1862 gchar
*title
= feed_get_title(feed
);
1863 if (title
== NULL
) {
1864 if (strstr(uri
, "://"))
1865 title
= g_path_get_basename(strstr(uri
,"://")+3);
1867 title
= g_strdup(uri
);
1868 subst_for_filename(title
);
1870 item
= folder_create_folder(root
->node
->data
, title
);
1872 if (verbose
&& manual_update
) {
1873 alertpanel_error(_("Could not create directory %s"),
1876 log_error(LOG_PROTOCOL
, _("Could not create directory %s"),
1881 main_window_cursor_normal(mainwindow_get_mainwindow());
1884 debug_print("item done %s\n", title
);
1885 ((VCalFolderItem
*)item
)->uri
= g_strdup(uri
);
1886 ((VCalFolderItem
*)item
)->feed
= feed
;
1889 if (((VCalFolderItem
*)item
)->feed
)
1890 g_free(((VCalFolderItem
*)item
)->feed
);
1892 ((VCalFolderItem
*)item
)->feed
= feed
;
1893 /* if title differs, update it */
1895 cal
= icalparser_parse_string(feed
);
1897 convert_to_utc(cal
);
1899 if (((VCalFolderItem
*)item
)->cal
)
1900 icalcomponent_free(((VCalFolderItem
*)item
)->cal
);
1902 ((VCalFolderItem
*)item
)->cal
= cal
;
1904 main_window_cursor_normal(mainwindow_get_mainwindow());
1905 ((VCalFolderItem
*)item
)->last_fetch
= time(NULL
);
1908 static void update_subscription(const gchar
*uri
, gboolean verbose
)
1910 FolderItem
*item
= get_folder_item_for_uri(uri
);
1913 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 Webcal feed.")))
1921 if (time(NULL
) - ((VCalFolderItem
*)(item
))->last_fetch
< 60 &&
1922 ((VCalFolderItem
*)(item
))->cal
)
1925 main_window_cursor_wait(mainwindow_get_mainwindow());
1927 label
= g_strdup_printf(_("Fetching calendar for %s..."),
1928 item
&& item
->name
? item
->name
: _("new subscription"));
1929 vcal_curl_read(uri
, label
, verbose
, update_subscription_finish
);
1933 static void check_subs_cb(GtkAction
*action
, gpointer data
)
1935 Folder
*root
= folder_find_from_name (PLUGIN_NAME
, vcal_folder_get_class());
1937 if (prefs_common_get_prefs()->work_offline
&&
1938 !inc_offline_should_override(TRUE
,
1939 _("Claws Mail needs network access in order "
1940 "to update the subscription.")))
1943 folderview_check_new(root
);
1946 static void subscribe_cal_cb(GtkAction
*action
, gpointer data
)
1950 gchar
*clip_text
= NULL
, *str
= NULL
;
1952 clip_text
= gtk_clipboard_wait_for_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
));
1956 #if GLIB_CHECK_VERSION(2,66,0)
1957 GError
*error
= NULL
;
1960 /* skip any leading white-space */
1961 while (str
&& *str
&& g_ascii_isspace(*str
))
1963 uri
= g_uri_parse(str
, G_URI_FLAGS_PARSE_RELAXED
, &error
);
1965 g_warning("could not parse clipboard text for URI: '%s'", error
->message
);
1966 g_error_free(error
);
1969 gchar
* newstr
= g_uri_to_string(uri
);
1971 debug_print("URI: '%s' -> '%s'\n", str
, newstr
? newstr
: "N/A");
1977 if (!is_uri_string(str
)) {
1979 /* if no URL, ignore clipboard text */
1984 tmp
= input_dialog(_("Subscribe to Webcal"), _("Enter the Webcal URL:"), str
? str
: "");
1992 if (!strncmp(tmp
, "http", 4)) {
1994 } else if (!strncmp(tmp
, "file://", 7)) {
1996 } else if (!strncmp(tmp
, "webcal", 6)) {
1997 uri
= g_strconcat("http", tmp
+6, NULL
);
2000 alertpanel_error(_("Could not parse the URL."));
2004 debug_print("uri %s\n", uri
);
2006 update_subscription(uri
, TRUE
);
2007 folder_write_list();
2011 static void unsubscribe_cal_cb(GtkAction
*action
, gpointer data
)
2013 FolderView
*folderview
= (FolderView
*)data
;
2014 FolderItem
*item
, *opened
;
2019 if (!folderview
->selected
) return;
2021 item
= folderview_get_selected_item(folderview
);
2022 g_return_if_fail(item
!= NULL
);
2023 g_return_if_fail(item
->path
!= NULL
);
2024 g_return_if_fail(item
->folder
!= NULL
);
2025 opened
= folderview_get_opened_item(folderview
);
2027 message
= g_strdup_printf
2028 (_("Do you really want to unsubscribe?"));
2029 avalue
= alertpanel_full(_("Delete subscription"), message
,
2030 NULL
, _("_Cancel"), "edit-delete", _("_Delete"),
2031 NULL
, NULL
, ALERTFOCUS_FIRST
,
2032 FALSE
, NULL
, ALERT_WARNING
);
2034 if (avalue
!= G_ALERTALTERNATE
) return;
2036 old_id
= folder_item_get_identifier(item
);
2038 vcal_item_closed(item
);
2040 if (item
== opened
||
2041 folder_is_child_of(item
, opened
)) {
2042 summary_clear_all(folderview
->summaryview
);
2043 folderview_close_opened(folderview
, TRUE
);
2046 if (item
->folder
->klass
->remove_folder(item
->folder
, item
) < 0) {
2047 folder_item_scan(item
);
2048 alertpanel_error(_("Can't remove the folder '%s'."), item
->name
);
2053 folder_write_list();
2055 prefs_filtering_delete_path(old_id
);
2059 gboolean
vcal_subscribe_uri(Folder
*folder
, const gchar
*uri
)
2062 if (folder
->klass
!= vcal_folder_get_class())
2068 if (!strncmp(uri
, "webcal", 6)) {
2069 tmp
= g_strconcat("http", uri
+6, NULL
);
2073 debug_print("uri %s\n", tmp
);
2075 update_subscription(tmp
, FALSE
);
2076 folder_write_list();
2080 static void rename_cb(GtkAction
*action
, gpointer data
)
2082 FolderView
*folderview
= (FolderView
*)data
;
2088 item
= folderview_get_selected_item(folderview
);
2089 g_return_if_fail(item
!= NULL
);
2090 g_return_if_fail(item
->path
!= NULL
);
2091 g_return_if_fail(item
->folder
!= NULL
);
2093 name
= trim_string(item
->name
, 32);
2094 message
= g_strdup_printf(_("Input new name for '%s':"), name
);
2095 new_folder
= input_dialog(_("Rename folder"), message
, name
);
2098 if (!new_folder
) return;
2099 AUTORELEASE_STR(new_folder
, {g_free(new_folder
); return;});
2101 if (strchr(new_folder
, G_DIR_SEPARATOR
) != NULL
) {
2102 alertpanel_error(_("'%c' can't be included in folder name."),
2107 if (folder_find_child_item_by_name(folder_item_parent(item
), new_folder
)) {
2108 name
= trim_string(new_folder
, 32);
2109 alertpanel_error(_("The folder '%s' already exists."), name
);
2114 if (folder_item_rename(item
, new_folder
) < 0) {
2115 alertpanel_error(_("The folder could not be renamed.\n"
2116 "The new folder name is not allowed."));
2120 folder_item_prefs_save_config_recursive(item
);
2121 folder_write_list();
2124 static void set_view_cb(GtkAction
*gaction
, GtkRadioAction
*current
, gpointer data
)
2126 FolderView
*folderview
= (FolderView
*)data
;
2127 gint action
= gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current
));
2128 FolderItem
*item
= NULL
, *oitem
= NULL
;
2130 if (!folderview
->selected
) return;
2131 if (setting_sensitivity
) return;
2133 oitem
= folderview_get_opened_item(folderview
);
2134 item
= folderview_get_selected_item(folderview
);
2139 if (((VCalFolderItem
*)(item
))->use_cal_view
== action
)
2141 debug_print("set view %d\n", action
);
2142 if (oitem
&& item
== oitem
&& oitem
->folder
->klass
== vcal_folder_get_class())
2143 oitem
->folder
->klass
->item_closed(oitem
);
2144 ((VCalFolderItem
*)(item
))->use_cal_view
= action
;
2145 if (((VCalFolderItem
*)(item
))->use_cal_view
) {
2146 if (oitem
&& item
== oitem
&& oitem
->folder
->klass
== vcal_folder_get_class())
2147 oitem
->folder
->klass
->item_opened(oitem
);
2151 gchar
*vcal_get_event_as_ical_str(VCalEvent
*event
)
2154 icalcomponent
*calendar
= icalcomponent_vanew(
2155 ICAL_VCALENDAR_COMPONENT
,
2156 icalproperty_new_version("2.0"),
2157 icalproperty_new_prodid(
2158 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
2159 icalproperty_new_calscale("GREGORIAN"),
2161 vcal_manager_event_dump(event
, FALSE
, FALSE
, calendar
, FALSE
);
2162 ical
= g_strdup(icalcomponent_as_ical_string(calendar
));
2163 icalcomponent_free(calendar
);
2168 static gchar
*get_name_from_property(icalproperty
*p
)
2172 if (p
&& icalproperty_get_parameter_as_string(p
, "CN") != NULL
)
2173 tmp
= g_strdup(icalproperty_get_parameter_as_string(p
, "CN"));
2178 static gchar
*get_email_from_property(icalproperty
*p
)
2181 gchar
*email
= NULL
;
2184 tmp
= g_strdup(icalproperty_get_organizer(p
));
2189 if (!strncasecmp(tmp
, "MAILTO:", strlen("MAILTO:")))
2190 email
= g_strdup(tmp
+strlen("MAILTO:"));
2192 email
= g_strdup(tmp
);
2198 static void convert_to_utc(icalcomponent
*calendar
)
2200 icalcomponent
*event
;
2201 icaltimezone
*tz
, *tzutc
= icaltimezone_get_utc_timezone();
2203 icalparameter
*tzid
;
2205 cm_return_if_fail(calendar
!= NULL
);
2208 event
= icalcomponent_get_first_component(calendar
,
2209 ICAL_VEVENT_COMPONENT
);
2211 event
= icalcomponent_get_next_component(calendar
,
2212 ICAL_VEVENT_COMPONENT
)) {
2215 if ((prop
= icalcomponent_get_first_property(event
, ICAL_DTSTART_PROPERTY
)) != NULL
2216 && (tzid
= icalproperty_get_first_parameter(prop
, ICAL_TZID_PARAMETER
)) != NULL
) {
2217 /* Event has its DTSTART with a timezone specification, let's convert
2218 * to UTC and remove the TZID parameter. */
2220 tz
= icalcomponent_get_timezone(calendar
, icalparameter_get_iana_value(tzid
));
2222 debug_print("Converting DTSTART to UTC.\n");
2223 icaltimetype t
= icalproperty_get_dtstart(prop
);
2224 icaltimezone_convert_time(&t
, tz
, tzutc
);
2225 icalproperty_set_dtstart(prop
, t
);
2226 icalproperty_remove_parameter_by_ref(prop
, tzid
);
2231 if ((prop
= icalcomponent_get_first_property(event
, ICAL_DTEND_PROPERTY
)) != NULL
2232 && (tzid
= icalproperty_get_first_parameter(prop
, ICAL_TZID_PARAMETER
)) != NULL
) {
2233 /* Event has its DTEND with a timezone specification, let's convert
2234 * to UTC and remove the TZID parameter. */
2236 tz
= icalcomponent_get_timezone(calendar
, icalparameter_get_iana_value(tzid
));
2238 debug_print("Converting DTEND to UTC.\n");
2239 icaltimetype t
= icalproperty_get_dtend(prop
);
2240 icaltimezone_convert_time(&t
, tz
, tzutc
);
2241 icalproperty_set_dtend(prop
, t
);
2242 icalproperty_remove_parameter_by_ref(prop
, tzid
);
2248 #define GET_PROP(comp,prop,kind) { \
2250 if (!(prop = icalcomponent_get_first_property(comp, kind))) { \
2252 ? icalcomponent_get_first_property(inner, kind) \
2257 #define GET_PROP_LIST(comp,list,kind) { \
2259 if (!(prop = icalcomponent_get_first_property(comp, kind))) { \
2261 ? icalcomponent_get_first_property(inner, kind) \
2264 list = g_slist_prepend(list, prop); \
2265 } while ((prop = icalcomponent_get_next_property(inner, kind)));\
2267 list = g_slist_prepend(list, prop); \
2268 } while ((prop = icalcomponent_get_next_property(comp, kind))); \
2271 #define TO_UTF8(string) { \
2272 if (string && !g_utf8_validate(string, -1, NULL)) { \
2273 gchar *tmp = conv_codeset_strdup(string, \
2274 charset ? charset:conv_get_locale_charset_str(),\
2281 VCalEvent
*vcal_get_event_from_ical(const gchar
*ical
, const gchar
*charset
)
2283 VCalEvent
*event
= NULL
;
2284 gchar
*int_ical
= g_strdup(ical
);
2285 icalcomponent
*comp
= icalcomponent_new_from_string(int_ical
);
2286 icalcomponent
*inner
= NULL
;
2287 icalproperty
*prop
= NULL
;
2288 GSList
*list
= NULL
, *cur
= NULL
;
2290 gchar
*location
= NULL
;
2291 gchar
*summary
= NULL
;
2292 gchar
*dtstart
= NULL
;
2293 gchar
*dtend
= NULL
;
2294 gchar
*org_email
= NULL
, *org_name
= NULL
;
2295 gchar
*description
= NULL
;
2298 gchar
*recur
= NULL
;
2300 enum icalproperty_method method
= ICAL_METHOD_REQUEST
;
2301 enum icalcomponent_kind type
= ICAL_VEVENT_COMPONENT
;
2302 GSList
*attendees
= NULL
;
2309 if ((inner
= icalcomponent_get_inner(comp
)) != NULL
)
2310 type
= icalcomponent_isa(inner
);
2312 GET_PROP(comp
, prop
, ICAL_UID_PROPERTY
);
2314 uid
= g_strdup(icalproperty_get_uid(prop
));
2316 icalproperty_free(prop
);
2318 GET_PROP(comp
, prop
, ICAL_LOCATION_PROPERTY
);
2320 location
= g_strdup(icalproperty_get_location(prop
));
2322 icalproperty_free(prop
);
2324 GET_PROP(comp
, prop
, ICAL_SUMMARY_PROPERTY
);
2326 summary
= g_strdup(icalproperty_get_summary(prop
));
2328 icalproperty_free(prop
);
2331 convert_to_utc(comp
);
2333 GET_PROP(comp
, prop
, ICAL_DTSTART_PROPERTY
);
2335 dtstart
= g_strdup(icaltime_as_ical_string(icalproperty_get_dtstart(prop
)));
2337 icalproperty_free(prop
);
2339 GET_PROP(comp
, prop
, ICAL_DTEND_PROPERTY
);
2341 dtend
= g_strdup(icaltime_as_ical_string(icalproperty_get_dtend(prop
)));
2343 icalproperty_free(prop
);
2345 GET_PROP(comp
, prop
, ICAL_DURATION_PROPERTY
);
2347 struct icaldurationtype duration
= icalproperty_get_duration(prop
);
2348 struct icaltimetype itt
;
2349 icalproperty_free(prop
);
2350 GET_PROP(comp
, prop
, ICAL_DTSTART_PROPERTY
);
2352 itt
= icalproperty_get_dtstart(prop
);
2353 icalproperty_free(prop
);
2354 dtend
= g_strdup(icaltime_as_ical_string(icaltime_add(itt
,duration
)));
2359 GET_PROP(comp
, prop
, ICAL_SEQUENCE_PROPERTY
);
2361 sequence
= icalproperty_get_sequence(prop
);
2362 icalproperty_free(prop
);
2364 GET_PROP(comp
, prop
, ICAL_METHOD_PROPERTY
);
2366 method
= icalproperty_get_method(prop
);
2367 icalproperty_free(prop
);
2369 GET_PROP(comp
, prop
, ICAL_ORGANIZER_PROPERTY
);
2371 org_email
= get_email_from_property(prop
);
2373 org_name
= get_name_from_property(prop
);
2375 icalproperty_free(prop
);
2377 GET_PROP(comp
, prop
, ICAL_DESCRIPTION_PROPERTY
);
2379 description
= g_strdup(icalproperty_get_description(prop
));
2380 TO_UTF8(description
);
2381 icalproperty_free(prop
);
2383 GET_PROP(comp
, prop
, ICAL_URL_PROPERTY
);
2385 url
= g_strdup(icalproperty_get_url(prop
));
2387 icalproperty_free(prop
);
2389 GET_PROP(comp
, prop
, ICAL_TZID_PROPERTY
);
2391 tzid
= g_strdup(icalproperty_get_tzid(prop
));
2393 icalproperty_free(prop
);
2395 GET_PROP(comp
, prop
, ICAL_RRULE_PROPERTY
);
2397 struct icalrecurrencetype rrule
= icalproperty_get_rrule(prop
);
2398 recur
= g_strdup(icalrecurrencetype_as_string(&rrule
));
2400 icalproperty_free(prop
);
2402 GET_PROP_LIST(comp
, list
, ICAL_ATTENDEE_PROPERTY
);
2403 for (cur
= list
; cur
; cur
= cur
->next
) {
2404 enum icalparameter_partstat partstat
= 0;
2405 enum icalparameter_cutype cutype
= 0;
2406 icalparameter
*param
= NULL
;
2407 gchar
*email
= NULL
;
2409 Answer
*answer
= NULL
;
2411 prop
= (icalproperty
*)(cur
->data
);
2413 email
= get_email_from_property(prop
);
2415 name
= get_name_from_property(prop
);
2418 param
= icalproperty_get_first_parameter(prop
, ICAL_PARTSTAT_PARAMETER
);
2420 partstat
= icalparameter_get_partstat(param
);
2422 param
= icalproperty_get_first_parameter(prop
, ICAL_CUTYPE_PARAMETER
);
2424 cutype
= icalparameter_get_cutype(param
);
2427 partstat
= ICAL_PARTSTAT_NEEDSACTION
;
2429 cutype
= ICAL_CUTYPE_INDIVIDUAL
;
2430 answer
= answer_new(email
, name
, partstat
, cutype
);
2431 attendees
= g_slist_prepend(attendees
, answer
);
2434 icalproperty_free(prop
);
2438 event
= vcal_manager_new_event (uid
, org_email
, org_name
,
2439 location
, summary
, description
,
2440 dtstart
, dtend
, recur
,
2442 method
, sequence
, type
);
2443 event
->answers
= attendees
;
2451 g_free(description
);
2456 icalcomponent_free(comp
);
2460 gboolean
vcal_event_exists(const gchar
*id
)
2462 MsgInfo
*info
= NULL
;
2463 Folder
*folder
= folder_find_from_name (PLUGIN_NAME
, vcal_folder_get_class());
2467 info
= folder_item_get_msginfo_by_msgid(folder
->inbox
, id
);
2469 procmsg_msginfo_free(&info
);
2475 void vcal_foreach_event(gboolean (*cb_func
)(const gchar
*vevent
))
2477 GSList
*list
= vcal_folder_get_waiting_events();
2481 debug_print("calling cb_func...\n");
2482 for (cur
= list
; cur
; cur
= cur
->next
) {
2483 VCalEvent
*event
= (VCalEvent
*)cur
->data
;
2484 gchar
*tmp
= vcal_get_event_as_ical_str(event
);
2486 debug_print(" ...for event %s\n", event
->uid
);
2489 vcal_manager_free_event(event
);
2494 /* please call vcalendar_refresh_folder_contents() after one or more
2495 * calls to this function */
2496 gboolean
vcal_delete_event(const gchar
*id
)
2498 MsgInfo
*info
= NULL
;
2499 Folder
*folder
= folder_find_from_name (PLUGIN_NAME
, vcal_folder_get_class());
2503 info
= folder_item_get_msginfo_by_msgid(folder
->inbox
, id
);
2505 debug_print("removing event %s\n", id
);
2506 vcal_remove_event(folder
, info
);
2507 procmsg_msginfo_free(&info
);
2508 folder_item_scan(folder
->inbox
);
2511 debug_print("not removing unexisting event %s\n", id
);
2515 /* please call vcalendar_refresh_folder_contents() after one or more
2516 * calls to this function */
2517 gchar
* vcal_add_event(const gchar
*vevent
)
2519 VCalEvent
*event
= vcal_get_event_from_ical(vevent
, NULL
);
2520 gchar
*retVal
= NULL
;
2521 Folder
*folder
= folder_find_from_name (PLUGIN_NAME
, vcal_folder_get_class());
2523 vcal_manager_free_event(event
);
2528 if (vcal_event_exists(event
->uid
)) {
2529 debug_print("event %s already exists\n", event
->uid
);
2530 vcal_manager_free_event(event
);
2533 debug_print("adding event %s\n", event
->uid
);
2534 if (!account_find_from_address(event
->organizer
, FALSE
) &&
2535 !vcal_manager_get_account_from_event(event
)) {
2536 PrefsAccount
*account
= account_get_default();
2537 vcal_manager_update_answer(event
, account
->address
,
2539 ICAL_PARTSTAT_ACCEPTED
,
2540 ICAL_CUTYPE_INDIVIDUAL
);
2541 debug_print("can't find our accounts in event, adding default\n");
2543 vcal_manager_save_event(event
, TRUE
);
2544 folder_item_scan(folder
->inbox
);
2545 retVal
= vcal_get_event_as_ical_str(event
);
2546 vcal_manager_free_event(event
);
2552 /* please call vcalendar_refresh_folder_contents() after one or more
2553 * calls to this function */
2554 gchar
* vcal_update_event(const gchar
*vevent
)
2556 VCalEvent
*event
= vcal_get_event_from_ical(vevent
, NULL
);
2559 r
= vcal_delete_event(event
->uid
);
2560 vcal_manager_free_event(event
);
2562 return vcal_add_event(vevent
);