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 "file-utils.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
avail_btn_can_be_sensitive(void)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
get_dtdate(const gchar * str,gint field)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
add_btn_cb(GtkButton * widget,gpointer data)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
remove_btn_cb(GtkButton * widget,gpointer data)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
attendee_add(VCalMeeting * meet,gchar * address,gchar * name,gchar * partstat,gchar * cutype,gboolean first)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
get_organizer(VCalMeeting * meet)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
get_organizer_name(VCalMeeting * meet)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
get_current_gmt_offset(void)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
get_gmt_offset_at_time(time_t then)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
get_date(VCalMeeting * meet,int start)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), <->tm_hour, <->tm_min);
452 } else {
453 gtkut_time_select_get_time(GTK_COMBO_BOX(meet->end_time), <->tm_hour, <->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
get_location(VCalMeeting * meet)466 static gchar *get_location(VCalMeeting *meet)
467 {
468 return gtk_editable_get_chars(GTK_EDITABLE(meet->location),0, -1);
469 }
470
get_summary(VCalMeeting * meet)471 static gchar *get_summary(VCalMeeting *meet)
472 {
473 return gtk_editable_get_chars(GTK_EDITABLE(meet->summary),0, -1);
474 }
475
get_description(VCalMeeting * meet)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
vcal_meeting_free(VCalMeeting * meet)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
destroy_meeting_cb(GtkWidget * widget,gpointer data)496 static void destroy_meeting_cb(GtkWidget *widget, gpointer data)
497 {
498 VCalMeeting *meet = (VCalMeeting *)data;
499 vcal_meeting_free(meet);
500 }
501
vcal_destroy(VCalMeeting * meet)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
meeting_key_pressed(GtkWidget * widget,GdkEventKey * event,gpointer data)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
meeting_start_changed(GtkWidget * widget,gpointer data)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
meeting_end_changed(GtkWidget * widget,gpointer data)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
att_update_icon(VCalMeeting * meet,VCalAttendee * attendee,gint avail,gchar * text)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
attendee_available(VCalAttendee * attendee,const gchar * dtstart,const gchar * dtend,const gchar * contents)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
get_avail_msg(const gchar * unavailable_persons,gboolean multiple,gboolean short_version,gint offset_before,gint offset_after)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
find_availability(const gchar * dtstart,const gchar * dtend,GSList * attendees,gboolean for_send,VCalMeeting * meet)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
check_attendees_availability(VCalMeeting * meet,gboolean tell_if_ok,gboolean for_send)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
check_avail_cb(GtkButton * widget,gpointer data)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
send_meeting_cb(GtkButton * widget,gpointer data)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, FALSE);
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, FALSE);
1305 }
1306
1307 return res;
1308 }
1309
vcal_meeting_create_real(VCalEvent * event,gboolean visible)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
vcal_meeting_create(VCalEvent * event)1642 VCalMeeting *vcal_meeting_create(VCalEvent *event)
1643 {
1644 return vcal_meeting_create_real(event, TRUE);
1645 }
1646
vcal_meeting_create_with_start(VCalEvent * event,struct tm * sdate)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
vcal_meeting_create_hidden(VCalEvent * event)1686 VCalMeeting *vcal_meeting_create_hidden(VCalEvent *event)
1687 {
1688 return vcal_meeting_create_real(event, FALSE);
1689 }
1690
vcal_meeting_send(VCalMeeting * meet)1691 gboolean vcal_meeting_send(VCalMeeting *meet)
1692 {
1693 return send_meeting_cb(NULL, meet);
1694 }
1695
vcal_meeting_alert_check(gpointer data)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
multisync_export(void)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%"G_GSIZE_FORMAT"-%d",
1828 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, TRUE);
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
vcal_meeting_export_calendar(const gchar * path,const gchar * user,const gchar * pass,gboolean automatic)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, TRUE);
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, TRUE) < 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, TRUE) < 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
vcal_meeting_export_freebusy(const gchar * path,const gchar * user,const gchar * pass)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, TRUE) < 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, TRUE) < 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 }
2148