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