2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2013 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, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "claws-features.h"
28 #include <glib/gi18n.h>
35 #include <libical/ical.h>
37 #include <gdk/gdkkeysyms.h>
38 #include <curl/curl.h>
39 #include <curl/curlver.h>
42 #include "vcalendar.h"
43 #include "vcal_folder.h"
44 #include "vcal_manager.h"
45 #include "vcal_meeting_gtk.h"
46 #include "vcal_prefs.h"
47 #include "common-views.h"
48 #include "mainwindow.h"
49 #include "prefs_account.h"
52 #include "alertpanel.h"
53 #include "addr_compl.h"
72 GtkWidget *avail_evtbox;
75 GtkWidget *start_time;
80 GtkWidget *description;
82 GtkWidget *attendees_vbox;
85 GSList *avail_accounts;
86 GtkWidget *total_avail_evtbox;
87 GtkWidget *total_avail_img;
88 GtkWidget *total_avail_msg;
89 PrefsAccount *account;
93 struct _VCalAttendee {
95 GtkWidget *remove_btn;
101 GtkWidget *avail_evtbox;
102 GtkWidget *avail_img;
103 gchar *cached_contents;
107 static GdkCursor *watch_cursor = NULL;
109 VCalAttendee *attendee_add(VCalMeeting *meet, gchar *address, gchar *name, gchar *partstat, gchar *cutype, gboolean first);
112 #define TABLE_ADD_LINE(label_text, widget, do_space) { \
113 gchar *tmpstr = g_strdup_printf("<span weight=\"bold\">%s</span>", \
114 label_text?label_text:""); \
115 GtkWidget *label = NULL; \
116 GtkWidget *spacer = NULL; \
117 GtkWidget *s_hbox = NULL; \
119 spacer = gtk_label_new(""); \
120 gtk_widget_set_size_request(spacer, 18, 16); \
121 s_hbox = gtk_hbox_new(FALSE, 6); \
122 gtk_box_pack_start(GTK_BOX(s_hbox), spacer, FALSE, FALSE, 0); \
123 gtk_box_pack_start(GTK_BOX(s_hbox), widget, TRUE, TRUE, 0); \
126 label = gtk_label_new(tmpstr); \
128 gtk_label_set_use_markup (GTK_LABEL (label), TRUE); \
129 gtk_misc_set_alignment (GTK_MISC(label), 1, 0.5); \
130 gtk_table_attach (GTK_TABLE (meet->table), \
131 label, 0, 1, i, i+1, \
132 GTK_FILL, GTK_FILL, 6, 6); \
133 gtk_table_attach (GTK_TABLE (meet->table), \
134 do_space?s_hbox:widget, 1, 2, i, i+1, \
135 GTK_FILL|GTK_EXPAND, GTK_FILL, 6, 6); \
136 if (GTK_IS_LABEL(widget)) { \
137 gtk_label_set_use_markup(GTK_LABEL (widget), TRUE); \
138 gtk_misc_set_alignment (GTK_MISC(widget),0, 0); \
139 gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE); \
143 gtk_table_attach (GTK_TABLE (meet->table), \
144 do_space?s_hbox:widget, 0, 2, i, i+1, \
145 GTK_FILL|GTK_EXPAND, GTK_FILL, 6, 6); \
150 #define TABLE_ADD_LINE(label_text, widget, do_space, intable1) { \
151 gchar *tmpstr = g_strdup_printf("<span weight=\"bold\">%s</span>", \
152 label_text?label_text:""); \
153 GtkWidget *label = NULL; \
154 GtkWidget *spacer = NULL; \
155 GtkWidget *s_hbox = NULL; \
157 spacer = gtk_label_new(""); \
158 gtk_widget_set_size_request(spacer, 18, 16); \
159 s_hbox = gtk_hbox_new(FALSE, 6); \
160 gtk_box_pack_start(GTK_BOX(s_hbox), spacer, FALSE, FALSE, 0); \
161 gtk_box_pack_start(GTK_BOX(s_hbox), widget, TRUE, TRUE, 0); \
164 label = gtk_label_new(tmpstr); \
166 gtk_label_set_use_markup (GTK_LABEL (label), TRUE); \
167 gtk_misc_set_alignment (GTK_MISC(label), 1, 0.5); \
169 gtk_table_attach (GTK_TABLE (meet->table1), \
170 label, 0, 1, i, i+1, \
171 GTK_FILL, GTK_FILL, 1, 1); \
174 gtk_table_attach (GTK_TABLE (meet->table2), \
175 label, 0, 1, i, i+1, \
176 GTK_FILL, GTK_FILL, 1, 1); \
179 gtk_table_attach (GTK_TABLE (meet->table1), \
180 do_space?s_hbox:widget, 1, 2, i, i+1, \
181 GTK_FILL|GTK_EXPAND, GTK_FILL, 1, 1); \
184 gtk_table_attach (GTK_TABLE (meet->table2), \
185 do_space?s_hbox:widget, 1, 2, i, i+1, \
186 GTK_FILL|GTK_EXPAND, GTK_FILL, 1, 1); \
188 if (GTK_IS_LABEL(widget)) { \
189 gtk_label_set_use_markup(GTK_LABEL (widget), TRUE); \
190 gtk_misc_set_alignment (GTK_MISC(widget),0, 0); \
191 gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE); \
196 gtk_table_attach (GTK_TABLE (meet->table1), \
197 do_space?s_hbox:widget, 0, 2, i, i+1, \
198 GTK_FILL|GTK_EXPAND, GTK_FILL, 1, 1); \
201 gtk_table_attach (GTK_TABLE (meet->table2), \
202 do_space?s_hbox:widget, 0, 2, i, i+1, \
203 GTK_FILL|GTK_EXPAND, GTK_FILL, 1, 1); \
217 static gboolean avail_btn_can_be_sensitive(void)
219 if (vcalprefs.freebusy_get_url == NULL
220 || *vcalprefs.freebusy_get_url == '\0')
226 static gint get_dtdate(const gchar *str, gint field)
228 time_t t = icaltime_as_timet((icaltime_from_string(str)));
238 lt = localtime_r(&t, &buft);
244 return lt->tm_mon + 1;
246 return lt->tm_year + 1900;
256 static gboolean add_btn_cb(GtkButton *widget, gpointer data)
258 VCalAttendee *attendee = (VCalAttendee *)data;
259 attendee_add(attendee->meet, NULL, NULL, NULL, NULL, FALSE);
263 static gboolean remove_btn_cb(GtkButton *widget, gpointer data)
265 VCalAttendee *attendee = (VCalAttendee *)data;
266 gtk_container_remove(GTK_CONTAINER(attendee->meet->attendees_vbox), attendee->hbox);
267 attendee->meet->attendees = g_slist_remove(attendee->meet->attendees, attendee);
269 g_free(attendee->status);
274 VCalAttendee *attendee_add(VCalMeeting *meet, gchar *address, gchar *name, gchar *partstat, gchar *cutype, gboolean first)
276 GtkWidget *att_hbox = gtk_hbox_new(FALSE, 6);
277 VCalAttendee *attendee = g_new0(VCalAttendee, 1);
279 attendee->address = gtk_entry_new();
280 attendee->cutype = gtk_combo_box_text_new();
281 attendee->avail_evtbox = gtk_event_box_new();
282 attendee->avail_img = gtk_image_new_from_stock
283 (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
285 gtk_widget_show(attendee->address);
286 gtk_widget_show(attendee->cutype);
287 gtk_widget_show(attendee->avail_evtbox);
289 CLAWS_SET_TIP(attendee->address, _("Use <tab> to autocomplete from addressbook"));
290 gtk_widget_set_size_request(attendee->avail_evtbox, 18, 16);
291 gtk_event_box_set_visible_window(GTK_EVENT_BOX(attendee->avail_evtbox), FALSE);
292 gtk_container_add (GTK_CONTAINER(attendee->avail_evtbox), attendee->avail_img);
295 gchar *str = g_strdup_printf("%s%s%s%s",
296 (name && strlen(name))?name:"",
297 (name && strlen(name))?" <":"",
299 (name && strlen(name))?">":"");
300 gtk_entry_set_text(GTK_ENTRY(attendee->address), str);
305 attendee->status = g_strdup(partstat);
307 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(attendee->cutype), _("Individual"));
308 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(attendee->cutype), _("Group"));
309 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(attendee->cutype), _("Resource"));
310 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(attendee->cutype), _("Room"));
312 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 0);
315 if (!strcmp(cutype, "group"))
316 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 1);
317 if (!strcmp(cutype, "resource"))
318 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 2);
319 if (!strcmp(cutype, "room"))
320 gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 3);
323 attendee->add_btn = gtk_button_new_with_label(_("Add..."));
324 attendee->remove_btn = gtk_button_new_with_label(_("Remove"));
325 attendee->meet = meet;
326 attendee->hbox = att_hbox;
328 gtk_widget_show(attendee->add_btn);
329 gtk_widget_show(attendee->remove_btn);
330 gtk_widget_show(attendee->hbox);
332 gtk_box_pack_start(GTK_BOX(attendee->hbox), attendee->avail_evtbox, FALSE, FALSE, 0);
333 gtk_widget_set_sensitive(attendee->remove_btn, !first);
334 meet->attendees = g_slist_append(meet->attendees, attendee);
336 g_signal_connect(G_OBJECT(attendee->remove_btn), "clicked",
337 G_CALLBACK(remove_btn_cb), attendee);
338 g_signal_connect(G_OBJECT(attendee->add_btn), "clicked",
339 G_CALLBACK(add_btn_cb), attendee);
341 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->address, FALSE, FALSE, 0);
342 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->cutype, FALSE, FALSE, 0);
343 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->add_btn, FALSE, FALSE, 0);
344 gtk_box_pack_start(GTK_BOX(att_hbox), attendee->remove_btn, FALSE, FALSE, 0);
345 gtk_box_pack_start(GTK_BOX(meet->attendees_vbox), att_hbox, FALSE, FALSE, 0);
346 address_completion_register_entry(GTK_ENTRY(attendee->address), FALSE);
348 gtk_widget_set_size_request(attendee->address, 320, -1);
350 gtk_widget_set_size_request(attendee->address, 220, -1);
355 static gchar *get_organizer(VCalMeeting *meet)
357 int index = gtk_combo_box_get_active(GTK_COMBO_BOX(meet->who));
359 GSList *cur = meet->avail_accounts;
360 while (i < index && cur && cur->data) {
361 debug_print("%d:skipping %s\n",i,((PrefsAccount *)(cur->data))->address);
365 if (cur && cur->data)
366 return g_strdup(((PrefsAccount *)(cur->data))->address);
371 static gchar *get_organizer_name(VCalMeeting *meet)
373 int index = gtk_combo_box_get_active(GTK_COMBO_BOX(meet->who));
375 GSList *cur = meet->avail_accounts;
376 while (i < index && cur && cur->data) {
377 debug_print("%d:skipping %s\n",i,((PrefsAccount *)(cur->data))->address);
381 if (cur && cur->data)
382 return g_strdup(((PrefsAccount *)(cur->data))->name);
387 static int get_current_gmt_offset(void)
389 time_t now = time(NULL);
399 gmtime_r(& now, & gmt);
400 localtime_r(& now, & local);
403 return mktime(&local)-mktime(&gmt);
406 static int get_gmt_offset_at_time(time_t then)
417 gmtime_r(& then, & gmt);
418 localtime_r(& then, & local);
421 return mktime(&local)-mktime(&gmt);
424 static gchar *get_date(VCalMeeting *meet, int start)
439 lt = localtime_r(&t, &buft);
441 gtk_calendar_get_date(GTK_CALENDAR(start ? meet->start_c : meet->end_c), &y, &m, &d);
444 lt->tm_year = y - 1900;
450 gtkut_time_select_get_time(GTK_COMBO_BOX(meet->start_time), <->tm_hour, <->tm_min);
452 gtkut_time_select_get_time(GTK_COMBO_BOX(meet->end_time), <->tm_hour, <->tm_min);
455 debug_print("%d %d %d, %d:%d\n", lt->tm_mday, lt->tm_mon, lt->tm_year, lt->tm_hour, lt->tm_min);
458 dst_offset = get_current_gmt_offset() - get_gmt_offset_at_time(t);
459 debug_print("DST change offset to apply to time %d\n", dst_offset);
461 debug_print("%s\n", ctime(&t));
462 return g_strdup(icaltime_as_ical_string(icaltime_from_timet_with_zone(t, FALSE, NULL)));
465 static gchar *get_location(VCalMeeting *meet)
467 return gtk_editable_get_chars(GTK_EDITABLE(meet->location),0, -1);
470 static gchar *get_summary(VCalMeeting *meet)
472 return gtk_editable_get_chars(GTK_EDITABLE(meet->summary),0, -1);
475 static gchar *get_description(VCalMeeting *meet)
477 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
478 GtkTextIter start, end;
480 gtk_text_buffer_get_start_iter(buffer, &start);
481 gtk_text_buffer_get_end_iter(buffer, &end);
482 return gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
485 void vcal_meeting_free(VCalMeeting *meet)
487 debug_print("freeing meeting\n");
489 address_completion_end(meet->window);
490 g_slist_free(meet->avail_accounts);
491 g_slist_free(meet->attendees);
495 static void destroy_meeting_cb(GtkWidget *widget, gpointer data)
497 VCalMeeting *meet = (VCalMeeting *)data;
498 vcal_meeting_free(meet);
501 static void vcal_destroy(VCalMeeting *meet)
503 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
504 gtk_text_buffer_remove_selection_clipboard(buffer, gtk_clipboard_get(GDK_SELECTION_PRIMARY));
505 gtk_widget_destroy(meet->window);
508 static gboolean meeting_key_pressed(GtkWidget *widget,
512 VCalMeeting *meet = (VCalMeeting *)data;
514 if (event && event->keyval == GDK_KEY_Escape) {
520 static void meeting_end_changed(GtkWidget *widget, gpointer data);
522 static void meeting_start_changed(GtkWidget *widget, gpointer data)
524 VCalMeeting *meet = (VCalMeeting *)data;
527 time_t start_t, end_t;
530 if (!gtkut_time_select_get_time(GTK_COMBO_BOX(meet->start_time), &start_lt.tm_hour, &start_lt.tm_min))
534 start_t = time(NULL);
542 localtime_r(&start_t, &start_lt);
543 localtime_r(&end_t, &end_lt);
545 gtk_calendar_get_date(GTK_CALENDAR(meet->start_c), &y, &m, &d);
546 start_lt.tm_mday = d; start_lt.tm_mon = m; start_lt.tm_year = y - 1900;
548 start_t = mktime(&start_lt);
549 debug_print("start %s\n", ctime(&start_t));
551 gtk_calendar_get_date(GTK_CALENDAR(meet->end_c), &y, &m, &d);
552 end_lt.tm_mday = d; end_lt.tm_mon = m; end_lt.tm_year = y - 1900;
554 gtkut_time_select_get_time(GTK_COMBO_BOX(meet->end_time), &end_lt.tm_hour, &end_lt.tm_min);
556 end_t = mktime(&end_lt);
558 debug_print("end %s\n", ctime(&end_t));
560 if (end_t > start_t) {
564 end_t = start_t + 3600;
570 localtime_r(&end_t, &end_lt);
571 debug_print("n %d %d %d, %d:%d\n", end_lt.tm_mday, end_lt.tm_mon, end_lt.tm_year, end_lt.tm_hour, end_lt.tm_min);
573 g_signal_handlers_block_by_func(gtk_bin_get_child(GTK_BIN(meet->end_time)), meeting_end_changed, meet);
574 g_signal_handlers_block_by_func(meet->end_c, meeting_end_changed, meet);
576 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c), end_lt.tm_mday);
578 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
580 end_lt.tm_year + 1900);
582 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time), end_lt.tm_hour, end_lt.tm_min);
584 g_signal_handlers_unblock_by_func(gtk_bin_get_child(GTK_BIN(meet->end_time)), meeting_end_changed, meet);
585 g_signal_handlers_unblock_by_func(meet->end_c, meeting_end_changed, meet);
588 static void meeting_end_changed(GtkWidget *widget, gpointer data)
590 VCalMeeting *meet = (VCalMeeting *)data;
593 time_t start_t, end_t;
596 if (!gtkut_time_select_get_time(GTK_COMBO_BOX(meet->end_time), &end_lt.tm_hour, &end_lt.tm_min))
598 start_t = time(NULL);
609 localtime_r(&start_t, &start_lt);
610 localtime_r(&end_t, &end_lt);
612 gtk_calendar_get_date(GTK_CALENDAR(meet->start_c), &y, &m, &d);
613 start_lt.tm_mday = d; start_lt.tm_mon = m; start_lt.tm_year = y - 1900;
614 gtkut_time_select_get_time(GTK_COMBO_BOX(meet->start_time), &start_lt.tm_hour, &start_lt.tm_min);
616 start_t = mktime(&start_lt);
617 debug_print("start %s\n", ctime(&start_t));
619 gtk_calendar_get_date(GTK_CALENDAR(meet->end_c), &y, &m, &d);
620 end_lt.tm_mday = d; end_lt.tm_mon = m; end_lt.tm_year = y - 1900;
622 end_t = mktime(&end_lt);
624 debug_print("end %s\n", ctime(&end_t));
626 if (end_t > start_t) {
630 start_t = end_t - 3600;
638 localtime_r(&start_t, &start_lt);
639 debug_print("n %d %d %d, %d:%d\n", start_lt.tm_mday, start_lt.tm_mon, start_lt.tm_year, start_lt.tm_hour, start_lt.tm_min);
641 g_signal_handlers_block_by_func(gtk_bin_get_child(GTK_BIN(meet->start_time)), meeting_start_changed, meet);
642 g_signal_handlers_block_by_func(meet->start_c, meeting_start_changed, meet);
644 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c), start_lt.tm_mday);
646 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
648 start_lt.tm_year + 1900);
650 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->start_time), start_lt.tm_hour, start_lt.tm_min);
652 g_signal_handlers_unblock_by_func(gtk_bin_get_child(GTK_BIN(meet->start_time)), meeting_start_changed, meet);
653 g_signal_handlers_unblock_by_func(meet->start_c, meeting_start_changed, meet);
656 static void att_update_icon(VCalMeeting *meet, VCalAttendee *attendee, gint avail, gchar *text)
658 const gchar *icon = GTK_STOCK_DIALOG_INFO;
661 case 0: icon = GTK_STOCK_DIALOG_WARNING; break;
662 case 1: icon = GTK_STOCK_DIALOG_INFO; break;
663 default: icon = GTK_STOCK_DIALOG_QUESTION; break;
665 if (!gtk_entry_get_text(GTK_ENTRY(attendee->address))
666 || strlen(gtk_entry_get_text(GTK_ENTRY(attendee->address)))==0) {
667 if (attendee->avail_img) {
668 gtk_widget_hide(attendee->avail_img);
670 CLAWS_SET_TIP(attendee->avail_evtbox, NULL);
671 } else if (attendee->avail_img) {
672 gtk_image_set_from_stock
673 (GTK_IMAGE(attendee->avail_img),
675 GTK_ICON_SIZE_SMALL_TOOLBAR);
676 gtk_widget_show(attendee->avail_img);
677 CLAWS_SET_TIP(attendee->avail_evtbox, text);
681 gboolean attendee_available(VCalAttendee *attendee, const gchar *dtstart, const gchar *dtend, const gchar *contents)
683 icalcomponent *toplvl, *vfreebusy;
684 icalproperty *busyprop;
685 struct icaltimetype start = icaltime_from_string(dtstart);
686 struct icaltimetype end = icaltime_from_string(dtend);
687 gboolean result = TRUE;
690 if (contents == NULL)
693 toplvl = icalcomponent_new_from_string((gchar *)contents);
698 vfreebusy = icalcomponent_get_first_component(toplvl, ICAL_VFREEBUSY_COMPONENT);
699 while (vfreebusy && icalcomponent_isa(vfreebusy) != ICAL_VFREEBUSY_COMPONENT)
700 vfreebusy = icalcomponent_get_next_component(toplvl, ICAL_VFREEBUSY_COMPONENT);
703 busyprop = icalcomponent_get_first_property(vfreebusy, ICAL_FREEBUSY_PROPERTY);
705 struct icalperiodtype ipt = icalproperty_get_freebusy(busyprop);
707 if ( icaltime_compare(start, ipt.end) >= 0 || icaltime_compare(end, ipt.start) <= 0 ) {
713 busyprop = icalcomponent_get_next_property(vfreebusy, ICAL_FREEBUSY_PROPERTY);
717 icalcomponent_free(toplvl);
721 static gchar *get_avail_msg(const gchar *unavailable_persons, gboolean multiple,
722 gboolean short_version, gint offset_before, gint offset_after)
724 gchar *msg, *intro = NULL, *outro = NULL, *before = NULL, *after = NULL;
727 intro = g_strdup(_("The following people are busy at the time of your planned meeting:\n- "));
728 else if (!strcmp(unavailable_persons, _("You")))
729 intro = g_strdup(_("You are busy at the time of your planned meeting"));
731 intro = g_strdup_printf(_("%s is busy at the time of your planned meeting"), unavailable_persons);
732 if (offset_before == 3600)
733 before = g_strdup_printf(_("%d hour sooner"), offset_before/3600);
734 else if (offset_before > 3600 && offset_before%3600 == 0)
735 before = g_strdup_printf(_("%d hours sooner"), offset_before/3600);
736 else if (offset_before > 3600)
737 before = g_strdup_printf(_("%d hours and %d minutes sooner"), offset_before/3600, (offset_before%3600)/60);
738 else if (offset_before == 1800)
739 before = g_strdup_printf(_("%d minutes sooner"), offset_before/60);
743 if (offset_after == 3600)
744 after = g_strdup_printf(_("%d hour later"), offset_after/3600);
745 else if (offset_after > 3600 && offset_after%3600 == 0)
746 after = g_strdup_printf(_("%d hours later"), offset_after/3600);
747 else if (offset_after > 3600)
748 after = g_strdup_printf(_("%d hours and %d minutes later"), offset_after/3600, (offset_after%3600)/60);
749 else if (offset_after == 1800)
750 after = g_strdup_printf(_("%d minutes later"), offset_after/60);
756 outro = g_strdup_printf(_("\n\nEveryone would be available %s or %s."), before, after);
757 else if (before || after)
758 outro = g_strdup_printf(_("\n\nEveryone would be available %s."), before?before:after);
760 outro = g_strdup_printf(_("\n\nIt isn't possible to have this meeting with everyone "
761 "in the previous or next 6 hours."));
765 outro = g_markup_printf_escaped(_("would be available %s or %s"), before, after);
766 else if (before || after)
767 outro = g_markup_printf_escaped(_("would be available %s"), before?before:after);
769 outro = g_strdup_printf(_("not available"));
772 outro = g_markup_printf_escaped(_(", but would be available %s or %s."), before, after);
773 else if (before || after)
774 outro = g_markup_printf_escaped(_(", but would be available %s."), before?before:after);
776 outro = g_strdup_printf(_(", and isn't available "
777 "in the previous or next 6 hours."));
780 if (multiple && short_version)
781 msg = g_strconcat(outro+2, NULL);
783 msg = g_strconcat(intro, unavailable_persons, outro, NULL);
784 else if (short_version)
785 msg = g_strdup(outro);
787 msg = g_strconcat(intro, outro, NULL);
795 static gboolean find_availability(const gchar *dtstart, const gchar *dtend, GSList *attendees, gboolean for_send, VCalMeeting *meet)
798 gint offset = -1800, offset_before = 0, offset_after = 0;
799 gboolean found = FALSE;
800 gchar *unavailable_persons = NULL;
802 struct icaltimetype start = icaltime_from_string(dtstart);
803 struct icaltimetype end = icaltime_from_string(dtend);
804 AlertValue val = G_ALERTALTERNATE;
806 GHashTable *avail_table_avail = g_hash_table_new(NULL, g_direct_equal);
807 GHashTable *avail_table_before = g_hash_table_new(NULL, g_direct_equal);
808 GHashTable *avail_table_after = g_hash_table_new(NULL, g_direct_equal);
810 for (cur = attendees; cur; cur = cur->next) {
811 VCalAttendee *attendee = (VCalAttendee *)cur->data;
812 if (!attendee_available(attendee, icaltime_as_ical_string(start), icaltime_as_ical_string(end),
813 attendee->cached_contents)) {
817 mail = g_strdup(_("You"));
819 mail = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
821 if (unavailable_persons == NULL) {
822 unavailable_persons = g_markup_printf_escaped("%s", mail);
824 gchar *tmp = g_markup_printf_escaped("%s,\n- %s", unavailable_persons, mail);
825 g_free(unavailable_persons);
826 unavailable_persons = tmp;
830 att_update_icon(meet, attendee, 0, _("not available"));
832 if (attendee->cached_contents != NULL)
833 att_update_icon(meet, attendee, 1, _("available"));
835 att_update_icon(meet, attendee, 2, _("Free/busy retrieval failed"));
837 g_hash_table_insert(avail_table_avail, attendee, GINT_TO_POINTER(1));
842 while (!found && offset >= -3600*6) {
844 struct icaltimetype new_start = icaltime_from_timet_with_zone(icaltime_as_timet(start)+offset, FALSE, NULL);
845 struct icaltimetype new_end = icaltime_from_timet_with_zone(icaltime_as_timet(end)+offset, FALSE, NULL);
846 for (cur = attendees; cur; cur = cur->next) {
847 VCalAttendee *attendee = (VCalAttendee *)cur->data;
848 debug_print("trying %s - %s (offset %d)\n",
849 icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end), offset);
850 if (!attendee_available(attendee, icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end),
851 attendee->cached_contents)) {
855 if (!g_hash_table_lookup(avail_table_before, attendee)
856 && !g_hash_table_lookup(avail_table_avail, attendee))
857 g_hash_table_insert(avail_table_before, attendee, GINT_TO_POINTER(-offset));
862 offset_before = -offset;
868 while (!found && offset <= 3600*6) {
870 struct icaltimetype new_start = icaltime_from_timet_with_zone(icaltime_as_timet(start)+offset, FALSE, NULL);
871 struct icaltimetype new_end = icaltime_from_timet_with_zone(icaltime_as_timet(end)+offset, FALSE, NULL);
872 for (cur = attendees; cur; cur = cur->next) {
873 VCalAttendee *attendee = (VCalAttendee *)cur->data;
874 debug_print("trying %s - %s (offset %d)\n",
875 icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end), offset);
876 if (!attendee_available(attendee, icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end),
877 attendee->cached_contents)) {
881 if (!g_hash_table_lookup(avail_table_after, attendee)
882 && !g_hash_table_lookup(avail_table_avail, attendee))
883 g_hash_table_insert(avail_table_after, attendee, GINT_TO_POINTER(offset));
888 offset_after = offset;
894 for (cur = attendees; cur; cur = cur->next) {
895 VCalAttendee *attendee = (VCalAttendee *)cur->data;
896 gint ok = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_avail, attendee));
897 gint o_before = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_before, attendee));
898 gint o_after = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_after, attendee));
899 if (!o_before && !o_after && !ok) {
900 att_update_icon(meet, attendee, 0, _("not available"));
901 } else if ((o_before != 0 || o_after != 0) && !ok) {
903 msg = get_avail_msg(_("You"), FALSE, TRUE, o_before, o_after);
905 msg = get_avail_msg(gtk_entry_get_text(GTK_ENTRY(attendee->address)), FALSE, TRUE, o_before, o_after);
906 att_update_icon(meet, attendee, 0, msg);
911 g_hash_table_destroy(avail_table_before);
912 g_hash_table_destroy(avail_table_after);
915 msg = get_avail_msg(unavailable_persons, (total > 1), FALSE, offset_before, offset_after);
917 val = alertpanel_full(_("Not everyone is available"), msg,
918 GTK_STOCK_CANCEL, _("Send anyway"), NULL, ALERTFOCUS_FIRST,
919 FALSE, NULL, ALERT_QUESTION);
922 msg = get_avail_msg(unavailable_persons, TRUE, TRUE, offset_before, offset_after);
923 g_free(unavailable_persons);
924 gtk_image_set_from_stock
925 (GTK_IMAGE(meet->total_avail_img),
926 GTK_STOCK_DIALOG_WARNING,
927 GTK_ICON_SIZE_SMALL_TOOLBAR);
928 gtk_widget_show(meet->total_avail_img);
929 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Not everyone is available. "
930 "See tooltips for more info..."));
931 CLAWS_SET_TIP(meet->total_avail_evtbox, msg);
933 return (val == G_ALERTALTERNATE);
936 static gboolean check_attendees_availability(VCalMeeting *meet, gboolean tell_if_ok, gboolean for_send)
940 gchar *real_url = NULL;
942 gchar *change_user = NULL, *change_dom = NULL;
943 gchar *dtstart = NULL;
945 gboolean find_avail = FALSE;
946 gboolean res = TRUE, uncertain = FALSE;
947 gchar *organizer = NULL;
948 VCalAttendee *dummy_org = NULL;
949 gchar *internal_ifb = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
950 "vcalendar", G_DIR_SEPARATOR_S,
951 "internal.ifb", NULL);
952 gboolean local_only = FALSE;
956 if (vcalprefs.freebusy_get_url == NULL
957 || *vcalprefs.freebusy_get_url == '\0') {
960 real_url = g_strdup(vcalprefs.freebusy_get_url);
963 while (strchr(tmp, '%')) {
964 tmp = strchr(tmp, '%')+1;
967 if (num_format > 2) {
968 g_warning("wrong format in %s!", real_url);
974 if (strstr(real_url, "%u") != NULL) {
975 change_user = strstr(real_url, "%u");
976 *(strstr(real_url, "%u")+1) = 's';
978 if (strstr(real_url, "%d") != NULL) {
979 change_dom = strstr(real_url, "%d");
980 *(strstr(real_url, "%d")+1) = 's';
982 debug_print("url format %s\n", real_url);
984 dtstart = get_date(meet, TRUE);
985 dtend = get_date(meet, FALSE);
987 /* hack to check our own avail. */
988 organizer = get_organizer(meet);
989 dummy_org = g_new0(VCalAttendee, 1);
990 dummy_org->address = gtk_entry_new();
991 dummy_org->avail_img = meet->avail_img;
992 dummy_org->avail_evtbox = meet->avail_evtbox;
993 dummy_org->org = TRUE;
994 gtk_entry_set_text(GTK_ENTRY(dummy_org->address), organizer);
996 dummy_org->cached_contents = file_read_to_str(internal_ifb);
997 g_free(internal_ifb);
1000 meet->attendees = g_slist_prepend(meet->attendees, dummy_org);
1001 attlist = meet->attendees;
1003 attlist = g_slist_prepend(NULL, dummy_org);
1006 gtk_widget_set_sensitive(meet->save_btn, FALSE);
1007 gtk_widget_set_sensitive(meet->avail_btn, FALSE);
1009 gdkwin = gtk_widget_get_window(meet->window);
1011 gdk_window_set_cursor(gdkwin, watch_cursor);
1013 for (cur = attlist; cur && cur->data; cur = cur->next) {
1014 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1015 gchar *email = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
1016 gchar *remail, *user, *domain;
1017 gchar *contents = NULL;
1019 if (*email == '\0') {
1021 att_update_icon(meet, attendee, 0, NULL);
1026 remail = g_strdup(email);
1028 extract_address(remail);
1029 if (strrchr(remail, ' '))
1030 user = g_strdup(strrchr(remail, ' ')+1);
1032 user = g_strdup(remail);
1033 if (strchr(user, '@')) {
1034 domain = g_strdup(strchr(user, '@')+1);
1035 *(strchr(user, '@')) = '\0';
1037 domain = g_strdup("");
1040 if (change_user && change_dom) {
1041 if (change_user < change_dom)
1042 tmp = g_strdup_printf(real_url, user, domain);
1044 tmp = g_strdup_printf(real_url, domain, user);
1045 } else if (change_user) {
1046 tmp = g_strdup_printf(real_url, user);
1047 } else if (change_dom) {
1048 tmp = g_strdup_printf(real_url, domain);
1050 tmp = g_strdup(real_url);
1054 debug_print("url to get %s\n", tmp);
1057 if (attendee->cached_contents != NULL) {
1058 contents = attendee->cached_contents;
1059 attendee->cached_contents = NULL;
1060 } else if (!local_only) {
1061 if (strncmp(tmp, "http://", 7)
1062 && strncmp(tmp, "https://", 8)
1063 && strncmp(tmp, "webcal://", 9)
1064 && strncmp(tmp, "webcals://", 10)
1065 && strncmp(tmp, "ftp://", 6))
1066 contents = file_read_to_str(tmp);
1068 gchar *label = g_strdup_printf(_("Fetching planning for %s..."), email);
1069 if (!strncmp(tmp, "webcal", 6)) {
1070 gchar *tmp2 = g_strdup_printf("http%s", tmp+6);
1074 contents = vcal_curl_read(tmp, label, FALSE, NULL);
1084 if (contents == NULL) {
1086 att_update_icon(meet, attendee, 2, _("Free/busy retrieval failed"));
1090 if (!attendee_available(attendee, dtstart, dtend, contents)) {
1092 debug_print("not available!\n");
1094 debug_print("available!\n");
1095 att_update_icon(meet, attendee, 1, _("Available"));
1097 attendee->cached_contents = contents;
1103 res = find_availability((dtstart), (dtend), attlist, for_send, meet);
1108 alertpanel_notice(_("Everyone is available."));
1109 else if (!uncertain) {
1110 gtk_image_set_from_stock
1111 (GTK_IMAGE(meet->total_avail_img),
1112 GTK_STOCK_DIALOG_INFO,
1113 GTK_ICON_SIZE_SMALL_TOOLBAR);
1114 gtk_widget_show(meet->total_avail_img);
1115 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Everyone is available."));
1116 CLAWS_SET_TIP(meet->total_avail_evtbox, NULL);
1118 gtk_image_set_from_stock
1119 (GTK_IMAGE(meet->total_avail_img),
1120 GTK_STOCK_DIALOG_QUESTION,
1121 GTK_ICON_SIZE_SMALL_TOOLBAR);
1122 gtk_widget_show(meet->total_avail_img);
1123 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Everyone is available."));
1124 CLAWS_SET_TIP(meet->total_avail_evtbox, _("Everyone seems available, but some free/busy information failed to be retrieved."));
1129 for (cur = attlist; cur && cur->data; cur = cur->next) {
1130 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1131 g_free(attendee->cached_contents);
1132 attendee->cached_contents = NULL;
1134 gtk_widget_set_sensitive(meet->save_btn, TRUE);
1135 gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1138 gdk_window_set_cursor(gdkwin, NULL);
1141 meet->attendees = g_slist_remove(meet->attendees, dummy_org);
1143 g_slist_free(attlist);
1144 gtk_widget_destroy(dummy_org->address);
1155 static gboolean check_avail_cb(GtkButton *widget, gpointer data)
1157 VCalMeeting *meet = (VCalMeeting *)data;
1158 check_attendees_availability(meet, TRUE, FALSE);
1162 static gboolean send_meeting_cb(GtkButton *widget, gpointer data)
1164 VCalMeeting *meet = (VCalMeeting *)data;
1166 gchar *organizer = NULL;
1167 gchar *organizer_name = NULL;
1168 gchar *dtstart = NULL;
1169 gchar *dtend = NULL;
1171 gchar *location = NULL;
1172 gchar *summary = NULL;
1173 gchar *description = NULL;
1174 VCalEvent *event = NULL;
1176 PrefsAccount *account = NULL;
1177 gboolean res = FALSE;
1178 gboolean found_att = FALSE;
1179 Folder *folder = folder_find_from_name (PLUGIN_NAME, vcal_folder_get_class());
1180 gboolean redisp = FALSE;
1183 if (meet->uid == NULL && meet->visible &&
1184 !check_attendees_availability(meet, FALSE, TRUE)) {
1189 MainWindow *mainwin = mainwindow_get_mainwindow();
1190 if (mainwin->summaryview->folder_item == folder->inbox) {
1192 summary_show(mainwin->summaryview, NULL);
1195 gtk_widget_set_sensitive(meet->save_btn, FALSE);
1196 gtk_widget_set_sensitive(meet->avail_btn, FALSE);
1198 gdkwin = gtk_widget_get_window(meet->window);
1200 gdk_window_set_cursor(gdkwin, watch_cursor);
1202 organizer = get_organizer(meet);
1203 account = account_find_from_address(organizer, FALSE);
1205 if(account == NULL) {
1206 debug_print("can't get account from address %s\n", organizer);
1211 organizer_name = get_organizer_name(meet);
1214 uid = g_strdup(meet->uid);
1216 uid = prefs_account_generate_msgid(account);
1219 dtstart = get_date(meet, TRUE);
1220 dtend = get_date(meet, FALSE);
1221 location = get_location(meet);
1222 summary = get_summary(meet);
1223 description = get_description(meet);
1225 event = vcal_manager_new_event(uid, organizer, organizer_name, location, summary, description,
1226 dtstart, dtend, NULL, tzid, NULL, meet->method,
1228 ICAL_VEVENT_COMPONENT);
1230 vcal_manager_update_answer(event, organizer, organizer_name,
1231 ICAL_PARTSTAT_ACCEPTED,
1232 ICAL_CUTYPE_INDIVIDUAL);
1234 for (cur = meet->attendees; cur && cur->data; cur = cur->next) {
1235 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1236 gchar *email = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
1238 gchar *orig_email = email;
1240 enum icalparameter_cutype cutype = ICAL_CUTYPE_INDIVIDUAL;
1241 enum icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION;
1243 index = gtk_combo_box_get_active(GTK_COMBO_BOX(attendee->cutype));
1245 cutype = ICAL_CUTYPE_INDIVIDUAL + index;
1246 if (attendee->status) {
1247 if(!strcmp(attendee->status, "accepted"))
1248 status = ICAL_PARTSTAT_ACCEPTED;
1249 if(!strcmp(attendee->status, "tentatively accepted"))
1250 status = ICAL_PARTSTAT_TENTATIVE;
1251 if(!strcmp(attendee->status, "declined"))
1252 status = ICAL_PARTSTAT_DECLINED;
1253 g_free(attendee->status);
1255 if (strlen(email)) {
1256 if (strstr(email, " <")) {
1258 email = strstr(email," <") + 2;
1259 *(strstr(name," <")) = '\0';
1260 if (strstr(email, ">"))
1261 *(strstr(email, ">")) = '\0';
1264 vcal_manager_update_answer(event, email, name,
1267 found_att = strcmp(email, organizer);
1273 res = vcal_manager_request(account, event);
1278 g_free(organizer_name);
1281 g_free(description);
1284 vcal_manager_free_event(event);
1286 gtk_widget_set_sensitive(meet->save_btn, TRUE);
1287 gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1289 gdk_window_set_cursor(gdkwin, NULL);
1294 alertpanel_error(_("Could not send the meeting invitation.\n"
1295 "Check the recipients."));
1299 folder_item_scan(folder->inbox);
1301 if (folder && redisp) {
1302 MainWindow *mainwin = mainwindow_get_mainwindow();
1303 summary_show(mainwin->summaryview, folder->inbox);
1309 static VCalMeeting *vcal_meeting_create_real(VCalEvent *event, gboolean visible)
1311 VCalMeeting *meet = g_new0(VCalMeeting, 1);
1312 GtkTextBuffer *buffer = NULL;
1313 GtkWidget *date_hbox, *date_vbox, *save_hbox, *label, *hbox;
1316 GtkWidget *scrolledwin;
1319 GtkWidget *notebook;
1320 GtkWidget *maemo_vbox0;
1324 watch_cursor = gdk_cursor_new(GDK_WATCH);
1326 meet->visible = visible;
1328 meet->window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "vcal_meeting_gtk");
1329 #ifndef GENERIC_UMPC
1330 meet->table = gtk_table_new(7, 2, FALSE);
1332 meet->table1 = gtk_table_new(4, 2, FALSE);
1333 meet->table2 = gtk_table_new(2, 2, FALSE);
1335 meet->who = gtk_combo_box_text_new();
1337 meet->start_c = gtk_calendar_new();
1338 meet->end_c = gtk_calendar_new();
1340 meet->avail_evtbox = gtk_event_box_new();
1341 meet->avail_img = gtk_image_new_from_stock
1342 (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
1344 meet->start_time = gtkut_time_select_combo_new();
1346 meet->end_time = gtkut_time_select_combo_new();
1348 meet->location = gtk_entry_new();
1349 meet->summary = gtk_entry_new();
1350 meet->description = gtk_text_view_new();
1351 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
1352 gtk_text_view_set_editable(GTK_TEXT_VIEW(meet->description), TRUE);
1353 gtk_text_buffer_add_selection_clipboard(buffer, gtk_clipboard_get(GDK_SELECTION_PRIMARY));
1355 scrolledwin = gtk_scrolled_window_new(NULL, NULL);
1356 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
1357 GTK_POLICY_AUTOMATIC,
1358 GTK_POLICY_AUTOMATIC);
1359 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
1361 gtk_container_add(GTK_CONTAINER(scrolledwin), meet->description);
1364 meet->uid = g_strdup(event->uid);
1365 meet->sequence = event->sequence + 1;
1366 meet->method = (event->method == ICAL_METHOD_CANCEL ?
1367 ICAL_METHOD_CANCEL:ICAL_METHOD_REQUEST);
1369 gtk_entry_set_text(GTK_ENTRY(meet->location), event->location);
1370 gtk_entry_set_text(GTK_ENTRY(meet->summary), event->summary);
1371 gtk_text_buffer_set_text(buffer, event->description, -1);
1373 meet->method = ICAL_METHOD_REQUEST;
1375 meet->save_btn = gtk_button_new_with_label(_("Save & Send"));
1376 meet->avail_btn = gtk_button_new_with_label(_("Check availability"));
1378 meet->total_avail_evtbox = gtk_event_box_new();
1379 meet->total_avail_img = gtk_image_new_from_stock
1380 (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
1381 meet->total_avail_msg = gtk_label_new("");
1383 gtk_widget_set_size_request(meet->total_avail_evtbox, 18, 16);
1384 gtk_event_box_set_visible_window(GTK_EVENT_BOX(meet->total_avail_evtbox), FALSE);
1385 gtk_container_add (GTK_CONTAINER(meet->total_avail_evtbox), meet->total_avail_img);
1387 g_signal_connect(G_OBJECT(meet->save_btn), "clicked",
1388 G_CALLBACK(send_meeting_cb), meet);
1390 g_signal_connect(G_OBJECT(meet->avail_btn), "clicked",
1391 G_CALLBACK(check_avail_cb), meet);
1393 g_signal_connect(G_OBJECT(meet->window), "destroy",
1394 G_CALLBACK(destroy_meeting_cb), meet);
1395 g_signal_connect(G_OBJECT(meet->window), "key_press_event",
1396 G_CALLBACK(meeting_key_pressed), meet);
1399 gtk_widget_set_size_request(meet->description, -1, 100);
1400 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(meet->description), GTK_WRAP_WORD);
1402 if (!event || (event && !event->dtstart && !event->dtend)) {
1403 time_t t = time (NULL)+ 3600;
1404 struct tm buft1, buft2;
1405 struct tm *lt = localtime_r (&t, &buft1);
1407 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1409 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1410 lt->tm_mon, lt->tm_year + 1900);
1412 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->start_time), lt->tm_hour, 0);
1415 lt = localtime_r(&t, &buft2);
1417 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1419 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1420 lt->tm_mon, lt->tm_year + 1900);
1422 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time), lt->tm_hour, 0);
1424 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1425 get_dtdate(event->dtstart, DAY));
1426 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1427 get_dtdate(event->dtend, DAY));
1429 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1430 get_dtdate(event->dtstart, MONTH)-1,
1431 get_dtdate(event->dtstart, YEAR));
1432 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1433 get_dtdate(event->dtend, MONTH)-1,
1434 get_dtdate(event->dtend, YEAR));
1436 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->start_time),
1437 get_dtdate(event->dtstart, HOUR),
1438 get_dtdate(event->dtstart, MINUTE));
1440 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time),
1441 get_dtdate(event->dtend, HOUR),
1442 get_dtdate(event->dtend, MINUTE));
1445 g_signal_connect(G_OBJECT(meet->start_c), "day-selected",
1446 G_CALLBACK(meeting_start_changed), meet);
1447 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(meet->start_time))),
1449 G_CALLBACK(meeting_start_changed),
1452 g_signal_connect(G_OBJECT(meet->end_c), "day-selected",
1453 G_CALLBACK(meeting_end_changed), meet);
1454 g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(meet->end_time))),
1456 G_CALLBACK(meeting_end_changed),
1459 #ifndef GENERIC_UMPC
1460 gtk_widget_set_size_request(meet->start_time, 80, -1);
1461 gtk_widget_set_size_request(meet->end_time, 80, -1);
1463 gtk_widget_set_size_request(meet->start_time, 120, -1);
1464 gtk_widget_set_size_request(meet->end_time, 120, -1);
1467 date_hbox = gtk_hbox_new(FALSE, 6);
1468 date_vbox = gtk_vbox_new(FALSE, 6);
1469 hbox = gtk_hbox_new(FALSE, 6);
1470 label = gtk_label_new(g_strconcat("<b>",_("Starts at:"),"</b> ",NULL));
1471 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1472 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1474 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1475 gtk_box_pack_start(GTK_BOX(hbox), meet->start_time, FALSE, FALSE, 0);
1476 label = gtk_label_new(g_strconcat("<b> ",_("on:"),"</b>",NULL));
1477 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1478 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1479 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1480 gtk_box_pack_start(GTK_BOX(date_vbox), hbox, FALSE, FALSE, 0);
1481 gtk_box_pack_start(GTK_BOX(date_vbox), meet->start_c, FALSE, FALSE, 0);
1482 gtk_box_pack_start(GTK_BOX(date_hbox), date_vbox, FALSE, FALSE, 0);
1484 #ifndef GENERIC_UMPC
1485 label = gtk_label_new(" ");
1487 label = gtk_label_new("");
1489 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1490 gtk_box_pack_start(GTK_BOX(date_hbox), label, TRUE, TRUE, 0);
1492 date_vbox = gtk_vbox_new(FALSE, 6);
1493 hbox = gtk_hbox_new(FALSE, 6);
1494 label = gtk_label_new(g_strconcat("<b>",_("Ends at:"),"</b> ", NULL));
1495 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1496 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1498 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1499 gtk_box_pack_start(GTK_BOX(hbox), meet->end_time, FALSE, FALSE, 0);
1500 label = gtk_label_new(g_strconcat("<b> ",_("on:"),"</b>",NULL));
1501 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1502 gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1503 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1504 gtk_box_pack_start(GTK_BOX(date_vbox), hbox, FALSE, FALSE, 0);
1505 gtk_box_pack_start(GTK_BOX(date_vbox), meet->end_c, FALSE, FALSE, 0);
1506 gtk_box_pack_start(GTK_BOX(date_hbox), date_vbox, FALSE, FALSE, 0);
1508 meet->attendees_vbox = gtk_vbox_new(FALSE, 6);
1509 gtk_widget_show_all(meet->attendees_vbox);
1511 attendee_add(meet, NULL, NULL, NULL, NULL, TRUE);
1513 gboolean firstadd = TRUE;
1514 GSList *list = vcal_manager_get_answers_emails(event);
1515 while (list && list->data) {
1516 gchar *address = (gchar *)list->data;
1517 gchar *name = vcal_manager_get_attendee_name(event, address);
1518 gchar *answer = vcal_manager_get_reply_text_for_attendee(event, address);
1519 gchar *type = vcal_manager_get_cutype_text_for_attendee(event, address);
1520 if (strcmp(event->organizer, address)) {
1521 attendee_add(meet, address, name, answer, type, firstadd);
1530 if (firstadd == TRUE)
1531 attendee_add(meet, NULL, NULL, NULL, NULL, TRUE);
1535 gtk_window_set_title(GTK_WINDOW(meet->window), _("New meeting"));
1537 gchar *title = g_strdup_printf(_("%s - Edit meeting"),
1539 gtk_window_set_title(GTK_WINDOW(meet->window), title);
1542 address_completion_start(meet->window);
1544 accounts = account_get_list();
1545 g_return_val_if_fail(accounts != NULL, NULL);
1547 for (i = 0; accounts != NULL; accounts = accounts->next) {
1548 PrefsAccount *ac = (PrefsAccount *)accounts->data;
1550 if (ac->protocol == A_NNTP) {
1553 if (!event && ac == account_get_cur_account()) {
1556 else if (event && !strcmp(ac->address, event->organizer))
1559 meet->avail_accounts = g_slist_append(meet->avail_accounts, ac);
1562 s = g_strdup_printf("%s: %s <%s>",
1564 ac->name, ac->address);
1566 s = g_strdup_printf("%s: %s",
1567 ac->account_name, ac->address);
1569 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(meet->who), s);
1573 gtk_combo_box_set_active(GTK_COMBO_BOX(meet->who), num);
1575 save_hbox = gtk_hbox_new(FALSE, 6);
1576 gtk_box_pack_start(GTK_BOX(save_hbox), meet->save_btn, FALSE, FALSE, 0);
1577 gtk_box_pack_start(GTK_BOX(save_hbox), meet->avail_btn, FALSE, FALSE, 0);
1578 gtk_box_pack_start(GTK_BOX(save_hbox), meet->total_avail_evtbox, FALSE, FALSE, 0);
1579 gtk_box_pack_start(GTK_BOX(save_hbox), meet->total_avail_msg, FALSE, FALSE, 0);
1581 hbox = gtk_hbox_new(FALSE, 6);
1582 gtk_box_pack_start(GTK_BOX(hbox), meet->avail_evtbox, FALSE, FALSE, 0);
1583 gtk_box_pack_start(GTK_BOX(hbox), meet->who, TRUE, TRUE, 0);
1585 gtk_widget_set_size_request(meet->avail_evtbox, 18, 16);
1586 gtk_event_box_set_visible_window(GTK_EVENT_BOX(meet->avail_evtbox), FALSE);
1587 gtk_container_add (GTK_CONTAINER(meet->avail_evtbox), meet->avail_img);
1589 #ifndef GENERIC_UMPC
1590 TABLE_ADD_LINE(_("Organizer:"), hbox, FALSE);
1591 TABLE_ADD_LINE(_("Summary:"), meet->summary, TRUE);
1592 TABLE_ADD_LINE(_("Time:"), date_hbox, TRUE);
1593 TABLE_ADD_LINE(_("Location:"), meet->location, TRUE);
1594 TABLE_ADD_LINE(_("Description:"), scrolledwin, TRUE);
1595 TABLE_ADD_LINE(_("Attendees:"), meet->attendees_vbox, FALSE);
1596 TABLE_ADD_LINE("", save_hbox, TRUE);
1598 gtk_widget_set_size_request(meet->window, -1, -1);
1599 gtk_container_add(GTK_CONTAINER(meet->window), meet->table);
1601 TABLE_ADD_LINE(_("Organizer:"), hbox, FALSE, TRUE);
1602 TABLE_ADD_LINE(_("Summary:"), meet->summary, TRUE, TRUE);
1603 TABLE_ADD_LINE(_("Location:"), meet->location, FALSE, TRUE);
1604 TABLE_ADD_LINE(_("Description:"), scrolledwin, TRUE, TRUE);
1605 TABLE_ADD_LINE(_("Attendees:"), meet->attendees_vbox, FALSE, TRUE);
1606 TABLE_ADD_LINE("", date_hbox, TRUE, FALSE);
1608 notebook = gtk_notebook_new ();
1609 gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
1610 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
1612 gtk_label_new_with_mnemonic(_("Event:")));
1614 gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
1616 gtk_label_new_with_mnemonic(_("Time:")));
1617 gtk_widget_show (notebook);
1619 maemo_vbox0 = gtk_vbox_new(FALSE, 3);
1620 gtk_box_pack_start(GTK_BOX(maemo_vbox0), notebook, TRUE, TRUE, 0);
1621 gtk_box_pack_start(GTK_BOX(maemo_vbox0), save_hbox, FALSE, FALSE, 0);
1623 gtk_widget_set_size_request(meet->window, -1, -1);
1624 gtk_container_add (GTK_CONTAINER (meet->window), maemo_vbox0);
1626 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(meet->window));
1630 gtk_widget_show_all(meet->window);
1631 for (cur = meet->attendees; cur; cur = cur->next) {
1632 gtk_widget_hide(((VCalAttendee *)cur->data)->avail_img);
1634 gtk_widget_hide(meet->avail_img);
1635 gtk_widget_hide(meet->total_avail_img);
1636 gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1641 VCalMeeting *vcal_meeting_create(VCalEvent *event)
1643 return vcal_meeting_create_real(event, TRUE);
1646 VCalMeeting *vcal_meeting_create_with_start(VCalEvent *event, struct tm *sdate)
1648 VCalMeeting *meet = vcal_meeting_create(event);
1650 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1652 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1655 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1656 sdate->tm_mon, sdate->tm_year+1900);
1657 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1658 sdate->tm_mon, sdate->tm_year+1900);
1660 if (sdate->tm_hour != 0) {
1661 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->start_time), sdate->tm_hour, 0);
1663 if (sdate->tm_hour < 23) {
1664 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time), sdate->tm_hour+1, 0);
1666 struct tm tm_tomorrow;
1668 tm_tomorrow.tm_mday = sdate->tm_mday;
1669 tm_tomorrow.tm_mon = sdate->tm_mon;
1670 tm_tomorrow.tm_wday = sdate->tm_wday;
1671 tm_tomorrow.tm_year = sdate->tm_year+1900;
1672 tm_tomorrow.tm_hour = sdate->tm_hour;
1673 orage_move_day(&tm_tomorrow, +1);
1674 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1675 tm_tomorrow.tm_mday);
1676 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1677 tm_tomorrow.tm_mon, tm_tomorrow.tm_year);
1679 gtkut_time_select_select_by_time(GTK_COMBO_BOX(meet->end_time), 0, 0);
1685 VCalMeeting *vcal_meeting_create_hidden(VCalEvent *event)
1687 return vcal_meeting_create_real(event, FALSE);
1690 gboolean vcal_meeting_send(VCalMeeting *meet)
1692 return send_meeting_cb(NULL, meet);
1695 gboolean vcal_meeting_alert_check(gpointer data)
1697 GSList *events = NULL, *cur = NULL;
1699 if (!vcalprefs.alert_enable)
1702 events = vcal_folder_get_waiting_events();
1704 for (cur = events; cur; cur = cur->next) {
1705 VCalEvent *event = (VCalEvent *)cur->data;
1706 time_t start, end, current;
1707 gboolean warn = FALSE;
1711 start = icaltime_as_timet(icaltime_from_string(event->dtstart));
1712 end = icaltime_as_timet(icaltime_from_string(event->dtend));
1713 current = time(NULL);
1715 if (start - current <= (vcalprefs.alert_delay*60)
1716 && start - current + 60 > (vcalprefs.alert_delay*60)) {
1718 } else if (event->postponed - current <= (vcalprefs.alert_delay*60)
1719 && event->postponed - current + 60 > (vcalprefs.alert_delay*60)) {
1723 time_t tmpt = icaltime_as_timet((icaltime_from_string(event->dtstart)));
1724 gchar *estart = NULL;
1726 int length = (end - start) / 60;
1727 gchar *duration = NULL, *hours = NULL, *minutes = NULL;
1728 gchar *message = NULL;
1729 gchar *title = NULL;
1730 gchar *label = NULL;
1731 int postpone_min = 0;
1735 estart = g_strdup(ctime(&tmpt));
1738 hours = g_strdup_printf(ngettext("%d hour", "%d hours",
1739 (length/60) > 1 ? 2 : 1), length/60);
1741 minutes = g_strdup_printf(ngettext("%d minute", "%d minutes",
1742 length%60), length%60);
1744 duration = g_strdup_printf("%s%s%s",
1746 hours && minutes ? " ":"",
1747 minutes?minutes:"");
1752 title = g_strdup_printf(_("Upcoming event: %s"), event->summary);
1753 message = g_strdup_printf(_("You have a meeting or event soon.\n"
1754 "It starts at %s and ends %s later.\n"
1756 "More information:\n\n"
1760 event->location?event->location:"",
1761 event->description);
1766 postpone_min = (vcalprefs.alert_delay/2 > 15) ? 15: (vcalprefs.alert_delay/2);
1767 if (postpone_min == 0)
1770 label = g_strdup_printf(ngettext("Remind me in %d minute", "Remind me in %d minutes",
1771 postpone_min > 1 ? 2:1),
1773 aval = alertpanel_full(title, message,
1774 label, GTK_STOCK_OK, NULL, ALERTFOCUS_FIRST, FALSE,
1775 NULL, ALERT_NOTICE);
1781 if (aval == G_ALERTDEFAULT) {
1782 if (event->postponed == 0)
1783 event->postponed = start + (postpone_min*60);
1785 event->postponed += (postpone_min*60);
1787 event->postponed = (time_t)0;
1789 vcal_manager_save_event(event, FALSE);
1792 vcal_manager_free_event((VCalEvent *)cur->data);
1795 g_slist_free(events);
1800 void multisync_export(void)
1802 GSList *list = NULL;
1803 gchar *path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1804 "vcalendar", G_DIR_SEPARATOR_S,
1806 GSList *files = NULL;
1811 icalcomponent *calendar = NULL;
1814 if (is_dir_exist(path) && remove_dir_recursive(path) < 0) {
1818 if (make_dir(path) != 0) {
1823 list = vcal_folder_get_waiting_events();
1824 for (cur = list; cur; cur = cur->next) {
1825 VCalEvent *event = (VCalEvent *)cur->data;
1826 file = g_strdup_printf("multisync%lld-%d",
1827 (long long)time(NULL), i);
1832 icalcomponent_vanew(
1833 ICAL_VCALENDAR_COMPONENT,
1834 icalproperty_new_version("2.0"),
1835 icalproperty_new_prodid(
1836 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
1837 icalproperty_new_calscale("GREGORIAN"),
1840 vcal_manager_event_dump(event, FALSE, FALSE, calendar, FALSE);
1841 tmp = g_strconcat(path, G_DIR_SEPARATOR_S, file, NULL);
1842 str_write_to_file(icalcomponent_as_ical_string(calendar), tmp);
1844 files = g_slist_append(files, file);
1845 vcal_manager_free_event(event);
1846 icalcomponent_free(calendar);
1851 file = g_strconcat(path, G_DIR_SEPARATOR_S, "backup_entries", NULL);
1852 fp = g_fopen(file, "wb");
1855 for (cur = files; cur; cur = cur->next) {
1856 file = (char *)cur->data;
1857 if (fprintf(fp, "1 1 %s\n", file) < 0)
1858 FILE_OP_ERROR(file, "fprintf");
1861 if (fclose(fp) == EOF)
1862 FILE_OP_ERROR(file, "fclose");
1864 FILE_OP_ERROR(file, "fopen");
1867 g_slist_free(files);
1870 gboolean vcal_meeting_export_calendar(const gchar *path,
1871 const gchar *user, const gchar *pass,
1874 GSList *list = vcal_folder_get_waiting_events();
1875 GSList *subs = NULL;
1877 icalcomponent *calendar = NULL;
1879 gchar *tmpfile = get_tmp_file();
1880 gchar *internal_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1881 "vcalendar", G_DIR_SEPARATOR_S,
1882 "internal.ics", NULL);
1884 gboolean res = TRUE;
1889 if (vcalprefs.export_subs && vcalprefs.export_enable)
1890 subs = vcal_folder_get_webcal_events();
1892 if (g_slist_length(list) == 0 && g_slist_length(subs) == 0) {
1896 alertpanel_full(_("Empty calendar"),
1897 _("There is nothing to export."),
1898 GTK_STOCK_OK, NULL, NULL, ALERTFOCUS_FIRST, FALSE,
1899 NULL, ALERT_NOTICE);
1902 str_write_to_file("", tmpfile);
1908 icalcomponent_vanew(
1909 ICAL_VCALENDAR_COMPONENT,
1910 icalproperty_new_version("2.0"),
1911 icalproperty_new_prodid(
1912 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
1913 icalproperty_new_calscale("GREGORIAN"),
1917 for (cur = list; cur; cur = cur->next) {
1918 VCalEvent *event = (VCalEvent *)cur->data;
1919 vcal_manager_event_dump(event, FALSE, FALSE, calendar, FALSE);
1920 vcal_manager_free_event(event);
1923 if (str_write_to_file(icalcomponent_as_ical_string(calendar), internal_file) < 0) {
1924 g_warning("can't export internal cal");
1927 g_free(internal_file);
1929 for (cur = subs; cur; cur = cur->next) {
1930 /* Not to be freed */
1931 icalcomponent *event = (icalcomponent *)cur->data;
1932 vcal_manager_icalevent_dump(event, NULL, calendar);
1935 if (vcalprefs.export_enable || path == NULL) {
1936 if (str_write_to_file(icalcomponent_as_ical_string(calendar), tmpfile) < 0) {
1937 alertpanel_error(_("Could not export the calendar."));
1939 icalcomponent_free(calendar);
1944 filesize = strlen(icalcomponent_as_ical_string(calendar));
1947 icalcomponent_free(calendar);
1953 if (!path && !automatic)
1954 file = filesel_select_file_save(_("Export calendar to ICS"), NULL);
1956 file = g_strdup(path);
1958 if (automatic && (!path || strlen(path) == 0 || !vcalprefs.export_enable)) {
1965 && strncmp(file, "http://", 7)
1966 && strncmp(file, "https://", 8)
1967 && strncmp(file, "webcal://", 9)
1968 && strncmp(file, "webcals://", 10)
1969 && strncmp(file, "ftp://", 6)) {
1970 gchar *afile = NULL;
1971 if (file[0] != G_DIR_SEPARATOR)
1972 afile=g_strdup_printf("%s%s%s", get_home_dir(),
1973 G_DIR_SEPARATOR_S, file);
1975 afile=g_strdup(file);
1976 if (move_file(tmpfile, afile, TRUE) != 0) {
1977 log_error(LOG_PROTOCOL, _("Couldn't export calendar to '%s'\n"),
1984 FILE *fp = g_fopen(tmpfile, "rb");
1985 if (!strncmp(file, "webcal", 6)) {
1986 gchar *tmp = g_strdup_printf("http%s", file+6);
1991 res = vcal_curl_put(file, fp, filesize, user, (pass != NULL ? pass : ""));
2000 gboolean vcal_meeting_export_freebusy(const gchar *path, const gchar *user,
2003 GSList *list = vcal_folder_get_waiting_events();
2005 icalcomponent *calendar = NULL, *timezone = NULL, *tzc = NULL, *vfreebusy = NULL;
2007 gchar *tmpfile = get_tmp_file();
2008 gchar *internal_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2009 "vcalendar", G_DIR_SEPARATOR_S,
2010 "internal.ifb", NULL);
2011 time_t whole_start = time(NULL);
2012 time_t whole_end = whole_start + (60*60*24*365);
2013 gboolean res = TRUE;
2014 struct icaltimetype itt_start, itt_end;
2020 icalcomponent_vanew(
2021 ICAL_VCALENDAR_COMPONENT,
2022 icalproperty_new_version("2.0"),
2023 icalproperty_new_prodid(
2024 "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
2025 icalproperty_new_calscale("GREGORIAN"),
2029 timezone = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
2031 icalcomponent_add_property(timezone,
2032 icalproperty_new_tzid("UTC"));
2034 tzc = icalcomponent_new(ICAL_XSTANDARD_COMPONENT);
2035 icalcomponent_add_property(tzc,
2036 icalproperty_new_dtstart(
2037 icaltime_from_string("19700101T000000")));
2038 icalcomponent_add_property(tzc,
2039 icalproperty_new_tzoffsetfrom(0.0));
2040 icalcomponent_add_property(tzc,
2041 icalproperty_new_tzoffsetto(0.0));
2042 icalcomponent_add_property(tzc,
2043 icalproperty_new_tzname("Greenwich meridian time"));
2045 icalcomponent_add_component(timezone, tzc);
2047 icalcomponent_add_component(calendar, timezone);
2049 itt_start = icaltime_from_timet_with_zone(whole_start, FALSE, NULL);
2050 itt_end = icaltime_from_timet_with_zone(whole_end, FALSE, NULL);
2051 itt_start.second = itt_start.minute = itt_start.hour = 0;
2052 itt_end.second = 59; itt_end.minute = 59; itt_end.hour = 23;
2056 icalcomponent_vanew(
2057 ICAL_VFREEBUSY_COMPONENT,
2058 icalproperty_vanew_dtstart(itt_start, 0),
2059 icalproperty_vanew_dtend(itt_end, 0),
2063 debug_print("DTSTART:%s\nDTEND:%s\n",
2064 icaltime_as_ical_string(itt_start),
2065 icaltime_as_ical_string(itt_end));
2067 for (cur = list; cur; cur = cur->next) {
2068 VCalEvent *event = (VCalEvent *)cur->data;
2070 struct icalperiodtype ipt;
2072 ipt.start = icaltime_from_string(event->dtstart);
2073 ipt.end = icaltime_from_string(event->dtend);
2074 ipt.duration = icaltime_subtract(ipt.end, ipt.start);
2075 if (icaltime_as_timet(ipt.start) <= icaltime_as_timet(itt_end)
2076 && icaltime_as_timet(ipt.end) >= icaltime_as_timet(itt_start)) {
2077 prop = icalproperty_new_freebusy(ipt);
2078 icalcomponent_add_property(vfreebusy, prop);
2080 vcal_manager_free_event(event);
2083 icalcomponent_add_component(calendar, vfreebusy);
2085 if (str_write_to_file(icalcomponent_as_ical_string(calendar), internal_file) < 0) {
2086 g_warning("can't export freebusy");
2089 g_free(internal_file);
2091 if (vcalprefs.export_freebusy_enable) {
2092 if (str_write_to_file(icalcomponent_as_ical_string(calendar), tmpfile) < 0) {
2093 alertpanel_error(_("Could not export the freebusy info."));
2095 icalcomponent_free(calendar);
2099 filesize = strlen(icalcomponent_as_ical_string(calendar));
2102 icalcomponent_free(calendar);
2105 if ((!path || strlen(path) == 0 || !vcalprefs.export_freebusy_enable)) {
2109 file = g_strdup(path);
2113 && strncmp(file, "http://", 7)
2114 && strncmp(file, "https://", 8)
2115 && strncmp(file, "webcal://", 9)
2116 && strncmp(file, "webcals://", 10)
2117 && strncmp(file, "ftp://", 6)) {
2118 gchar *afile = NULL;
2119 if (file[0] != G_DIR_SEPARATOR)
2120 afile=g_strdup_printf("%s%s%s", get_home_dir(),
2121 G_DIR_SEPARATOR_S, file);
2123 afile=g_strdup(file);
2124 if (move_file(tmpfile, file, TRUE) != 0) {
2125 log_error(LOG_PROTOCOL, _("Couldn't export free/busy to '%s'\n"),
2132 FILE *fp = g_fopen(tmpfile, "rb");
2133 if (!strncmp(file, "webcal", 6)) {
2134 gchar *tmp = g_strdup_printf("http%s", file+6);
2139 res = vcal_curl_put(file, fp, filesize, user, (pass != NULL ? pass : ""));