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