Understand webcals:// as https://.
[claws.git] / src / plugins / vcalendar / vcal_meeting_gtk.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2013 Colin Leroy <colin@colino.net> and 
4  * the Claws Mail team
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #include "claws-features.h"
24 #endif
25
26 #include <stddef.h>
27 #include <glib.h>
28 #include <glib/gi18n.h>
29
30 #include "defs.h"
31
32 #ifdef USE_PTHREAD
33 #include <pthread.h>
34 #endif
35 #include <ical.h>
36 #include <gtk/gtk.h>
37 #include <gdk/gdkkeysyms.h>
38 #include <curl/curl.h>
39 #include <curl/curlver.h>
40 #include "combobox.h"
41
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"
50 #include "account.h"
51 #include "filesel.h"
52 #include "alertpanel.h"
53 #include "addr_compl.h"
54 #include "gtkutils.h"
55 #include "log.h"
56 #include "utils.h"
57
58 struct _VCalMeeting
59 {
60         gchar     *uid;
61         gint       sequence;
62         gint       method;
63         GtkWidget *window;
64 #ifndef GENERIC_UMPC
65         GtkWidget *table;
66 #else
67         GtkWidget *table1;
68         GtkWidget *table2;
69 #endif
70         GtkWidget *type;
71         GtkWidget *who;
72         GtkWidget *avail_evtbox;
73         GtkWidget *avail_img;
74         GtkWidget *start_c;
75         GtkWidget *start_time;
76         GtkWidget *end_c;
77         GtkWidget *end_time;
78         GtkWidget *location;
79         GtkWidget *summary;
80         GtkWidget *description;
81         GSList    *attendees;
82         GtkWidget *attendees_vbox;
83         GtkWidget *save_btn;
84         GtkWidget *avail_btn;
85         GSList    *avail_accounts;
86         GtkWidget *total_avail_evtbox;
87         GtkWidget *total_avail_img;
88         GtkWidget *total_avail_msg;
89         PrefsAccount *account;
90         gboolean visible;
91 #if !GTK_CHECK_VERSION(2,12,0)
92         GtkTooltips *tips;
93 #endif
94 };
95
96 struct _VCalAttendee {
97         GtkWidget *address;
98         GtkWidget *remove_btn;
99         GtkWidget *add_btn;
100         GtkWidget *cutype;
101         GtkWidget *hbox;
102         VCalMeeting *meet;
103         gchar *status;
104         GtkWidget *avail_evtbox;
105         GtkWidget *avail_img;
106         gchar *cached_contents;
107         gboolean org;
108 };
109
110 static GdkCursor *watch_cursor = NULL;
111
112 VCalAttendee *attendee_add(VCalMeeting *meet, gchar *address, gchar *name, gchar *partstat, gchar *cutype, gboolean first);
113
114 #ifndef GENERIC_UMPC
115 #define TABLE_ADD_LINE(label_text, widget, do_space) {                          \
116         gchar *tmpstr = g_strdup_printf("<span weight=\"bold\">%s</span>",      \
117                                 label_text?label_text:"");                      \
118         GtkWidget *label = NULL;                                                \
119         GtkWidget *spacer = NULL;                                               \
120         GtkWidget *s_hbox = NULL;                                               \
121         if (do_space) {                                                         \
122                 spacer = gtk_label_new("");                                     \
123                 gtk_widget_set_size_request(spacer, 18, 16);                            \
124                 s_hbox = gtk_hbox_new(FALSE, 6);                                \
125                 gtk_box_pack_start(GTK_BOX(s_hbox), spacer, FALSE, FALSE, 0);   \
126                 gtk_box_pack_start(GTK_BOX(s_hbox), widget, TRUE, TRUE, 0);     \
127         }                                                                       \
128         if (label_text) {                                                       \
129                 label = gtk_label_new(tmpstr);                                  \
130                 g_free(tmpstr);                                                 \
131                 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);             \
132                 gtk_misc_set_alignment (GTK_MISC(label), 1, 0.5);               \
133                 gtk_table_attach (GTK_TABLE (meet->table),                      \
134                                   label, 0, 1, i, i+1,                          \
135                                   GTK_FILL, GTK_FILL, 6, 6);                    \
136                 gtk_table_attach (GTK_TABLE (meet->table),                      \
137                                   do_space?s_hbox:widget, 1, 2, i, i+1,         \
138                                   GTK_FILL|GTK_EXPAND, GTK_FILL, 6, 6);         \
139                 if (GTK_IS_LABEL(widget)) {                                     \
140                         gtk_label_set_use_markup(GTK_LABEL (widget), TRUE);     \
141                         gtk_misc_set_alignment (GTK_MISC(widget),0, 0);         \
142                         gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE);       \
143                 }                                                               \
144         } else {                                                                \
145                 g_free(tmpstr);                                                 \
146                 gtk_table_attach (GTK_TABLE (meet->table),                      \
147                                   do_space?s_hbox:widget, 0, 2, i, i+1,         \
148                                   GTK_FILL|GTK_EXPAND, GTK_FILL, 6, 6);         \
149         }                                                                       \
150         i++;                                                                    \
151 }
152 #else
153 #define TABLE_ADD_LINE(label_text, widget, do_space, intable1) {                        \
154         gchar *tmpstr = g_strdup_printf("<span weight=\"bold\">%s</span>",      \
155                                 label_text?label_text:"");                      \
156         GtkWidget *label = NULL;                                                \
157         GtkWidget *spacer = NULL;                                               \
158         GtkWidget *s_hbox = NULL;                                               \
159         if (do_space) {                                                         \
160                 spacer = gtk_label_new("");                                     \
161                 gtk_widget_set_size_request(spacer, 18, 16);                            \
162                 s_hbox = gtk_hbox_new(FALSE, 6);                                \
163                 gtk_box_pack_start(GTK_BOX(s_hbox), spacer, FALSE, FALSE, 0);   \
164                 gtk_box_pack_start(GTK_BOX(s_hbox), widget, TRUE, TRUE, 0);     \
165         }                                                                       \
166         if (label_text) {                                                       \
167                 label = gtk_label_new(tmpstr);                                  \
168                 g_free(tmpstr);                                                 \
169                 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);             \
170                 gtk_misc_set_alignment (GTK_MISC(label), 1, 0.5);               \
171                 if(intable1)    {                                               \
172                         gtk_table_attach (GTK_TABLE (meet->table1),             \
173                                           label, 0, 1, i, i+1,                  \
174                                           GTK_FILL, GTK_FILL, 1, 1);            \
175                 }                                                               \
176                 else    {                                                       \
177                         gtk_table_attach (GTK_TABLE (meet->table2),             \
178                                           label, 0, 1, i, i+1,                  \
179                                           GTK_FILL, GTK_FILL, 1, 1);            \
180                 }                                                               \
181                 if(intable1)    {                                               \
182                         gtk_table_attach (GTK_TABLE (meet->table1),             \
183                                           do_space?s_hbox:widget, 1, 2, i, i+1, \
184                                           GTK_FILL|GTK_EXPAND, GTK_FILL, 1, 1); \
185                 }                                                               \
186                 else    {                                                       \
187                         gtk_table_attach (GTK_TABLE (meet->table2),             \
188                                           do_space?s_hbox:widget, 1, 2, i, i+1, \
189                                           GTK_FILL|GTK_EXPAND, GTK_FILL, 1, 1); \
190                 }                                                               \
191                 if (GTK_IS_LABEL(widget)) {                                     \
192                         gtk_label_set_use_markup(GTK_LABEL (widget), TRUE);     \
193                         gtk_misc_set_alignment (GTK_MISC(widget),0, 0);         \
194                         gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE);       \
195                 }                                                               \
196         } else {                                                                \
197                 g_free(tmpstr);                                                 \
198                 if(intable1)    {                                               \
199                         gtk_table_attach (GTK_TABLE (meet->table1),             \
200                                           do_space?s_hbox:widget, 0, 2, i, i+1, \
201                                           GTK_FILL|GTK_EXPAND, GTK_FILL, 1, 1); \
202                 }                                                               \
203                 else    {                                                       \
204                         gtk_table_attach (GTK_TABLE (meet->table2),             \
205                                           do_space?s_hbox:widget, 0, 2, i, i+1, \
206                                           GTK_FILL|GTK_EXPAND, GTK_FILL, 1, 1); \
207                 }                                                               \
208         }                                                                       \
209         i++;                                                                    \
210 }
211 #endif
212 enum {
213         DAY,
214         MONTH,
215         YEAR,
216         HOUR,
217         MINUTE
218 };
219
220 static gboolean avail_btn_can_be_sensitive(void)
221 {
222         if (vcalprefs.freebusy_get_url == NULL
223         ||  *vcalprefs.freebusy_get_url == '\0')
224                 return FALSE;
225         else 
226                 return TRUE;
227 }
228
229 static gint get_dtdate(const gchar *str, gint field)
230 {
231         time_t t = icaltime_as_timet((icaltime_from_string(str)));
232         struct tm buft;
233         struct tm *lt;
234         
235         tzset();
236
237 #ifdef G_OS_WIN32
238         if (t < 0)
239                 t = 1;
240 #endif
241         lt = localtime_r(&t, &buft);
242
243         switch(field){
244         case DAY:
245                 return lt->tm_mday;
246         case MONTH:
247                 return lt->tm_mon + 1;
248         case YEAR:
249                 return lt->tm_year + 1900;
250         case HOUR:
251                 return lt->tm_hour;
252         case MINUTE:
253                 return lt->tm_min;
254         }
255         return -1;
256
257 }
258
259 static gboolean add_btn_cb(GtkButton *widget, gpointer data)
260 {
261         VCalAttendee *attendee = (VCalAttendee *)data;
262         attendee_add(attendee->meet, NULL, NULL, NULL, NULL, FALSE);
263         return TRUE;
264 }
265
266 static gboolean remove_btn_cb(GtkButton *widget, gpointer data)
267 {
268         VCalAttendee *attendee = (VCalAttendee *)data;
269         gtk_container_remove(GTK_CONTAINER(attendee->meet->attendees_vbox), attendee->hbox);
270         attendee->meet->attendees = g_slist_remove(attendee->meet->attendees, attendee);
271         
272         g_free(attendee->status);
273
274         return TRUE;
275 }
276
277 VCalAttendee *attendee_add(VCalMeeting *meet, gchar *address, gchar *name, gchar *partstat, gchar *cutype, gboolean first)
278 {
279         GtkWidget *att_hbox = gtk_hbox_new(FALSE, 6);
280         VCalAttendee *attendee  = g_new0(VCalAttendee, 1);
281 #if !(GTK_CHECK_VERSION(2,12,0))
282         GtkTooltips *tips = meet->tips;
283 #endif
284
285         attendee->address       = gtk_entry_new();
286         attendee->cutype        = gtk_combo_box_new_text();
287         attendee->avail_evtbox  = gtk_event_box_new();
288         attendee->avail_img     = gtk_image_new_from_stock
289                         (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
290
291         gtk_widget_show(attendee->address);
292         gtk_widget_show(attendee->cutype);
293         gtk_widget_show(attendee->avail_evtbox);
294
295         CLAWS_SET_TIP(attendee->address, _("Use <tab> to autocomplete from addressbook"));
296         gtk_widget_set_size_request(attendee->avail_evtbox, 18, 16);
297         gtk_event_box_set_visible_window(GTK_EVENT_BOX(attendee->avail_evtbox), FALSE);
298         gtk_container_add (GTK_CONTAINER(attendee->avail_evtbox), attendee->avail_img);
299
300         if (address) {
301                 gchar *str = g_strdup_printf("%s%s%s%s",
302                                 (name && strlen(name))?name:"",
303                                 (name && strlen(name))?" <":"",
304                                 address,
305                                 (name && strlen(name))?">":"");
306                 gtk_entry_set_text(GTK_ENTRY(attendee->address), str);
307                 g_free(str);
308         }
309
310         if (partstat)
311                 attendee->status = g_strdup(partstat);
312
313         gtk_combo_box_append_text(GTK_COMBO_BOX(attendee->cutype), _("Individual"));
314         gtk_combo_box_append_text(GTK_COMBO_BOX(attendee->cutype), _("Group"));
315         gtk_combo_box_append_text(GTK_COMBO_BOX(attendee->cutype), _("Resource"));
316         gtk_combo_box_append_text(GTK_COMBO_BOX(attendee->cutype), _("Room"));
317         
318         gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 0);
319         
320         if (cutype) {
321                 if (!strcmp(cutype, "group"))
322                         gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 1);
323                 if (!strcmp(cutype, "resource"))
324                         gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 2);
325                 if (!strcmp(cutype, "room"))
326                         gtk_combo_box_set_active(GTK_COMBO_BOX(attendee->cutype), 3);
327         }
328         
329         attendee->add_btn       = gtk_button_new_with_label(_("Add..."));
330         attendee->remove_btn    = gtk_button_new_with_label(_("Remove"));
331         attendee->meet          = meet;
332         attendee->hbox          = att_hbox;
333
334         gtk_widget_show(attendee->add_btn);
335         gtk_widget_show(attendee->remove_btn);
336         gtk_widget_show(attendee->hbox);
337
338         gtk_box_pack_start(GTK_BOX(attendee->hbox), attendee->avail_evtbox, FALSE, FALSE, 0);
339         gtk_widget_set_sensitive(attendee->remove_btn, !first);
340         meet->attendees         = g_slist_append(meet->attendees, attendee);
341         
342         g_signal_connect(G_OBJECT(attendee->remove_btn), "clicked",
343                          G_CALLBACK(remove_btn_cb), attendee);
344         g_signal_connect(G_OBJECT(attendee->add_btn), "clicked",
345                          G_CALLBACK(add_btn_cb), attendee);
346         
347         gtk_box_pack_start(GTK_BOX(att_hbox), attendee->address, FALSE, FALSE, 0);
348         gtk_box_pack_start(GTK_BOX(att_hbox), attendee->cutype, FALSE, FALSE, 0);
349         gtk_box_pack_start(GTK_BOX(att_hbox), attendee->add_btn, FALSE, FALSE, 0);
350         gtk_box_pack_start(GTK_BOX(att_hbox), attendee->remove_btn, FALSE, FALSE, 0);
351         gtk_box_pack_start(GTK_BOX(meet->attendees_vbox), att_hbox, FALSE, FALSE, 0);
352         address_completion_register_entry(GTK_ENTRY(attendee->address), FALSE);
353 #ifndef GENERIC_UMPC
354         gtk_widget_set_size_request(attendee->address, 320, -1);
355 #else
356         gtk_widget_set_size_request(attendee->address, 220, -1);
357 #endif
358         return attendee;
359 }
360
361 static gchar *get_organizer(VCalMeeting *meet)
362 {
363         int index = gtk_combo_box_get_active(GTK_COMBO_BOX(meet->who));
364         int i = 0;
365         GSList *cur = meet->avail_accounts;
366         while (i < index && cur && cur->data) {
367                 debug_print("%d:skipping %s\n",i,((PrefsAccount *)(cur->data))->address);
368                 cur = cur->next;
369                 i++;
370         }
371         if (cur)
372                 return g_strdup(((PrefsAccount *)(cur->data))->address);
373         else
374                 return g_strdup("");
375 }
376
377 static gchar *get_organizer_name(VCalMeeting *meet)
378 {
379         int index = gtk_combo_box_get_active(GTK_COMBO_BOX(meet->who));
380         int i = 0;
381         GSList *cur = meet->avail_accounts;
382         while (i < index && cur && cur->data) {
383                 debug_print("%d:skipping %s\n",i,((PrefsAccount *)(cur->data))->address);
384                 cur = cur->next;
385                 i++;
386         }
387         if (cur)
388                 return g_strdup(((PrefsAccount *)(cur->data))->name);
389         else
390                 return g_strdup("");
391 }
392
393 static void get_time_from_combo(GtkWidget *combo, int *h, int *m)
394 {
395         gchar *tmp;
396         gchar **parts;
397
398         if (!h || !m) 
399                 return;
400
401         tmp = gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(combo))), 0, -1);
402         parts = g_strsplit(tmp, ":", 2);
403         if (parts[0] && parts[1] && *parts[0] && *parts[1]) {
404                 *h = atoi(parts[0]);
405                 *m = atoi(parts[1]);
406         }
407         g_strfreev(parts);
408         g_free(tmp);
409 }
410
411 static int get_current_gmt_offset(void)
412 {
413         time_t now = time(NULL);
414         struct tm gmt;
415         struct tm local;
416         
417         tzset();
418
419 #ifdef G_OS_WIN32
420         if (now < 0)
421                 now = 1;
422 #endif
423         gmtime_r(& now, & gmt);
424         localtime_r(& now, & local);
425         
426         local.tm_isdst = 0;
427         return mktime(&local)-mktime(&gmt);
428 }
429
430 static int get_gmt_offset_at_time(time_t then)
431 {
432         struct tm gmt;
433         struct tm local;
434         
435         tzset();
436
437 #ifdef G_OS_WIN32
438         if (then < 0)
439                 then = 1;
440 #endif
441         gmtime_r(& then, & gmt);
442         localtime_r(& then, & local);
443         
444         local.tm_isdst = 0;
445         return mktime(&local)-mktime(&gmt);
446 }
447
448 static gchar *get_date(VCalMeeting *meet, int start) 
449 {
450         struct tm *lt;
451         time_t t;
452         guint d, m, y;
453         int dst_offset = 0;
454         struct tm buft;
455
456         tzset();
457
458         t = time(NULL);
459 #ifdef G_OS_WIN32
460         if (t < 0)
461                 t = 1;
462 #endif
463         lt = localtime_r(&t, &buft);
464         
465         gtk_calendar_get_date(GTK_CALENDAR(start ? meet->start_c : meet->end_c), &y, &m, &d);
466         lt->tm_mday = d;
467         lt->tm_mon  = m;
468         lt->tm_year = y - 1900;
469         lt->tm_hour = 0;
470         lt->tm_min  = 0;
471         lt->tm_sec  = 0;
472
473         if (start) {
474                 get_time_from_combo(meet->start_time, &lt->tm_hour, &lt->tm_min);
475         } else {
476                 get_time_from_combo(meet->end_time, &lt->tm_hour, &lt->tm_min);
477         }
478
479         debug_print("%d %d %d, %d:%d\n", lt->tm_mday, lt->tm_mon, lt->tm_year, lt->tm_hour, lt->tm_min);
480         t = mktime(lt);
481
482         dst_offset = get_current_gmt_offset() - get_gmt_offset_at_time(t);
483         debug_print("DST change offset to apply to time %d\n", dst_offset);
484         t += dst_offset;
485         debug_print("%s\n", ctime(&t));
486         return g_strdup(icaltime_as_ical_string(icaltime_from_timet(t, FALSE)));
487 }
488
489 static gchar *get_location(VCalMeeting *meet)
490 {
491         return gtk_editable_get_chars(GTK_EDITABLE(meet->location),0, -1);
492 }
493
494 static gchar *get_summary(VCalMeeting *meet) 
495 {
496         return gtk_editable_get_chars(GTK_EDITABLE(meet->summary),0, -1);
497 }
498
499 static gchar *get_description(VCalMeeting *meet) 
500 {
501         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
502         GtkTextIter start, end;
503         
504         gtk_text_buffer_get_start_iter(buffer, &start);
505         gtk_text_buffer_get_end_iter(buffer, &end);
506         return gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
507 }
508
509 void vcal_meeting_free(VCalMeeting *meet)
510 {
511         debug_print("freeing meeting\n");
512         g_free(meet->uid);
513         address_completion_end(meet->window);
514         g_slist_free(meet->avail_accounts);
515         g_slist_free(meet->attendees);
516         g_free(meet);
517 }
518
519 static void destroy_meeting_cb(GtkWidget *widget, gpointer data)
520 {
521         VCalMeeting *meet = (VCalMeeting *)data;
522         vcal_meeting_free(meet);
523 }
524
525 static void vcal_destroy(VCalMeeting *meet)
526 {
527         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
528         gtk_text_buffer_remove_selection_clipboard(buffer, gtk_clipboard_get(GDK_SELECTION_PRIMARY));
529         gtk_widget_destroy(meet->window);
530 }
531
532 static gboolean meeting_key_pressed(GtkWidget *widget,
533                                     GdkEventKey *event,
534                                     gpointer data)
535 {
536         VCalMeeting *meet = (VCalMeeting *)data;
537         
538         if (event && event->keyval == GDK_Escape) {
539                 vcal_destroy(meet);
540         }
541         return FALSE;
542 }
543
544 static int get_list_item_num(int h, int m)
545 {
546         if (m % 15 != 0)
547                 return -1;
548
549         return (h*4 + m/15);
550 }
551
552 static void meeting_end_changed(GtkWidget *widget, gpointer data);
553
554 static void meeting_start_changed(GtkWidget *widget, gpointer data)
555 {
556         VCalMeeting *meet = (VCalMeeting *)data;
557         struct tm start_lt;
558         struct tm end_lt;
559         time_t start_t, end_t;
560         guint d, m, y;
561         int num = -1;
562
563         if (strlen(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(meet->start_time))))) < 5)
564                 return;
565         tzset();
566
567         start_t = time(NULL);
568         end_t = time(NULL);
569 #ifdef G_OS_WIN32
570         if (start_t < 0)
571                 start_t = 1;
572         if (end_t < 0)
573                 end_t = 1;
574 #endif
575         localtime_r(&start_t, &start_lt);
576         localtime_r(&end_t, &end_lt);
577
578         gtk_calendar_get_date(GTK_CALENDAR(meet->start_c), &y, &m, &d);
579         start_lt.tm_mday = d; start_lt.tm_mon  = m; start_lt.tm_year = y - 1900;
580         get_time_from_combo(meet->start_time, &start_lt.tm_hour, &start_lt.tm_min);
581
582         start_t = mktime(&start_lt);
583         debug_print("start %s\n", ctime(&start_t));
584
585         gtk_calendar_get_date(GTK_CALENDAR(meet->end_c), &y, &m, &d);
586         end_lt.tm_mday = d; end_lt.tm_mon  = m; end_lt.tm_year = y - 1900;
587
588         get_time_from_combo(meet->end_time, &end_lt.tm_hour, &end_lt.tm_min);
589         end_t = mktime(&end_lt);
590
591         debug_print("end   %s\n", ctime(&end_t));
592         
593         if (end_t > start_t) {
594                 debug_print("ok\n");
595                 return;
596         }
597         end_t = start_t + 3600;
598
599 #ifdef G_OS_WIN32
600         if (end_t < 0)
601                 end_t = 1;
602 #endif
603         localtime_r(&end_t, &end_lt);
604         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);
605
606         g_signal_handlers_block_by_func(gtk_bin_get_child(GTK_BIN(meet->end_time)), meeting_end_changed, meet);
607         g_signal_handlers_block_by_func(meet->end_c, meeting_end_changed, meet);
608
609         gtk_calendar_select_day(GTK_CALENDAR(meet->end_c), end_lt.tm_mday);
610
611         gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
612                                 end_lt.tm_mon,
613                                 end_lt.tm_year + 1900);
614
615         num = get_list_item_num(end_lt.tm_hour, end_lt.tm_min);
616         if (num > -1) {
617                 gchar *time_text = g_strdup_printf("%02d:%02d", end_lt.tm_hour, end_lt.tm_min);
618                 combobox_select_by_text(GTK_COMBO_BOX(meet->end_time), time_text);
619                 g_free(time_text);
620         } else {
621                 gchar *tmp = g_strdup_printf("%02d:%02d",
622                                 end_lt.tm_hour, 
623                                 end_lt.tm_min);
624                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(meet->end_time))),
625                                    tmp);
626                 g_free(tmp);
627         }
628         g_signal_handlers_unblock_by_func(gtk_bin_get_child(GTK_BIN(meet->end_time)), meeting_end_changed, meet);
629         g_signal_handlers_unblock_by_func(meet->end_c, meeting_end_changed, meet);
630 }
631
632 static void meeting_end_changed(GtkWidget *widget, gpointer data)
633 {
634         VCalMeeting *meet = (VCalMeeting *)data;
635         struct tm start_lt;
636         struct tm end_lt;
637         time_t start_t, end_t;
638         guint d, m, y;
639         int num = -1;
640
641         if (strlen(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(meet->end_time))))) < 5)
642                 return;
643         start_t = time(NULL);
644         end_t = time(NULL);
645
646         tzset();
647
648 #ifdef G_OS_WIN32
649         if (start_t < 0)
650                 start_t = 1;
651         if (end_t < 0)
652                 end_t = 1;
653 #endif
654         localtime_r(&start_t, &start_lt);
655         localtime_r(&end_t, &end_lt);
656         
657         gtk_calendar_get_date(GTK_CALENDAR(meet->start_c), &y, &m, &d);
658         start_lt.tm_mday = d; start_lt.tm_mon  = m; start_lt.tm_year = y - 1900;
659         get_time_from_combo(meet->start_time, &start_lt.tm_hour, &start_lt.tm_min);
660
661         start_t = mktime(&start_lt);
662         debug_print("start %s\n", ctime(&start_t));
663
664         gtk_calendar_get_date(GTK_CALENDAR(meet->end_c), &y, &m, &d);
665         end_lt.tm_mday = d; end_lt.tm_mon  = m; end_lt.tm_year = y - 1900;
666         get_time_from_combo(meet->end_time, &end_lt.tm_hour, &end_lt.tm_min);
667
668         end_t = mktime(&end_lt);
669
670         debug_print("end   %s\n", ctime(&end_t));
671         
672         if (end_t > start_t) {
673                 debug_print("ok\n");
674                 return;
675         }
676         start_t = end_t - 3600;
677         
678         tzset();
679
680 #ifdef G_OS_WIN32
681         if (start_t < 0)
682                 start_t = 1;
683 #endif
684         localtime_r(&start_t, &start_lt);
685         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);
686
687         g_signal_handlers_block_by_func(gtk_bin_get_child(GTK_BIN(meet->start_time)), meeting_start_changed, meet);
688         g_signal_handlers_block_by_func(meet->start_c, meeting_start_changed, meet);
689
690         gtk_calendar_select_day(GTK_CALENDAR(meet->start_c), start_lt.tm_mday);
691
692         gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
693                                 start_lt.tm_mon,
694                                 start_lt.tm_year + 1900);
695
696         num = get_list_item_num(start_lt.tm_hour, start_lt.tm_min);
697         if (num > -1) {
698                 gchar *time_text = g_strdup_printf("%02d:%02d", start_lt.tm_hour, start_lt.tm_min);
699                 combobox_select_by_text(GTK_COMBO_BOX(meet->start_time), time_text);
700                 g_free(time_text);
701         } else {
702                 gchar *tmp = g_strdup_printf("%02d:%02d",
703                                 start_lt.tm_hour, 
704                                 start_lt.tm_min);
705                 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(meet->start_time))),
706                                    tmp);
707                 g_free(tmp);
708         }
709
710         g_signal_handlers_unblock_by_func(gtk_bin_get_child(GTK_BIN(meet->start_time)), meeting_start_changed, meet);
711         g_signal_handlers_unblock_by_func(meet->start_c, meeting_start_changed, meet);
712 }
713
714 static void att_update_icon(VCalMeeting *meet, VCalAttendee *attendee, gint avail, gchar *text)
715 {
716         const gchar *icon = GTK_STOCK_DIALOG_INFO;
717 #if !(GTK_CHECK_VERSION(2,12,0))
718         GtkTooltips *tips = meet->tips;
719 #endif
720
721         switch (avail) {
722                 case 0:  icon = GTK_STOCK_DIALOG_WARNING;       break;
723                 case 1:  icon = GTK_STOCK_DIALOG_INFO;          break;
724                 default: icon = GTK_STOCK_DIALOG_QUESTION;      break;
725         }
726         if (!gtk_entry_get_text(GTK_ENTRY(attendee->address)) 
727          || strlen(gtk_entry_get_text(GTK_ENTRY(attendee->address)))==0) {
728                 if (attendee->avail_img) {
729                         gtk_widget_hide(attendee->avail_img);
730                 }
731                 CLAWS_SET_TIP(attendee->avail_evtbox, NULL);
732         } else if (attendee->avail_img) {
733                 gtk_image_set_from_stock
734                         (GTK_IMAGE(attendee->avail_img), 
735                         icon, 
736                         GTK_ICON_SIZE_SMALL_TOOLBAR);
737                 gtk_widget_show(attendee->avail_img);
738                 CLAWS_SET_TIP(attendee->avail_evtbox, text);
739         }
740 }
741
742 gboolean attendee_available(VCalAttendee *attendee, const gchar *dtstart, const gchar *dtend, const gchar *contents)
743 {
744         icalcomponent *toplvl, *vfreebusy;
745         icalproperty *busyprop;
746         struct icaltimetype start = icaltime_from_string(dtstart);
747         struct icaltimetype end = icaltime_from_string(dtend);
748         gboolean result = TRUE;
749         
750
751         if (contents == NULL)
752                 return TRUE;
753
754         toplvl = icalcomponent_new_from_string((gchar *)contents);
755         
756         if (toplvl == NULL)
757                 return TRUE;
758
759         vfreebusy = icalcomponent_get_first_component(toplvl, ICAL_VFREEBUSY_COMPONENT);
760         while (vfreebusy && icalcomponent_isa(vfreebusy) != ICAL_VFREEBUSY_COMPONENT)
761                 vfreebusy = icalcomponent_get_next_component(toplvl, ICAL_VFREEBUSY_COMPONENT);
762         
763         if (vfreebusy) {
764                 busyprop = icalcomponent_get_first_property(vfreebusy, ICAL_FREEBUSY_PROPERTY);
765                 while (busyprop) {
766                         struct icalperiodtype ipt = icalproperty_get_freebusy(busyprop);
767                         
768                         if ( icaltime_compare(start, ipt.end) >= 0 || icaltime_compare(end, ipt.start) <= 0 ) {
769                                 result = TRUE;
770                         } else {
771                                 result = FALSE;
772                                 break;
773                         }
774                         busyprop = icalcomponent_get_next_property(vfreebusy, ICAL_FREEBUSY_PROPERTY);
775                 }
776         }
777
778         icalcomponent_free(toplvl);
779         return result;
780 }
781
782 static gchar *get_avail_msg(const gchar *unavailable_persons, gboolean multiple, 
783         gboolean short_version, gint offset_before, gint offset_after)
784 {
785         gchar *msg, *intro = NULL, *outro = NULL, *before = NULL, *after = NULL;
786
787         if (multiple)
788                 intro = g_strdup(_("The following person(s) are busy at the time of your planned meeting:\n- "));
789         else if (!strcmp(unavailable_persons, _("You")))
790                 intro = g_strdup(_("You are busy at the time of your planned meeting"));
791         else
792                 intro = g_strdup_printf(_("%s is busy at the time of your planned meeting"), unavailable_persons);
793         if (offset_before == 3600)
794                 before = g_strdup_printf(_("%d hour sooner"), offset_before/3600);
795         else if (offset_before > 3600 && offset_before%3600 == 0)
796                 before = g_strdup_printf(_("%d hours sooner"), offset_before/3600);
797         else if (offset_before > 3600)
798                 before = g_strdup_printf(_("%d hours and %d minutes sooner"), offset_before/3600, (offset_before%3600)/60);
799         else if (offset_before == 1800)
800                 before = g_strdup_printf(_("%d minutes sooner"), offset_before/60);
801         else
802                 before = NULL;
803         
804         if (offset_after == 3600)
805                 after = g_strdup_printf(_("%d hour later"), offset_after/3600);
806         else if (offset_after > 3600 && offset_after%3600 == 0)
807                 after = g_strdup_printf(_("%d hours later"), offset_after/3600);
808         else if (offset_after > 3600)
809                 after = g_strdup_printf(_("%d hours and %d minutes later"), offset_after/3600, (offset_after%3600)/60);
810         else if (offset_after == 1800)
811                 after = g_strdup_printf(_("%d minutes later"), offset_after/60);
812         else
813                 after = NULL;
814         
815         if (multiple) {
816                 if (before && after)
817                         outro = g_strdup_printf(_("\n\nEveryone would be available %s or %s."), before, after);
818                 else if (before || after)
819                         outro = g_strdup_printf(_("\n\nEveryone would be available %s."), before?before:after);
820                 else
821                         outro = g_strdup_printf(_("\n\nIt isn't possible to have this meeting with everyone "
822                                                 "in the previous or next 6 hours."));
823         } else {
824                 if (short_version) {
825                         if (before && after)
826                                 outro = g_markup_printf_escaped(_("would be available %s or %s"), before, after);
827                         else if (before || after)
828                                 outro = g_markup_printf_escaped(_("would be available %s"), before?before:after);
829                         else
830                                 outro = g_strdup_printf(_("not available"));
831                 } else {
832                         if (before && after)
833                                 outro = g_markup_printf_escaped(_(", but would be available %s or %s."), before, after);
834                         else if (before || after)
835                                 outro = g_markup_printf_escaped(_(", but would be available %s."), before?before:after);
836                         else
837                                 outro = g_strdup_printf(_(", and isn't available "
838                                                         "in the previous or next 6 hours."));
839                 }
840         }
841         if (multiple && short_version)
842                 msg = g_strconcat(outro+2, NULL);
843         else if (multiple)
844                 msg = g_strconcat(intro, unavailable_persons, outro, NULL);
845         else if (short_version)
846                 msg = g_strdup(outro);
847         else
848                 msg = g_strconcat(intro, outro, NULL);
849         g_free(intro);
850         g_free(outro);
851         g_free(before);
852         g_free(after);
853         return msg;
854 }
855
856 static gboolean find_availability(const gchar *dtstart, const gchar *dtend, GSList *attendees, gboolean for_send, VCalMeeting *meet)
857 {
858         GSList *cur;
859         gint offset = -1800, offset_before = 0, offset_after = 0;
860         gboolean found = FALSE;
861         gchar *unavailable_persons = NULL;
862         gchar *msg = NULL;
863         struct icaltimetype start = icaltime_from_string(dtstart);
864         struct icaltimetype end = icaltime_from_string(dtend);
865         AlertValue val = G_ALERTALTERNATE;
866         gint total = 0;
867         GHashTable *avail_table_avail = g_hash_table_new(NULL, g_direct_equal);
868         GHashTable *avail_table_before = g_hash_table_new(NULL, g_direct_equal);
869         GHashTable *avail_table_after = g_hash_table_new(NULL, g_direct_equal);
870 #if !(GTK_CHECK_VERSION(2,12,0))
871         GtkTooltips *tips = meet->tips;
872 #endif
873         
874         for (cur = attendees; cur; cur = cur->next) {
875                 VCalAttendee *attendee = (VCalAttendee *)cur->data;
876                 if (!attendee_available(attendee, icaltime_as_ical_string(start), icaltime_as_ical_string(end),
877                                 attendee->cached_contents)) {
878                         gchar *mail = NULL;
879                         
880                         if (attendee->org)
881                                 mail = g_strdup(_("You"));
882                         else
883                                 mail = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
884
885                         if (unavailable_persons == NULL) {
886                                 unavailable_persons = g_markup_printf_escaped("%s", mail);
887                         } else {
888                                 gchar *tmp = g_markup_printf_escaped("%s,\n- %s", unavailable_persons, mail);
889                                 g_free(unavailable_persons);
890                                 unavailable_persons = tmp;
891                         }
892                         total++;
893                         g_free(mail);
894                         att_update_icon(meet, attendee, 0, _("not available"));
895                 } else {
896                         if (attendee->cached_contents != NULL)
897                                 att_update_icon(meet, attendee, 1, _("available"));
898                         else
899                                 att_update_icon(meet, attendee, 2, _("Free/busy retrieval failed"));
900
901                         g_hash_table_insert(avail_table_avail, attendee, GINT_TO_POINTER(1));
902                 }
903         }
904         offset = -1800;
905         found = FALSE;
906         while (!found && offset >= -3600*6) {
907                 gboolean ok = TRUE;
908                 struct icaltimetype new_start = icaltime_from_timet(icaltime_as_timet(start)+offset, FALSE);
909                 struct icaltimetype new_end   = icaltime_from_timet(icaltime_as_timet(end)+offset, FALSE);
910                 for (cur = attendees; cur; cur = cur->next) {
911                         VCalAttendee *attendee = (VCalAttendee *)cur->data;
912                         debug_print("trying %s - %s (offset %d)\n", 
913                                 icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end), offset);
914                         if (!attendee_available(attendee, icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end),
915                                         attendee->cached_contents)) {
916                                 ok = FALSE;
917                                 break;
918                         } else {
919                                 if (!g_hash_table_lookup(avail_table_before, attendee)
920                                 &&  !g_hash_table_lookup(avail_table_avail, attendee))
921                                         g_hash_table_insert(avail_table_before, attendee, GINT_TO_POINTER(-offset));
922                         }
923                 }
924                 if (ok) {
925                         found = TRUE;
926                         offset_before = -offset;
927                 }
928                 offset -= 1800;
929         }
930         found = FALSE;
931         offset = 1800;
932         while (!found && offset <= 3600*6) {
933                 gboolean ok = TRUE;
934                 struct icaltimetype new_start = icaltime_from_timet(icaltime_as_timet(start)+offset, FALSE);
935                 struct icaltimetype new_end   = icaltime_from_timet(icaltime_as_timet(end)+offset, FALSE);
936                 for (cur = attendees; cur; cur = cur->next) {
937                         VCalAttendee *attendee = (VCalAttendee *)cur->data;
938                         debug_print("trying %s - %s (offset %d)\n", 
939                                 icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end), offset);
940                         if (!attendee_available(attendee, icaltime_as_ical_string(new_start), icaltime_as_ical_string(new_end),
941                                         attendee->cached_contents)) {
942                                 ok = FALSE;
943                                 break;
944                         } else {
945                                 if (!g_hash_table_lookup(avail_table_after, attendee)
946                                 &&  !g_hash_table_lookup(avail_table_avail, attendee))
947                                         g_hash_table_insert(avail_table_after, attendee, GINT_TO_POINTER(offset));
948                         }
949                 }
950                 if (ok) {
951                         found = TRUE;
952                         offset_after = offset;
953                 }
954
955                 offset += 1800;
956         }
957         
958         for (cur = attendees; cur; cur = cur->next) {
959                 VCalAttendee *attendee = (VCalAttendee *)cur->data;
960                 gint ok = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_avail, attendee));
961                 gint o_before = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_before, attendee));
962                 gint o_after = GPOINTER_TO_INT(g_hash_table_lookup(avail_table_after, attendee));
963                 if (!o_before && !o_after && !ok) {
964                         att_update_icon(meet, attendee, 0, _("not available"));
965                 } else if ((o_before != 0 || o_after != 0) && !ok) {
966                         if (attendee->org)
967                                 msg = get_avail_msg(_("You"), FALSE, TRUE, o_before, o_after);
968                         else
969                                 msg = get_avail_msg(gtk_entry_get_text(GTK_ENTRY(attendee->address)), FALSE, TRUE, o_before, o_after);
970                         att_update_icon(meet, attendee, 0, msg);
971                         g_free(msg);
972                 }
973                 
974         }
975         g_hash_table_destroy(avail_table_before);
976         g_hash_table_destroy(avail_table_after);
977
978         if (for_send) {
979                 msg = get_avail_msg(unavailable_persons, (total > 1), FALSE, offset_before, offset_after);
980
981                 val = alertpanel_full(_("Not everyone is available"), msg,
982                                         GTK_STOCK_CANCEL, _("Send anyway"), NULL, FALSE,
983                                         NULL, ALERT_QUESTION, G_ALERTDEFAULT);
984                 g_free(msg);
985         }
986         msg = get_avail_msg(unavailable_persons, TRUE, TRUE, offset_before, offset_after);
987         g_free(unavailable_persons);
988         gtk_image_set_from_stock
989                 (GTK_IMAGE(meet->total_avail_img), 
990                 GTK_STOCK_DIALOG_WARNING, 
991                 GTK_ICON_SIZE_SMALL_TOOLBAR);
992         gtk_widget_show(meet->total_avail_img);
993         gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Not everyone is available. "
994                                 "See tooltips for more info..."));
995         CLAWS_SET_TIP(meet->total_avail_evtbox, msg);
996         g_free(msg);
997         return (val == G_ALERTALTERNATE);
998 }
999
1000 static gboolean check_attendees_availability(VCalMeeting *meet, gboolean tell_if_ok, gboolean for_send)
1001 {
1002         GSList *cur;
1003         gchar *tmp = NULL;
1004         gchar *real_url = NULL;
1005         gint num_format = 0;
1006         gchar *change_user = NULL, *change_dom = NULL;
1007         gchar *dtstart = NULL;
1008         gchar *dtend = NULL;
1009         gboolean find_avail = FALSE;
1010         gboolean res = TRUE, uncertain = FALSE;
1011         gchar *organizer = NULL;
1012         VCalAttendee *dummy_org = NULL;
1013         gchar *internal_ifb = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1014                                 "vcalendar", G_DIR_SEPARATOR_S, 
1015                                 "internal.ifb", NULL);
1016         gboolean local_only = FALSE;
1017         GSList *attlist;
1018 #if !(GTK_CHECK_VERSION(2,12,0))
1019         GtkTooltips *tips = meet->tips;
1020 #endif
1021
1022         if (vcalprefs.freebusy_get_url == NULL
1023         ||  *vcalprefs.freebusy_get_url == '\0') {
1024                 local_only = TRUE;
1025         } else {
1026                 real_url = g_strdup(vcalprefs.freebusy_get_url);
1027                 tmp = real_url;
1028
1029                 while (strchr(tmp, '%')) {
1030                         tmp = strchr(tmp, '%')+1;
1031                         num_format++;
1032                 }
1033                 if (num_format > 2) {
1034                         g_warning("wrong format in %s!\n", real_url);
1035                         g_free(real_url);
1036                         return FALSE;
1037                 }
1038
1039                 tmp = NULL;
1040                 if (strstr(real_url, "%u") != NULL) {
1041                         change_user = strstr(real_url, "%u");
1042                         *(strstr(real_url, "%u")+1) = 's';
1043                 } 
1044                 if (strstr(real_url, "%d") != NULL) {
1045                         change_dom = strstr(real_url, "%d");
1046                         *(strstr(real_url, "%d")+1) = 's';
1047                 } 
1048                 debug_print("url format %s\n", real_url);
1049         }
1050         dtstart = get_date(meet, TRUE);
1051         dtend = get_date(meet, FALSE);
1052
1053         /* hack to check our own avail. */
1054         organizer = get_organizer(meet);
1055         dummy_org = g_new0(VCalAttendee, 1);
1056         dummy_org->address      = gtk_entry_new();
1057         dummy_org->avail_img    = meet->avail_img;
1058         dummy_org->avail_evtbox = meet->avail_evtbox;
1059         dummy_org->org = TRUE;
1060         gtk_entry_set_text(GTK_ENTRY(dummy_org->address), organizer);
1061         g_free(organizer);
1062         dummy_org->cached_contents = file_read_to_str(internal_ifb);
1063         g_free(internal_ifb);
1064         
1065         if (!local_only) {
1066                 meet->attendees = g_slist_prepend(meet->attendees, dummy_org);
1067                 attlist = meet->attendees;
1068         } else {
1069                 attlist = g_slist_prepend(NULL, dummy_org);
1070         }
1071         
1072         gtk_widget_set_sensitive(meet->save_btn, FALSE);
1073         gtk_widget_set_sensitive(meet->avail_btn, FALSE);
1074
1075         if (meet->window->window)
1076                 gdk_window_set_cursor(meet->window->window, watch_cursor);
1077
1078         for (cur = attlist; cur && cur->data; cur = cur->next) {
1079                 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1080                 gchar *email = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
1081                 gchar *remail, *user, *domain;
1082                 gchar *contents = NULL;
1083
1084                 if (*email == '\0') {
1085                         g_free(email);
1086                         att_update_icon(meet, attendee, 0, NULL);
1087                         continue;
1088                 }
1089
1090                 if (!local_only) {
1091                         remail = g_strdup(email);
1092
1093                         extract_address(remail);
1094                         if (strrchr(remail, ' '))
1095                                 user = g_strdup(strrchr(remail, ' ')+1);
1096                         else
1097                                 user = g_strdup(remail);
1098                         if (strchr(user, '@')) {
1099                                 domain = g_strdup(strchr(user, '@')+1);
1100                                 *(strchr(user, '@')) = '\0';
1101                         } else {
1102                                 domain = g_strdup("");
1103                         }
1104                         g_free(remail);
1105                         if (change_user && change_dom) {
1106                                 if (change_user < change_dom)
1107                                         tmp = g_strdup_printf(real_url, user, domain);
1108                                 else
1109                                         tmp = g_strdup_printf(real_url, domain, user);
1110                         } else if (change_user) {
1111                                 tmp = g_strdup_printf(real_url, user);
1112                         } else if (change_dom) {
1113                                 tmp = g_strdup_printf(real_url, domain);
1114                         } else {
1115                                 tmp = g_strdup(real_url);
1116                         }
1117                         g_free(user);
1118                         g_free(domain);
1119                         debug_print("url to get %s\n", tmp);
1120                 }
1121
1122                 if (attendee->cached_contents != NULL) {
1123                         contents = attendee->cached_contents;
1124                         attendee->cached_contents = NULL;
1125                 } else if (!local_only) {
1126                         if (strncmp(tmp, "http://", 7) 
1127                         && strncmp(tmp, "https://", 8)
1128                         && strncmp(tmp, "webcal://", 9)
1129                         && strncmp(tmp, "webcals://", 10)
1130                         && strncmp(tmp, "ftp://", 6))
1131                                 contents = file_read_to_str(tmp);
1132                         else {
1133                                 gchar *label = g_strdup_printf(_("Fetching planning for %s..."), email);
1134                                 if (!strncmp(tmp, "webcal", 6)) {
1135                                         gchar *tmp2 = g_strdup_printf("http%s", tmp+6);
1136                                         g_free(tmp);
1137                                         tmp = tmp2;
1138                                 }
1139                                 contents = vcal_curl_read(tmp, label, FALSE, NULL);
1140                                 g_free(label);
1141                         }
1142                 } else {
1143                         contents = NULL;
1144                 }
1145
1146                 g_free(email);
1147                 g_free(tmp);
1148
1149                 if (contents == NULL) {
1150                         uncertain = TRUE;
1151                         att_update_icon(meet, attendee, 2, _("Free/busy retrieval failed"));
1152                         continue;
1153                 }
1154                 else {
1155                         if (!attendee_available(attendee, dtstart, dtend, contents)) {
1156                                 find_avail = TRUE;
1157                                 debug_print("not available!\n");
1158                         } else {
1159                                 debug_print("available!\n");
1160                                 att_update_icon(meet, attendee, 1, _("Available"));
1161                         }
1162                         attendee->cached_contents = contents;
1163                         
1164                 }
1165         }
1166         
1167         if (find_avail) {
1168                 res = find_availability((dtstart), (dtend), attlist, for_send, meet);
1169         } else {
1170                 res = TRUE;
1171                 if (tell_if_ok) {
1172                         if (for_send)
1173                                 alertpanel_notice(_("Everyone is available."));
1174                         else if (!uncertain) {
1175                                 gtk_image_set_from_stock
1176                                         (GTK_IMAGE(meet->total_avail_img), 
1177                                         GTK_STOCK_DIALOG_INFO, 
1178                                         GTK_ICON_SIZE_SMALL_TOOLBAR);
1179                                 gtk_widget_show(meet->total_avail_img);
1180                                 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Everyone is available."));
1181                                 CLAWS_SET_TIP(meet->total_avail_evtbox, NULL);
1182                         } else {
1183                                 gtk_image_set_from_stock
1184                                         (GTK_IMAGE(meet->total_avail_img), 
1185                                         GTK_STOCK_DIALOG_QUESTION, 
1186                                         GTK_ICON_SIZE_SMALL_TOOLBAR);
1187                                 gtk_widget_show(meet->total_avail_img);
1188                                 gtk_label_set_text(GTK_LABEL(meet->total_avail_msg), _("Everyone is available."));
1189                                 CLAWS_SET_TIP(meet->total_avail_evtbox, _("Everyone seems available, but some free/busy information failed to be retrieved."));
1190                         }
1191                 }
1192         }
1193
1194         for (cur = attlist; cur && cur->data; cur = cur->next) {
1195                 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1196                 g_free(attendee->cached_contents);
1197                 attendee->cached_contents = NULL;
1198         }
1199         gtk_widget_set_sensitive(meet->save_btn, TRUE);
1200         gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1201         if (meet->window->window)
1202                 gdk_window_set_cursor(meet->window->window, NULL);
1203
1204         if (!local_only)
1205                 meet->attendees = g_slist_remove(meet->attendees, dummy_org);
1206         else
1207                 g_slist_free(attlist);
1208         gtk_widget_destroy(dummy_org->address);
1209         g_free(dummy_org);
1210
1211         if (!local_only)
1212                 g_free(real_url);
1213
1214         g_free(dtstart);
1215         g_free(dtend);
1216         return res;
1217 }
1218
1219 static gboolean check_avail_cb(GtkButton *widget, gpointer data)
1220 {
1221         VCalMeeting *meet = (VCalMeeting *)data;
1222         check_attendees_availability(meet, TRUE, FALSE);
1223         return TRUE;
1224 }
1225
1226 static gboolean send_meeting_cb(GtkButton *widget, gpointer data)
1227 {
1228         VCalMeeting *meet = (VCalMeeting *)data;
1229         gchar *uid = NULL;
1230         gchar *organizer = NULL;
1231         gchar *organizer_name = NULL;
1232         gchar *dtstart = NULL;
1233         gchar *dtend = NULL;
1234         gchar *tzid = NULL;
1235         gchar *location = NULL;
1236         gchar *summary = NULL;
1237         gchar *description = NULL;
1238         VCalEvent *event = NULL;
1239         gchar buf[256];
1240         GSList *cur;
1241         PrefsAccount *account = NULL;
1242         gboolean res = FALSE;
1243         gboolean found_att = FALSE;
1244         Folder *folder = folder_find_from_name ("vCalendar", vcal_folder_get_class());
1245         gboolean redisp = FALSE;
1246
1247         if (meet->uid == NULL && meet->visible && 
1248             !check_attendees_availability(meet, FALSE, TRUE)) {
1249                 return FALSE;
1250         }
1251
1252         if (folder) {
1253                 MainWindow *mainwin = mainwindow_get_mainwindow();
1254                 if (mainwin->summaryview->folder_item == folder->inbox) {
1255                         redisp = TRUE;
1256                         summary_show(mainwin->summaryview, NULL);
1257                 }
1258         }
1259         gtk_widget_set_sensitive(meet->save_btn, FALSE);
1260         gtk_widget_set_sensitive(meet->avail_btn, FALSE);
1261         if (meet->window->window)
1262                 gdk_window_set_cursor(meet->window->window, watch_cursor);
1263
1264         organizer       = get_organizer(meet);
1265         organizer_name  = get_organizer_name(meet);
1266         account         = account_find_from_address(organizer, FALSE);
1267
1268         if (account && account->set_domain && account->domain) {
1269                 g_snprintf(buf, sizeof(buf), "%s", account->domain); 
1270         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
1271                 g_snprintf(buf, sizeof(buf), "%s", 
1272                         strchr(account->address, '@') ?
1273                                 strchr(account->address, '@')+1 :
1274                                 account->address);
1275         } else {
1276                 g_snprintf(buf, sizeof(buf), "%s", "");
1277         }
1278         generate_msgid(buf, 255, account->address);
1279
1280         if (meet->uid) {
1281                 uid     = g_strdup(meet->uid);
1282         } else {
1283                 uid     = g_strdup(buf);
1284         }
1285
1286         dtstart         = get_date(meet, TRUE);
1287         dtend           = get_date(meet, FALSE);
1288         location        = get_location(meet);
1289         summary         = get_summary(meet);
1290         description     = get_description(meet);
1291         
1292         event = vcal_manager_new_event(uid, organizer, organizer_name, location, summary, description,
1293                                         dtstart, dtend, NULL, tzid, NULL, meet->method, 
1294                                         meet->sequence,
1295                                         ICAL_VEVENT_COMPONENT);
1296         
1297         vcal_manager_update_answer(event, organizer, organizer_name, 
1298                                    ICAL_PARTSTAT_ACCEPTED,
1299                                    ICAL_CUTYPE_INDIVIDUAL);
1300         
1301         for (cur = meet->attendees; cur && cur->data; cur = cur->next) {
1302                 VCalAttendee *attendee = (VCalAttendee *)cur->data;
1303                 gchar *email = gtk_editable_get_chars(GTK_EDITABLE(attendee->address), 0, -1);
1304                 gint index = 0;
1305                 gchar *orig_email = email;
1306                 gchar *name = NULL;
1307                 enum icalparameter_cutype cutype = ICAL_CUTYPE_INDIVIDUAL;
1308                 enum icalparameter_partstat status = ICAL_PARTSTAT_NEEDSACTION;
1309                 
1310                 index = gtk_combo_box_get_active(GTK_COMBO_BOX(attendee->cutype));
1311                 
1312                 cutype = ICAL_CUTYPE_INDIVIDUAL + index;
1313                 if (attendee->status) {
1314                         if(!strcmp(attendee->status, "accepted"))
1315                                 status = ICAL_PARTSTAT_ACCEPTED;
1316                         if(!strcmp(attendee->status, "tentatively accepted"))
1317                                 status = ICAL_PARTSTAT_TENTATIVE;
1318                         if(!strcmp(attendee->status, "declined"))
1319                                 status = ICAL_PARTSTAT_DECLINED;
1320                         g_free(attendee->status);                       
1321                 }
1322                 if (strlen(email)) {
1323                         if (strstr(email, " <")) {
1324                                 name = email;
1325                                 email = strstr(email," <") + 2;
1326                                 *(strstr(name," <")) = '\0';
1327                                 if (strstr(email, ">"))
1328                                         *(strstr(email, ">")) = '\0';
1329                         } 
1330                         
1331                         vcal_manager_update_answer(event, email, name, 
1332                                         status, cutype);
1333                                         
1334                         found_att = strcmp(email, organizer);
1335                 }
1336                 g_free(orig_email);
1337         }
1338         
1339         if (found_att)
1340                 res = vcal_manager_request(account, event);
1341         else
1342                 res = TRUE;
1343         g_free(uid);
1344         g_free(organizer);
1345         g_free(organizer_name);
1346         g_free(dtstart);
1347         g_free(dtend);
1348         g_free(description);
1349         g_free(location);
1350         g_free(summary);
1351         vcal_manager_free_event(event);
1352
1353         gtk_widget_set_sensitive(meet->save_btn, TRUE);
1354         gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1355         if (meet->window->window)
1356                 gdk_window_set_cursor(meet->window->window, NULL);
1357
1358         if (res) {
1359                 vcal_destroy(meet);
1360         } else {
1361                 alertpanel_error(_("Could not send the meeting invitation.\n"
1362                                    "Check the recipients."));
1363         }
1364
1365         if (folder)
1366                 folder_item_scan(folder->inbox);
1367
1368         if (folder && redisp) {
1369                 MainWindow *mainwin = mainwindow_get_mainwindow();
1370                 summary_show(mainwin->summaryview, folder->inbox);
1371         }
1372
1373         return res;
1374 }
1375
1376 static GList *get_predefined_times(void)
1377 {
1378         int h,m;
1379         GList *times = NULL;
1380         for (h = 0; h < 24; h++) {
1381                 for (m = 0; m < 60; m += 15) {
1382                         gchar *tmp = g_strdup_printf("%02d:%02d", h, m);
1383                         times = g_list_append(times, tmp);
1384                 }
1385         }
1386         return times;
1387 }
1388
1389 static VCalMeeting *vcal_meeting_create_real(VCalEvent *event, gboolean visible)
1390 {
1391         VCalMeeting *meet = g_new0(VCalMeeting, 1);
1392         GtkTextBuffer *buffer = NULL;
1393         GtkWidget *date_hbox, *date_vbox, *save_hbox, *label, *hbox;
1394         gchar *s = NULL;
1395         GtkObject *start_h_adj, *start_m_adj, *end_h_adj, *end_m_adj;
1396         int i = 0, num = 0;
1397         GtkWidget *scrolledwin;
1398         GList *times = NULL;
1399         GList *accounts;
1400         GtkListStore *menu;
1401         gchar *time_text = NULL;
1402 #ifdef GENERIC_UMPC
1403         GtkWidget *notebook;
1404         GtkWidget *maemo_vbox0;
1405 #endif
1406
1407         if (!watch_cursor)
1408                 watch_cursor = gdk_cursor_new(GDK_WATCH);
1409
1410 #if !(GTK_CHECK_VERSION(2,12,0))
1411         meet->tips = tips;
1412 #endif
1413         meet->visible = visible;
1414         start_h_adj = gtk_adjustment_new (0, 0, 23, 1, 10, 10);
1415         start_m_adj = gtk_adjustment_new (0, 0, 59, 1, 10, 10);
1416         end_h_adj   = gtk_adjustment_new (0, 0, 23, 1, 10, 10);
1417         end_m_adj   = gtk_adjustment_new (0, 0, 59, 1, 10, 10);
1418
1419         meet->window            = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "vcal_meeting_gtk");
1420 #ifndef GENERIC_UMPC
1421         meet->table             = gtk_table_new(7, 2, FALSE);
1422 #else
1423         meet->table1            = gtk_table_new(4, 2, FALSE);
1424         meet->table2            = gtk_table_new(2, 2, FALSE);
1425 #endif
1426         meet->who               = gtk_combo_box_new_text();
1427         
1428         meet->start_c           = gtk_calendar_new();
1429         meet->end_c             = gtk_calendar_new();
1430
1431         meet->avail_evtbox  = gtk_event_box_new();
1432         meet->avail_img = gtk_image_new_from_stock
1433                         (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
1434
1435         times = get_predefined_times();
1436
1437 #if !GTK_CHECK_VERSION(2, 24, 0)
1438         meet->start_time = gtk_combo_box_entry_new_text();
1439 #else
1440         meet->start_time = gtk_combo_box_text_new_with_entry();
1441 #endif
1442         gtk_combo_box_set_active(GTK_COMBO_BOX(meet->start_time), -1);
1443         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(meet->start_time)));
1444 #if !GTK_CHECK_VERSION(2, 24, 0)        
1445         combobox_set_popdown_strings(GTK_COMBO_BOX(meet->start_time), times);
1446 #else   
1447         combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(meet->start_time), times);
1448 #endif
1449         
1450 #if !GTK_CHECK_VERSION(2, 24, 0)
1451         meet->end_time = gtk_combo_box_entry_new_text();
1452 #else
1453         meet->end_time = gtk_combo_box_text_new_with_entry();
1454 #endif
1455         gtk_combo_box_set_active(GTK_COMBO_BOX(meet->end_time), -1);
1456         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(meet->end_time)));
1457 #if !GTK_CHECK_VERSION(2, 24, 0)        
1458         combobox_set_popdown_strings(GTK_COMBO_BOX(meet->end_time), times);
1459 #else   
1460         combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(meet->end_time), times);
1461 #endif
1462
1463         list_free_strings(times);
1464         g_list_free(times);
1465
1466         meet->location          = gtk_entry_new();
1467         meet->summary           = gtk_entry_new();
1468         meet->description       = gtk_text_view_new();
1469         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(meet->description));
1470         gtk_text_view_set_editable(GTK_TEXT_VIEW(meet->description), TRUE);
1471         gtk_text_buffer_add_selection_clipboard(buffer, gtk_clipboard_get(GDK_SELECTION_PRIMARY));
1472
1473         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
1474         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
1475                                        GTK_POLICY_AUTOMATIC,
1476                                        GTK_POLICY_AUTOMATIC);
1477         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
1478                                             GTK_SHADOW_IN);
1479         gtk_container_add(GTK_CONTAINER(scrolledwin), meet->description);
1480
1481         if (event) {
1482                 meet->uid = g_strdup(event->uid);
1483                 meet->sequence = event->sequence + 1;
1484                 meet->method = (event->method == ICAL_METHOD_CANCEL ?
1485                                 ICAL_METHOD_CANCEL:ICAL_METHOD_REQUEST);
1486
1487                 gtk_entry_set_text(GTK_ENTRY(meet->location), event->location);
1488                 gtk_entry_set_text(GTK_ENTRY(meet->summary), event->summary);   
1489                 gtk_text_buffer_set_text(buffer, event->description, -1);       
1490         } else 
1491                 meet->method = ICAL_METHOD_REQUEST;
1492         
1493         meet->save_btn          = gtk_button_new_with_label(_("Save & Send"));
1494         meet->avail_btn         = gtk_button_new_with_label(_("Check availability"));
1495
1496         meet->total_avail_evtbox  = gtk_event_box_new();
1497         meet->total_avail_img   = gtk_image_new_from_stock
1498                         (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
1499         meet->total_avail_msg = gtk_label_new("");
1500         
1501         gtk_widget_set_size_request(meet->total_avail_evtbox, 18, 16);
1502         gtk_event_box_set_visible_window(GTK_EVENT_BOX(meet->total_avail_evtbox), FALSE);
1503         gtk_container_add (GTK_CONTAINER(meet->total_avail_evtbox), meet->total_avail_img);
1504
1505         g_signal_connect(G_OBJECT(meet->save_btn), "clicked",
1506                          G_CALLBACK(send_meeting_cb), meet);
1507
1508         g_signal_connect(G_OBJECT(meet->avail_btn), "clicked",
1509                          G_CALLBACK(check_avail_cb), meet);
1510
1511         g_signal_connect(G_OBJECT(meet->window), "destroy",
1512                          G_CALLBACK(destroy_meeting_cb), meet);
1513         g_signal_connect(G_OBJECT(meet->window), "key_press_event",
1514                          G_CALLBACK(meeting_key_pressed), meet);
1515         
1516
1517         gtk_widget_set_size_request(meet->description, -1, 100);
1518         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(meet->description), GTK_WRAP_WORD);
1519         
1520         if (!event || (event && !event->dtstart && !event->dtend)) {
1521                 time_t t = time (NULL)+ 3600;
1522                 struct tm buft1, buft2;
1523                 struct tm *lt = localtime_r (&t, &buft1);
1524                 mktime(lt);
1525                 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1526                                         lt->tm_mday);
1527                 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1528                                         lt->tm_mon, lt->tm_year + 1900);
1529         
1530                 time_text = g_strdup_printf("%02d:%02d", lt->tm_hour, 0);
1531                 combobox_select_by_text(GTK_COMBO_BOX(meet->start_time), time_text);
1532                 g_free(time_text);
1533
1534                 t += 3600;
1535                 lt = localtime_r(&t, &buft2);
1536                 
1537                 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1538                                         lt->tm_mday);
1539                 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1540                                         lt->tm_mon, lt->tm_year + 1900);
1541
1542                 time_text = g_strdup_printf("%02d:%02d", lt->tm_hour, 0);
1543                 combobox_select_by_text(GTK_COMBO_BOX(meet->end_time), time_text);
1544                 g_free(time_text);
1545
1546         } else {
1547                 gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1548                                         get_dtdate(event->dtstart, DAY));
1549                 gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1550                                         get_dtdate(event->dtend, DAY));
1551
1552                 gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1553                                         get_dtdate(event->dtstart, MONTH)-1,
1554                                         get_dtdate(event->dtstart, YEAR));
1555                 gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1556                                         get_dtdate(event->dtend, MONTH)-1,
1557                                         get_dtdate(event->dtend, YEAR));
1558
1559                 num = get_list_item_num(get_dtdate(event->dtstart, HOUR), 
1560                                         get_dtdate(event->dtstart, MINUTE));
1561                 if (num > -1) {
1562                         time_text = g_strdup_printf("%02d:%02d", get_dtdate(event->dtstart, HOUR), 
1563                                                                  get_dtdate(event->dtstart, MINUTE));
1564                         combobox_select_by_text(GTK_COMBO_BOX(meet->start_time), time_text);
1565                         g_free(time_text);
1566                 } else {
1567                         gchar *tmp = g_strdup_printf("%02d:%02d",
1568                                         get_dtdate(event->dtstart, HOUR), 
1569                                         get_dtdate(event->dtstart, MINUTE));
1570                         gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(meet->start_time))),
1571                                            tmp);
1572                         g_free(tmp);
1573                 }
1574
1575                 num = get_list_item_num(get_dtdate(event->dtend, HOUR), 
1576                                         get_dtdate(event->dtend, MINUTE));
1577                 if (num > -1) {
1578                         time_text = g_strdup_printf("%02d:%02d", get_dtdate(event->dtend, HOUR), 
1579                                                                  get_dtdate(event->dtend, MINUTE));
1580                         combobox_select_by_text(GTK_COMBO_BOX(meet->end_time), time_text);
1581                         g_free(time_text);
1582                 } else {
1583                         gchar *tmp = g_strdup_printf("%02d:%02d",
1584                                         get_dtdate(event->dtend, HOUR), 
1585                                         get_dtdate(event->dtend, MINUTE));
1586                         gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(meet->end_time))),
1587                                            tmp);
1588                         g_free(tmp);
1589                 }
1590         }
1591
1592         g_signal_connect(G_OBJECT(meet->start_c), "day-selected",
1593                          G_CALLBACK(meeting_start_changed), meet);
1594         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(meet->start_time))),
1595                          "changed",
1596                          G_CALLBACK(meeting_start_changed),
1597                          meet);
1598
1599         g_signal_connect(G_OBJECT(meet->end_c), "day-selected",
1600                          G_CALLBACK(meeting_end_changed), meet);
1601         g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(meet->end_time))),
1602                          "changed",
1603                          G_CALLBACK(meeting_end_changed),
1604                          meet);
1605
1606 #ifndef GENERIC_UMPC
1607         gtk_widget_set_size_request(meet->start_time, 80, -1);
1608         gtk_widget_set_size_request(meet->end_time, 80, -1);
1609 #else
1610         gtk_widget_set_size_request(meet->start_time, 120, -1);
1611         gtk_widget_set_size_request(meet->end_time, 120, -1);
1612 #endif
1613         
1614         date_hbox = gtk_hbox_new(FALSE, 6);
1615         date_vbox = gtk_vbox_new(FALSE, 6);
1616         hbox = gtk_hbox_new(FALSE, 6);
1617         label = gtk_label_new(_("<b>Starts at:</b> "));
1618         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1619         gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1620         
1621         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1622         gtk_box_pack_start(GTK_BOX(hbox), meet->start_time, FALSE, FALSE, 0);
1623         label = gtk_label_new(_("<b> on:</b>"));
1624         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1625         gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1626         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1627         gtk_box_pack_start(GTK_BOX(date_vbox), hbox, FALSE, FALSE, 0);
1628         gtk_box_pack_start(GTK_BOX(date_vbox), meet->start_c, FALSE, FALSE, 0);
1629         gtk_box_pack_start(GTK_BOX(date_hbox), date_vbox, FALSE, FALSE, 0);
1630
1631 #ifndef GENERIC_UMPC
1632         label = gtk_label_new(" "); 
1633 #else
1634         label = gtk_label_new(""); 
1635 #endif
1636         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1637         gtk_box_pack_start(GTK_BOX(date_hbox), label, TRUE, TRUE, 0);
1638
1639         date_vbox = gtk_vbox_new(FALSE, 6);
1640         hbox = gtk_hbox_new(FALSE, 6);
1641         label = gtk_label_new(_("<b>Ends at:</b> ")); 
1642         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1643         gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1644         
1645         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1646         gtk_box_pack_start(GTK_BOX(hbox), meet->end_time, FALSE, FALSE, 0);
1647         label = gtk_label_new(_("<b> on:</b>")); 
1648         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
1649         gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
1650         gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1651         gtk_box_pack_start(GTK_BOX(date_vbox), hbox, FALSE, FALSE, 0);
1652         gtk_box_pack_start(GTK_BOX(date_vbox), meet->end_c, FALSE, FALSE, 0);
1653         gtk_box_pack_start(GTK_BOX(date_hbox), date_vbox, FALSE, FALSE, 0);
1654
1655         meet->attendees_vbox = gtk_vbox_new(FALSE, 6);
1656         gtk_widget_show_all(meet->attendees_vbox);
1657         if (!event) {
1658                 attendee_add(meet, NULL, NULL, NULL, NULL, TRUE);
1659         } else {
1660                 gboolean firstadd = TRUE;
1661                 GSList *list = vcal_manager_get_answers_emails(event);
1662                 while (list && list->data) {
1663                         gchar *address = (gchar *)list->data;
1664                         gchar *name = vcal_manager_get_attendee_name(event, address);
1665                         gchar *answer = vcal_manager_get_reply_text_for_attendee(event, address);
1666                         gchar *type = vcal_manager_get_cutype_text_for_attendee(event, address);
1667                         if (strcmp(event->organizer, address)) {
1668                                 attendee_add(meet, address, name, answer, type, firstadd);
1669                                 firstadd = FALSE;
1670                         }
1671                         g_free(name);
1672                         g_free(answer);
1673                         g_free(type);
1674                         list = list->next;
1675                 } 
1676                 
1677                 if (firstadd == TRUE)
1678                         attendee_add(meet, NULL, NULL, NULL, NULL, TRUE);
1679         }
1680
1681         if (!event) {
1682                 gtk_window_set_title(GTK_WINDOW(meet->window), _("New meeting"));
1683         } else {
1684                 gchar *title = g_strdup_printf(_("%s - Edit meeting"),
1685                         event->summary);
1686                 gtk_window_set_title(GTK_WINDOW(meet->window), title);
1687                 g_free(title);
1688         }
1689         address_completion_start(meet->window);
1690         
1691         accounts = account_get_list();
1692         g_return_val_if_fail(accounts != NULL, NULL);
1693
1694         for (i = 0; accounts != NULL; accounts = accounts->next) {
1695                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
1696                 
1697                 if (ac->protocol == A_NNTP) {
1698                         continue;
1699                 }
1700                 if (!event && ac == account_get_cur_account()) {
1701                         num = i;
1702                 }
1703                 else if (event && !strcmp(ac->address, event->organizer))
1704                         num = i;
1705
1706                 meet->avail_accounts = g_slist_append(meet->avail_accounts, ac);
1707                 
1708                 if (ac->name)
1709                         s = g_strdup_printf("%s: %s <%s>",
1710                                                ac->account_name,
1711                                                ac->name, ac->address);
1712                 else
1713                         s = g_strdup_printf("%s: %s",
1714                                                ac->account_name, ac->address);
1715
1716                 gtk_combo_box_append_text(GTK_COMBO_BOX(meet->who), s);
1717                 g_free(s);
1718                 i++;
1719         }
1720         gtk_combo_box_set_active(GTK_COMBO_BOX(meet->who), num);
1721         
1722         save_hbox = gtk_hbox_new(FALSE, 6);
1723         gtk_box_pack_start(GTK_BOX(save_hbox), meet->save_btn, FALSE, FALSE, 0);
1724         gtk_box_pack_start(GTK_BOX(save_hbox), meet->avail_btn, FALSE, FALSE, 0);
1725         gtk_box_pack_start(GTK_BOX(save_hbox), meet->total_avail_evtbox, FALSE, FALSE, 0);
1726         gtk_box_pack_start(GTK_BOX(save_hbox), meet->total_avail_msg, FALSE, FALSE, 0);
1727         
1728         hbox = gtk_hbox_new(FALSE, 6);
1729         gtk_box_pack_start(GTK_BOX(hbox), meet->avail_evtbox, FALSE, FALSE, 0);
1730         gtk_box_pack_start(GTK_BOX(hbox), meet->who, TRUE, TRUE, 0);
1731
1732         gtk_widget_set_size_request(meet->avail_evtbox, 18, 16);
1733         gtk_event_box_set_visible_window(GTK_EVENT_BOX(meet->avail_evtbox), FALSE);
1734         gtk_container_add (GTK_CONTAINER(meet->avail_evtbox), meet->avail_img);
1735
1736 #ifndef GENERIC_UMPC
1737         TABLE_ADD_LINE(_("Organizer:"), hbox, FALSE);
1738         TABLE_ADD_LINE(_("Summary:"), meet->summary, TRUE);
1739         TABLE_ADD_LINE(_("Time:"), date_hbox, TRUE);
1740         TABLE_ADD_LINE(_("Location:"), meet->location, TRUE);
1741         TABLE_ADD_LINE(_("Description:"), scrolledwin, TRUE);
1742         TABLE_ADD_LINE(_("Attendees:"), meet->attendees_vbox, FALSE);
1743         TABLE_ADD_LINE("", save_hbox, TRUE);
1744         
1745         gtk_widget_set_size_request(meet->window, -1, -1);
1746         gtk_container_add(GTK_CONTAINER(meet->window), meet->table);
1747 #else
1748         TABLE_ADD_LINE(_("Organizer:"), hbox, FALSE, TRUE);
1749         TABLE_ADD_LINE(_("Summary:"), meet->summary, TRUE, TRUE);
1750         TABLE_ADD_LINE(_("Location:"), meet->location, FALSE, TRUE);
1751         TABLE_ADD_LINE(_("Description:"), scrolledwin, TRUE, TRUE);
1752         TABLE_ADD_LINE(_("Attendees:"), meet->attendees_vbox, FALSE, TRUE);
1753         TABLE_ADD_LINE("", date_hbox, TRUE, FALSE);
1754         
1755         notebook = gtk_notebook_new ();
1756         gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
1757         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
1758                         meet->table1,
1759                         gtk_label_new_with_mnemonic(_("Event:")));
1760                         
1761         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
1762                         meet->table2,
1763                         gtk_label_new_with_mnemonic(_("Time:")));
1764         gtk_widget_show (notebook);
1765         
1766         maemo_vbox0 = gtk_vbox_new(FALSE, 3);
1767         gtk_box_pack_start(GTK_BOX(maemo_vbox0), notebook, TRUE, TRUE, 0);
1768         gtk_box_pack_start(GTK_BOX(maemo_vbox0), save_hbox, FALSE, FALSE, 0);
1769         
1770         gtk_widget_set_size_request(meet->window, -1, -1);
1771         gtk_container_add (GTK_CONTAINER (meet->window), maemo_vbox0);
1772         
1773         maemo_connect_key_press_to_mainwindow(GTK_WINDOW(meet->window));
1774 #endif
1775         if (visible) {
1776                 GSList *cur;
1777                 gtk_widget_show_all(meet->window);
1778                 for (cur = meet->attendees; cur; cur = cur->next) {
1779                         gtk_widget_hide(((VCalAttendee *)cur->data)->avail_img);
1780                 }
1781                 gtk_widget_hide(meet->avail_img);
1782                 gtk_widget_hide(meet->total_avail_img);
1783                 gtk_widget_set_sensitive(meet->avail_btn, avail_btn_can_be_sensitive());
1784         }
1785         return meet;
1786 }
1787
1788 VCalMeeting *vcal_meeting_create(VCalEvent *event)
1789 {
1790         return vcal_meeting_create_real(event, TRUE);
1791 }
1792
1793 VCalMeeting *vcal_meeting_create_with_start(VCalEvent *event, struct tm *sdate)
1794 {
1795         VCalMeeting *meet = vcal_meeting_create(event);
1796         int num = -1;
1797         gtk_calendar_select_day(GTK_CALENDAR(meet->start_c),
1798                                 sdate->tm_mday);
1799         gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1800                                 sdate->tm_mday);
1801
1802         gtk_calendar_select_month(GTK_CALENDAR(meet->start_c),
1803                                 sdate->tm_mon, sdate->tm_year+1900);
1804         gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1805                                 sdate->tm_mon, sdate->tm_year+1900);
1806
1807         if (sdate->tm_hour != 0) {
1808                 num = get_list_item_num(sdate->tm_hour, 0);
1809                 if (num > -1) {
1810                         gchar *time_text = g_strdup_printf("%02d:%02d", sdate->tm_hour, 0);
1811                         combobox_select_by_text(GTK_COMBO_BOX(meet->start_time), time_text);
1812                         g_free(time_text);
1813                 } 
1814                 if (sdate->tm_hour < 23) {
1815                         num = get_list_item_num(sdate->tm_hour+1, 0);
1816                         if (num > -1) {
1817                                 gchar *time_text = g_strdup_printf("%02d:%02d", sdate->tm_hour+1, 0);
1818                                 combobox_select_by_text(GTK_COMBO_BOX(meet->end_time), time_text);
1819                                 g_free(time_text);
1820                         } 
1821                 } else {
1822                         struct tm tm_tomorrow;
1823                         gchar *time_text;
1824                         tm_tomorrow.tm_mday = sdate->tm_mday;
1825                         tm_tomorrow.tm_mon = sdate->tm_mon;
1826                         tm_tomorrow.tm_wday = sdate->tm_wday;
1827                         tm_tomorrow.tm_year = sdate->tm_year+1900;
1828                         tm_tomorrow.tm_hour = sdate->tm_hour;
1829                         orage_move_day(&tm_tomorrow, +1);
1830                         gtk_calendar_select_day(GTK_CALENDAR(meet->end_c),
1831                                                 tm_tomorrow.tm_mday);
1832                         gtk_calendar_select_month(GTK_CALENDAR(meet->end_c),
1833                                                 tm_tomorrow.tm_mon, tm_tomorrow.tm_year);
1834                         
1835                         time_text = g_strdup_printf("%02d:%02d", 0, 0);
1836                         combobox_select_by_text(GTK_COMBO_BOX(meet->end_time), time_text);
1837                         g_free(time_text);
1838                 }
1839         }
1840         return meet;
1841 }
1842
1843 VCalMeeting *vcal_meeting_create_hidden(VCalEvent *event)
1844 {
1845         return vcal_meeting_create_real(event, FALSE);
1846 }
1847
1848 gboolean vcal_meeting_send(VCalMeeting *meet)
1849 {
1850         return send_meeting_cb(NULL, meet);
1851 }
1852
1853 gboolean vcal_meeting_alert_check(gpointer data)
1854 {
1855         GSList *events = NULL, *cur = NULL;
1856
1857         if (!vcalprefs.alert_enable)
1858                 return TRUE;
1859
1860         events = vcal_folder_get_waiting_events();
1861
1862         for (cur = events; cur; cur = cur->next) {
1863                 VCalEvent *event = (VCalEvent *)cur->data;
1864                 time_t start, end, current;
1865                 gboolean warn = FALSE;
1866
1867                 tzset();
1868
1869                 start = icaltime_as_timet(icaltime_from_string(event->dtstart));
1870                 end = icaltime_as_timet(icaltime_from_string(event->dtend));
1871                 current = time(NULL);
1872                 
1873                 if (start - current <= (vcalprefs.alert_delay*60) 
1874                 &&  start - current + 60 > (vcalprefs.alert_delay*60)) {
1875                         warn = TRUE;
1876                 } else if (event->postponed - current <= (vcalprefs.alert_delay*60)
1877                 &&         event->postponed - current + 60 > (vcalprefs.alert_delay*60)) {
1878                         warn = TRUE;
1879                 }
1880                 if (warn) {
1881                         time_t tmpt = icaltime_as_timet((icaltime_from_string(event->dtstart)));
1882                         gchar *estart = NULL;
1883                         AlertValue aval;
1884                         int length = (end - start) / 60;
1885                         gchar *duration = NULL, *hours = NULL, *minutes = NULL;
1886                         gchar *message = NULL;
1887                         gchar *title = NULL;
1888                         gchar *label = NULL;
1889                         int postpone_min = 0;
1890
1891                         tzset();
1892
1893                         estart = g_strdup(ctime(&tmpt));
1894
1895                         if (length >= 60)
1896                                 hours = g_strdup_printf(ngettext("%d hour", "%d hours", 
1897                                                 (length/60) > 1 ? 2 : 1), length/60);
1898                         if (length%60)
1899                                 minutes = g_strdup_printf(ngettext("%d minute", "%d minutes",
1900                                                 length%60), length%60);
1901
1902                         duration = g_strdup_printf("%s%s%s",
1903                                         hours?hours:"",
1904                                         hours && minutes ? " ":"",
1905                                         minutes?minutes:"");
1906
1907                         g_free(hours);
1908                         g_free(minutes);
1909
1910                         title = g_strdup_printf(_("Upcoming event: %s"), event->summary);
1911                         message = g_strdup_printf(_("You have a meeting or event soon.\n"
1912                                          "It starts at %s and ends %s later.\n"
1913                      "Location: %s\n"
1914                                          "More information:\n\n"
1915                                          "%s"),
1916                                                 estart,
1917                                                 duration,
1918                                                 event->location?event->location:"",
1919                                                 event->description);
1920
1921                         g_free(duration);
1922                         g_free(estart);
1923
1924                         postpone_min = (vcalprefs.alert_delay/2 > 15) ? 15: (vcalprefs.alert_delay/2);
1925                         if (postpone_min == 0)
1926                                 postpone_min = 1;
1927
1928                         label = g_strdup_printf(ngettext("Remind me in %d minute", "Remind me in %d minutes",
1929                                                  postpone_min > 1 ? 2:1), 
1930                                                  postpone_min);
1931                         aval = alertpanel_full(title, message,
1932                                         label, GTK_STOCK_OK, NULL, FALSE,
1933                                         NULL, ALERT_NOTICE, G_ALERTDEFAULT);
1934                         g_free(label);
1935
1936                         g_free(title);
1937                         g_free(message);
1938
1939                         if (aval == G_ALERTDEFAULT) {
1940                                 if (event->postponed == 0)
1941                                         event->postponed = start + (postpone_min*60);
1942                                 else
1943                                         event->postponed += (postpone_min*60);
1944                         } else {
1945                                 event->postponed = (time_t)0;
1946                         }
1947                         vcal_manager_save_event(event, FALSE);
1948                 }
1949                 
1950                 vcal_manager_free_event((VCalEvent *)cur->data);
1951         }
1952         
1953         g_slist_free(events);
1954
1955         return TRUE;
1956 }
1957
1958 void multisync_export(void)
1959 {
1960         GSList *list = NULL;
1961         gchar *path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1962                                 "vcalendar", G_DIR_SEPARATOR_S, 
1963                                 "multisync", NULL);
1964         GSList *files = NULL;
1965         GSList *cur = NULL;
1966         gchar *file = NULL;
1967         gchar *tmp = NULL;
1968         gint i = 0;
1969         icalcomponent *calendar = NULL;
1970         FILE *fp;
1971
1972         if (is_dir_exist(path))
1973                 remove_dir_recursive(path);
1974         if (!is_dir_exist(path))
1975                 make_dir(path);
1976         if (!is_dir_exist(path)) {
1977                 perror(path);
1978                 g_free(path);
1979                 return;
1980         }
1981         
1982         list = vcal_folder_get_waiting_events();
1983         for (cur = list; cur; cur = cur->next) {
1984                 VCalEvent *event = (VCalEvent *)cur->data;
1985                 file = g_strdup_printf("multisync%lu-%d", time(NULL), i);
1986
1987                 i++;
1988
1989                 calendar = 
1990                         icalcomponent_vanew(
1991                             ICAL_VCALENDAR_COMPONENT,
1992                             icalproperty_new_version("2.0"),
1993                             icalproperty_new_prodid(
1994                                  "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
1995                             icalproperty_new_calscale("GREGORIAN"),
1996                             0
1997                     );  
1998                 vcal_manager_event_dump(event, FALSE, FALSE, calendar, FALSE);
1999                 tmp = g_strconcat(path, G_DIR_SEPARATOR_S, file, NULL);
2000                 str_write_to_file(icalcomponent_as_ical_string(calendar), tmp);
2001                 g_free(tmp);
2002                 files = g_slist_append(files, file);
2003                 vcal_manager_free_event(event);
2004                 icalcomponent_free(calendar);
2005         }
2006
2007         g_slist_free(list);
2008         
2009         file = g_strconcat(path, G_DIR_SEPARATOR_S, "backup_entries", NULL);
2010         fp = g_fopen(file, "wb");
2011         g_free(file);
2012         if (fp) {
2013                 for (cur = files; cur; cur = cur->next) {
2014                         file = (char *)cur->data;
2015                         if (fprintf(fp, "1 1 %s\n", file) < 0)
2016                                 perror(file);
2017                         g_free(file);
2018                 }
2019                 if (fclose(fp) == EOF)
2020                         perror(file);
2021         } else {
2022                 perror(file);
2023         }
2024         g_free(path);
2025         g_slist_free(files);
2026 }
2027
2028 gboolean vcal_meeting_export_calendar(const gchar *path, 
2029                                 const gchar *user, const gchar *pass,
2030                                 gboolean automatic)
2031 {
2032         GSList *list = vcal_folder_get_waiting_events();
2033         GSList *subs = NULL;
2034         GSList *cur;
2035         icalcomponent *calendar = NULL;
2036         gchar *file = NULL;
2037         gchar *tmpfile = get_tmp_file();
2038         gchar *internal_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2039                                 "vcalendar", G_DIR_SEPARATOR_S, 
2040                                 "internal.ics", NULL);
2041
2042         gboolean res = TRUE;
2043         long filesize = 0;
2044         
2045         multisync_export();
2046
2047         if (vcalprefs.export_subs && vcalprefs.export_enable)
2048                 subs = vcal_folder_get_webcal_events();
2049
2050         if (g_slist_length(list) == 0 && g_slist_length(subs) == 0) {
2051                 g_slist_free(list);
2052                 g_slist_free(subs);
2053                 if (!automatic) {
2054                         alertpanel_full(_("Empty calendar"),
2055                                         _("There is nothing to export."),
2056                                         GTK_STOCK_OK, NULL, NULL, FALSE,
2057                                         NULL, ALERT_NOTICE, G_ALERTDEFAULT);
2058                         return FALSE;
2059                 } else {
2060                         str_write_to_file("", tmpfile);
2061                         goto putfile;
2062                 }
2063         }
2064         
2065         calendar = 
2066                 icalcomponent_vanew(
2067                     ICAL_VCALENDAR_COMPONENT,
2068                     icalproperty_new_version("2.0"),
2069                     icalproperty_new_prodid(
2070                          "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
2071                     icalproperty_new_calscale("GREGORIAN"),
2072                     0
2073             );  
2074
2075         for (cur = list; cur; cur = cur->next) {
2076                 VCalEvent *event = (VCalEvent *)cur->data;
2077                 vcal_manager_event_dump(event, FALSE, FALSE, calendar, FALSE);
2078                 vcal_manager_free_event(event);
2079         }
2080
2081         if (str_write_to_file(icalcomponent_as_ical_string(calendar), internal_file) < 0) {
2082                 g_warning("can't export internal cal\n");
2083         }
2084         
2085         g_free(internal_file);
2086
2087         for (cur = subs; cur; cur = cur->next) {
2088                 /* Not to be freed */
2089                 icalcomponent *event = (icalcomponent *)cur->data;
2090                 vcal_manager_icalevent_dump(event, NULL, calendar);
2091         }
2092
2093         if (vcalprefs.export_enable || path == NULL) {
2094                 if (str_write_to_file(icalcomponent_as_ical_string(calendar), tmpfile) < 0) {
2095                         alertpanel_error(_("Could not export the calendar."));
2096                         g_free(tmpfile);
2097                         icalcomponent_free(calendar);
2098                         g_slist_free(list);
2099                         g_slist_free(subs);
2100                         return FALSE;
2101                 }
2102                 filesize = strlen(icalcomponent_as_ical_string(calendar));
2103         }
2104
2105         icalcomponent_free(calendar);
2106         
2107 putfile:
2108         g_slist_free(list);
2109         g_slist_free(subs);
2110
2111         if (!path && !automatic)
2112                 file = filesel_select_file_save(_("Export calendar to ICS"), NULL);
2113         else
2114                 file = g_strdup(path);
2115
2116         if (automatic && (!path || strlen(path) == 0 || !vcalprefs.export_enable)) {
2117                 g_free(tmpfile);
2118                 g_free(file);
2119                 return TRUE;
2120         }
2121
2122         if (file 
2123         && strncmp(file, "http://", 7) 
2124         && strncmp(file, "https://", 8)
2125         && strncmp(file, "webcal://", 9)
2126         && strncmp(file, "webcals://", 10)
2127         && strncmp(file, "ftp://", 6)) {
2128                 gchar *afile = NULL;
2129                 if (file[0] != G_DIR_SEPARATOR)
2130                         afile=g_strdup_printf("%s%s%s", get_home_dir(), 
2131                                         G_DIR_SEPARATOR_S, file);
2132                 else
2133                         afile=g_strdup(file);
2134                 if (move_file(tmpfile, afile, TRUE) != 0) {
2135                         log_error(LOG_PROTOCOL, _("Couldn't export calendar to '%s'\n"),
2136                                 afile);
2137                         res = FALSE;
2138                 }
2139                 g_free(afile);
2140                 g_free(file);
2141         } else if (file) {
2142                 FILE *fp = g_fopen(tmpfile, "rb");
2143                 if (!strncmp(file, "webcal", 6)) {
2144                         gchar *tmp = g_strdup_printf("http%s", file+6);
2145                         g_free(file);
2146                         file = tmp;
2147                 }
2148                 if (fp) {
2149                         res = vcal_curl_put(file, fp, filesize, user, pass);
2150                         fclose(fp);
2151                 }
2152                 g_free(file);
2153         }
2154         g_free(tmpfile);
2155         return res;
2156 }
2157
2158 gboolean vcal_meeting_export_freebusy(const gchar *path, const gchar *user,
2159                                 const gchar *pass)
2160 {
2161         GSList *list = vcal_folder_get_waiting_events();
2162         GSList *cur;
2163         icalcomponent *calendar = NULL, *timezone = NULL, *tzc = NULL, *vfreebusy = NULL;
2164         gchar *file = NULL;
2165         gchar *tmpfile = get_tmp_file();
2166         gchar *internal_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
2167                                 "vcalendar", G_DIR_SEPARATOR_S, 
2168                                 "internal.ifb", NULL);
2169         time_t whole_start = time(NULL);
2170         time_t whole_end = whole_start + (60*60*24*365);
2171         gboolean res = TRUE;
2172         struct icaltimetype itt_start, itt_end;
2173         long filesize = 0;
2174         
2175         multisync_export();
2176
2177         calendar = 
2178                 icalcomponent_vanew(
2179                     ICAL_VCALENDAR_COMPONENT,
2180                     icalproperty_new_version("2.0"),
2181                     icalproperty_new_prodid(
2182                          "-//Claws Mail//NONSGML Claws Mail Calendar//EN"),
2183                     icalproperty_new_calscale("GREGORIAN"),
2184                     0
2185             );  
2186
2187         timezone = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
2188         
2189         icalcomponent_add_property(timezone,
2190                 icalproperty_new_tzid("UTC"));
2191         
2192         tzc = icalcomponent_new(ICAL_XSTANDARD_COMPONENT);
2193         icalcomponent_add_property(tzc,
2194                 icalproperty_new_dtstart(
2195                         icaltime_from_string("19700101T000000")));
2196         icalcomponent_add_property(tzc,
2197                 icalproperty_new_tzoffsetfrom(0.0));
2198         icalcomponent_add_property(tzc,
2199                 icalproperty_new_tzoffsetto(0.0));
2200         icalcomponent_add_property(tzc,
2201                 icalproperty_new_tzname("Greenwich meridian time"));
2202
2203         icalcomponent_add_component(timezone, tzc);
2204
2205         icalcomponent_add_component(calendar, timezone);
2206
2207         itt_start = icaltime_from_timet(whole_start, FALSE);
2208         itt_end = icaltime_from_timet(whole_end, FALSE);
2209         itt_start.second = itt_start.minute = itt_start.hour = 0;
2210         itt_end.second = 59; itt_end.minute = 59; itt_end.hour = 23;
2211
2212
2213         vfreebusy = 
2214             icalcomponent_vanew(
2215                 ICAL_VFREEBUSY_COMPONENT,
2216                 icalproperty_vanew_dtstart(itt_start, 0),
2217                 icalproperty_vanew_dtend(itt_end, 0),
2218                 0
2219                 );
2220
2221         debug_print("DTSTART:%s\nDTEND:%s\n",
2222                 icaltime_as_ical_string(itt_start),
2223                 icaltime_as_ical_string(itt_end));
2224
2225         for (cur = list; cur; cur = cur->next) {
2226                 VCalEvent *event = (VCalEvent *)cur->data;
2227                 icalproperty *prop;
2228                 struct icalperiodtype ipt;
2229         
2230                 ipt.start = icaltime_from_string(event->dtstart);
2231                 ipt.end = icaltime_from_string(event->dtend);
2232                 ipt.duration = icaltime_subtract(ipt.end, ipt.start);
2233                 if (icaltime_as_timet(ipt.start) <= icaltime_as_timet(itt_end) 
2234                  && icaltime_as_timet(ipt.end) >= icaltime_as_timet(itt_start)) {
2235                         prop = icalproperty_new_freebusy(ipt);
2236                         icalcomponent_add_property(vfreebusy, prop);
2237                 }
2238                 vcal_manager_free_event(event);
2239         }
2240
2241         icalcomponent_add_component(calendar, vfreebusy);
2242         
2243         if (str_write_to_file(icalcomponent_as_ical_string(calendar), internal_file) < 0) {
2244                 g_warning("can't export freebusy\n");
2245         }
2246         
2247         g_free(internal_file);
2248
2249         if (vcalprefs.export_freebusy_enable) {
2250                 if (str_write_to_file(icalcomponent_as_ical_string(calendar), tmpfile) < 0) {
2251                         alertpanel_error(_("Could not export the freebusy info."));
2252                         g_free(tmpfile);
2253                         icalcomponent_free(calendar);
2254                         g_slist_free(list);
2255                         return FALSE;
2256                 }
2257                 filesize = strlen(icalcomponent_as_ical_string(calendar));
2258         }
2259
2260         icalcomponent_free(calendar);
2261         g_slist_free(list);
2262         
2263         if ((!path || strlen(path) == 0 || !vcalprefs.export_freebusy_enable)) {
2264                 g_free(tmpfile);
2265                 return TRUE;
2266         }
2267         file = g_strdup(path);
2268
2269
2270         if (file 
2271         && strncmp(file, "http://", 7) 
2272         && strncmp(file, "https://", 8)
2273         && strncmp(file, "webcal://", 9)
2274         && strncmp(file, "webcals://", 10)
2275         && strncmp(file, "ftp://", 6)) {
2276                 gchar *afile = NULL;
2277                 if (file[0] != G_DIR_SEPARATOR)
2278                         afile=g_strdup_printf("%s%s%s", get_home_dir(), 
2279                                         G_DIR_SEPARATOR_S, file);
2280                 else
2281                         afile=g_strdup(file);
2282                 if (move_file(tmpfile, file, TRUE) != 0) {
2283                         log_error(LOG_PROTOCOL, _("Couldn't export free/busy to '%s'\n"),
2284                                 afile);
2285                         res = FALSE;
2286                 }
2287                 g_free(afile);
2288                 g_free(file);
2289         } else if (file) {
2290                 FILE *fp = g_fopen(tmpfile, "rb");
2291                 if (!strncmp(file, "webcal", 6)) {
2292                         gchar *tmp = g_strdup_printf("http%s", file+6);
2293                         g_free(file);
2294                         file = tmp;
2295                 }
2296                 if (fp) {
2297                         res = vcal_curl_put(file, fp, filesize, user, pass);
2298                         fclose(fp);
2299                 }
2300                 g_free(file);
2301         }
2302         g_free(tmpfile);
2303         return res;
2304 }