No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * JP Rosevear <jpr@novell.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28 #include <glib/gi18n.h>
29 #include <libedataserverui/libedataserverui.h>
30 #include <libedataserver/libedataserver.h>
31
32 #include <e-util/e-util.h>
33 #include <e-util/e-unicode.h>
34 #include <calendar/gui/itip-utils.h>
35 #include <webkit/webkitdom.h>
36
37 #include <libevolution-utils/e-alert-dialog.h>
38 #include <e-util/e-mktemp.h>
39
40 #include <shell/e-shell.h>
41 #include <shell/e-shell-utils.h>
42
43 #include <libemail-utils/mail-mt.h>
44
45 #include <libemail-engine/mail-folder-cache.h>
46 #include <libemail-engine/mail-tools.h>
47
48 #include <mail/em-config.h>
49 #include <mail/em-utils.h>
50 #include <em-format/e-mail-formatter-utils.h>
51
52 #include <calendar/gui/itip-utils.h>
53
54 #include "e-conflict-search-selector.h"
55 #include "e-source-conflict-search.h"
56 #include "itip-view.h"
57 #include "e-mail-part-itip.h"
58
59 #define d(x)
60
61 #define MEETING_ICON "stock_new-meeting"
62
63 #define ITIP_VIEW_GET_PRIVATE(obj) \
64 (G_TYPE_INSTANCE_GET_PRIVATE \
65 ((obj), ITIP_TYPE_VIEW, ItipViewPrivate))
66
67 G_DEFINE_TYPE (ItipView, itip_view, G_TYPE_OBJECT)
68
69 typedef struct {
70 ItipViewInfoItemType type;
71 gchar *message;
72
73 guint id;
74 } ItipViewInfoItem;
75
76 struct _ItipViewPrivate {
77 ESourceRegistry *registry;
78 gulong source_added_id;
79 gulong source_removed_id;
80 gchar *extension_name;
81
82 ItipViewMode mode;
83 ECalClientSourceType type;
84
85 gchar *sender;
86 gchar *organizer;
87 gchar *organizer_sentby;
88 gchar *delegator;
89 gchar *attendee;
90 gchar *attendee_sentby;
91 gchar *proxy;
92
93 gchar *summary;
94
95 gchar *location;
96 gchar *status;
97 gchar *comment;
98
99 struct tm *start_tm;
100 gint start_tm_is_date : 1;
101 gchar *start_label;
102 const gchar *start_header;
103
104 struct tm *end_tm;
105 gint end_tm_is_date : 1;
106 gchar *end_label;
107 const gchar *end_header;
108
109 GSList *upper_info_items;
110 GSList *lower_info_items;
111
112 guint next_info_item_id;
113
114 gchar *description;
115
116 gint buttons_sensitive : 1;
117
118 gboolean is_recur_set;
119
120 gint needs_decline : 1;
121
122 WebKitDOMDocument *dom_document;
123 EMailPartItip *itip_part;
124
125 gchar *error;
126 };
127
128 #define TEXT_ROW_SENDER "text_row_sender"
129 #define TABLE_ROW_SUMMARY "table_row_summary"
130 #define TABLE_ROW_LOCATION "table_row_location"
131 #define TABLE_ROW_START_DATE "table_row_start_time"
132 #define TABLE_ROW_END_DATE "table_row_end_time"
133 #define TABLE_ROW_STATUS "table_row_status"
134 #define TABLE_ROW_COMMENT "table_row_comment"
135 #define TABLE_ROW_DESCRIPTION "table_row_description"
136 #define TABLE_ROW_RSVP_COMMENT "table_row_rsvp_comment"
137 #define TABLE_ROW_ESCB "table_row_escb"
138 #define TABLE_ROW_BUTTONS "table_row_buttons"
139 #define TABLE_ROW_ESCB_LABEL "table_row_escb_label"
140
141 #define TABLE_BUTTONS "table_buttons"
142
143 #define SELECT_ESOURCE "select_esource"
144 #define TEXTAREA_RSVP_COMMENT "textarea_rsvp_comment"
145
146 #define CHECKBOX_RSVP "checkbox_rsvp"
147 #define CHECKBOX_RECUR "checkbox_recur"
148 #define CHECKBOX_UPDATE "checkbox_update"
149 #define CHECKBOX_FREE_TIME "checkbox_free_time"
150 #define CHECKBOX_KEEP_ALARM "checkbox_keep_alarm"
151 #define CHECKBOX_INHERIT_ALARM "checkbox_inherit_alarm"
152
153 #define BUTTON_OPEN_CALENDAR "button_open_calendar"
154 #define BUTTON_DECLINE "button_decline"
155 #define BUTTON_DECLINE_ALL "button_decline_all"
156 #define BUTTON_ACCEPT "button_accept"
157 #define BUTTON_ACCEPT_ALL "button_accept_all"
158 #define BUTTON_TENTATIVE "button_tentative"
159 #define BUTTON_TENTATIVE_ALL "button_tentative_all"
160 #define BUTTON_SEND_INFORMATION "button_send_information"
161 #define BUTTON_UPDATE "button_update"
162 #define BUTTON_UPDATE_ATTENDEE_STATUS "button_update_attendee_status"
163 #define BUTTON_SAVE "button_save"
164
165 #define TABLE_UPPER_ITIP_INFO "table_upper_itip_info"
166 #define TABLE_LOWER_ITIP_INFO "table_lower_itip_info"
167
168 #define DIV_ITIP_CONTENT "div_itip_content"
169 #define DIV_ITIP_ERROR "div_itip_error"
170
171 enum {
172 PROP_0,
173 PROP_EXTENSION_NAME,
174 PROP_REGISTRY
175 };
176
177 enum {
178 SOURCE_SELECTED,
179 RESPONSE,
180 LAST_SIGNAL
181 };
182
183 static guint signals[LAST_SIGNAL] = { 0 };
184
185 static void
186 format_date_and_time_x (struct tm *date_tm,
187 struct tm *current_tm,
188 gboolean use_24_hour_format,
189 gboolean show_midnight,
190 gboolean show_zero_seconds,
191 gboolean is_date,
192 gchar *buffer,
193 gint buffer_size)
194 {
195 gchar *format;
196 struct tm tomorrow_tm, week_tm;
197
198 /* Calculate a normalized "tomorrow" */
199 tomorrow_tm = *current_tm;
200 /* Don't need this if date is in the past. Also, year assumption won't fail. */
201 if (date_tm->tm_year >= current_tm->tm_year && tomorrow_tm.tm_mday == time_days_in_month (current_tm->tm_year + 1900, current_tm->tm_mon)) {
202 tomorrow_tm.tm_mday = 1;
203 if (tomorrow_tm.tm_mon == 11) {
204 tomorrow_tm.tm_mon = 1;
205 tomorrow_tm.tm_year++;
206 } else {
207 tomorrow_tm.tm_mon++;
208 }
209 } else {
210 tomorrow_tm.tm_mday++;
211 }
212
213 /* Calculate a normalized "next seven days" */
214 week_tm = *current_tm;
215 /* Don't need this if date is in the past. Also, year assumption won't fail. */
216 if (date_tm->tm_year >= current_tm->tm_year && week_tm.tm_mday + 6 > time_days_in_month (date_tm->tm_year + 1900, date_tm->tm_mon)) {
217 week_tm.tm_mday = (week_tm.tm_mday + 6) % time_days_in_month (date_tm->tm_year + 1900, date_tm->tm_mon);
218 if (week_tm.tm_mon == 11) {
219 week_tm.tm_mon = 1;
220 week_tm.tm_year++;
221 } else {
222 week_tm.tm_mon++;
223 }
224 } else {
225 week_tm.tm_mday += 6;
226 }
227
228 /* Today */
229 if (date_tm->tm_mday == current_tm->tm_mday &&
230 date_tm->tm_mon == current_tm->tm_mon &&
231 date_tm->tm_year == current_tm->tm_year) {
232 if (is_date || (!show_midnight && date_tm->tm_hour == 0
233 && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
234 /* strftime format of a weekday and a date. */
235 format = _("Today");
236 } else if (use_24_hour_format) {
237 if (!show_zero_seconds && date_tm->tm_sec == 0)
238 /* strftime format of a time,
239 * in 24-hour format, without seconds. */
240 format = _("Today %H:%M");
241 else
242 /* strftime format of a time,
243 * in 24-hour format. */
244 format = _("Today %H:%M:%S");
245 } else {
246 if (!show_zero_seconds && date_tm->tm_sec == 0)
247 /* strftime format of a time,
248 * in 12-hour format, without seconds. */
249 format = _("Today %l:%M %p");
250 else
251 /* strftime format of a time,
252 * in 12-hour format. */
253 format = _("Today %l:%M:%S %p");
254 }
255
256 /* Tomorrow */
257 } else if (date_tm->tm_mday == tomorrow_tm.tm_mday &&
258 date_tm->tm_mon == tomorrow_tm.tm_mon &&
259 date_tm->tm_year == tomorrow_tm.tm_year) {
260 if (is_date || (!show_midnight && date_tm->tm_hour == 0
261 && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
262 /* strftime format of a weekday and a date. */
263 format = _("Tomorrow");
264 } else if (use_24_hour_format) {
265 if (!show_zero_seconds && date_tm->tm_sec == 0)
266 /* strftime format of a time,
267 * in 24-hour format, without seconds. */
268 format = _("Tomorrow %H:%M");
269 else
270 /* strftime format of a time,
271 * in 24-hour format. */
272 format = _("Tomorrow %H:%M:%S");
273 } else {
274 if (!show_zero_seconds && date_tm->tm_sec == 0)
275 /* strftime format of a time,
276 * in 12-hour format, without seconds. */
277 format = _("Tomorrow %l:%M %p");
278 else
279 /* strftime format of a time,
280 * in 12-hour format. */
281 format = _("Tomorrow %l:%M:%S %p");
282 }
283
284 /* Within 6 days */
285 } else if ((date_tm->tm_year >= current_tm->tm_year &&
286 date_tm->tm_mon >= current_tm->tm_mon &&
287 date_tm->tm_mday >= current_tm->tm_mday) &&
288
289 (date_tm->tm_year < week_tm.tm_year ||
290
291 (date_tm->tm_year == week_tm.tm_year &&
292 date_tm->tm_mon < week_tm.tm_mon) ||
293
294 (date_tm->tm_year == week_tm.tm_year &&
295 date_tm->tm_mon == week_tm.tm_mon &&
296 date_tm->tm_mday < week_tm.tm_mday))) {
297 if (is_date || (!show_midnight && date_tm->tm_hour == 0
298 && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
299 /* strftime format of a weekday. */
300 format = _("%A");
301 } else if (use_24_hour_format) {
302 if (!show_zero_seconds && date_tm->tm_sec == 0)
303 /* strftime format of a weekday and a
304 * time, in 24-hour format, without seconds. */
305 format = _("%A %H:%M");
306 else
307 /* strftime format of a weekday and a
308 * time, in 24-hour format. */
309 format = _("%A %H:%M:%S");
310 } else {
311 if (!show_zero_seconds && date_tm->tm_sec == 0)
312 /* strftime format of a weekday and a
313 * time, in 12-hour format, without seconds. */
314 format = _("%A %l:%M %p");
315 else
316 /* strftime format of a weekday and a
317 * time, in 12-hour format. */
318 format = _("%A %l:%M:%S %p");
319 }
320
321 /* This Year */
322 } else if (date_tm->tm_year == current_tm->tm_year) {
323 if (is_date || (!show_midnight && date_tm->tm_hour == 0
324 && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
325 /* strftime format of a weekday and a date
326 * without a year. */
327 format = _("%A, %B %e");
328 } else if (use_24_hour_format) {
329 if (!show_zero_seconds && date_tm->tm_sec == 0)
330 /* strftime format of a weekday, a date
331 * without a year and a time,
332 * in 24-hour format, without seconds. */
333 format = _("%A, %B %e %H:%M");
334 else
335 /* strftime format of a weekday, a date without a year
336 * and a time, in 24-hour format. */
337 format = _("%A, %B %e %H:%M:%S");
338 } else {
339 if (!show_zero_seconds && date_tm->tm_sec == 0)
340 /* strftime format of a weekday, a date without a year
341 * and a time, in 12-hour format, without seconds. */
342 format = _("%A, %B %e %l:%M %p");
343 else
344 /* strftime format of a weekday, a date without a year
345 * and a time, in 12-hour format. */
346 format = _("%A, %B %e %l:%M:%S %p");
347 }
348 } else {
349 if (is_date || (!show_midnight && date_tm->tm_hour == 0
350 && date_tm->tm_min == 0 && date_tm->tm_sec == 0)) {
351 /* strftime format of a weekday and a date. */
352 format = _("%A, %B %e, %Y");
353 } else if (use_24_hour_format) {
354 if (!show_zero_seconds && date_tm->tm_sec == 0)
355 /* strftime format of a weekday, a date and a
356 * time, in 24-hour format, without seconds. */
357 format = _("%A, %B %e, %Y %H:%M");
358 else
359 /* strftime format of a weekday, a date and a
360 * time, in 24-hour format. */
361 format = _("%A, %B %e, %Y %H:%M:%S");
362 } else {
363 if (!show_zero_seconds && date_tm->tm_sec == 0)
364 /* strftime format of a weekday, a date and a
365 * time, in 12-hour format, without seconds. */
366 format = _("%A, %B %e, %Y %l:%M %p");
367 else
368 /* strftime format of a weekday, a date and a
369 * time, in 12-hour format. */
370 format = _("%A, %B %e, %Y %l:%M:%S %p");
371 }
372 }
373
374 /* strftime returns 0 if the string doesn't fit, and leaves the buffer
375 * undefined, so we set it to the empty string in that case. */
376 if (e_utf8_strftime_fix_am_pm (buffer, buffer_size, format, date_tm) == 0)
377 buffer[0] = '\0';
378 }
379
380 static gchar *
381 dupe_first_bold (const gchar *format,
382 const gchar *first,
383 const gchar *second)
384 {
385 gchar *f, *s, *res;
386
387 f = g_markup_printf_escaped ("<b>%s</b>", first ? first : "");
388 s = g_markup_escape_text (second ? second : "", -1);
389
390 res = g_strdup_printf (format, f, s);
391
392 g_free (f);
393 g_free (s);
394
395 return res;
396 }
397
398 static gchar *
399 set_calendar_sender_text (ItipView *view)
400 {
401 ItipViewPrivate *priv;
402 const gchar *organizer, *attendee;
403 gchar *sender = NULL;
404 gchar *on_behalf_of = NULL;
405
406 priv = view->priv;
407
408 organizer = priv->organizer ? priv->organizer : _("An unknown person");
409 attendee = priv->attendee ? priv->attendee : _("An unknown person");
410
411 /* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
412 if (priv->organizer && priv->proxy)
413 on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
414 else if (priv->attendee && priv->proxy)
415 on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);
416
417 switch (priv->mode) {
418 case ITIP_VIEW_MODE_PUBLISH:
419 if (priv->organizer_sentby)
420 sender = dupe_first_bold (_("%s through %s has published the following meeting information:"), organizer, priv->organizer_sentby);
421 else
422 sender = dupe_first_bold (_("%s has published the following meeting information:"), organizer, NULL);
423 break;
424 case ITIP_VIEW_MODE_REQUEST:
425 /* FIXME is the delegator stuff handled correctly here? */
426 if (priv->delegator) {
427 sender = dupe_first_bold (_("%s has delegated the following meeting to you:"), priv->delegator, NULL);
428 } else {
429 if (priv->organizer_sentby)
430 sender = dupe_first_bold (_("%s through %s requests your presence at the following meeting:"), organizer, priv->organizer_sentby);
431 else
432 sender = dupe_first_bold (_("%s requests your presence at the following meeting:"), organizer, NULL);
433 }
434 break;
435 case ITIP_VIEW_MODE_ADD:
436 /* FIXME What text for this? */
437 if (priv->organizer_sentby)
438 sender = dupe_first_bold (_("%s through %s wishes to add to an existing meeting:"), organizer, priv->organizer_sentby);
439 else
440 sender = dupe_first_bold (_("%s wishes to add to an existing meeting:"), organizer, NULL);
441 break;
442 case ITIP_VIEW_MODE_REFRESH:
443 if (priv->attendee_sentby)
444 sender = dupe_first_bold (_("%s through %s wishes to receive the latest information for the following meeting:"), attendee, priv->attendee_sentby);
445 else
446 sender = dupe_first_bold (_("%s wishes to receive the latest information for the following meeting:"), attendee, NULL);
447 break;
448 case ITIP_VIEW_MODE_REPLY:
449 if (priv->attendee_sentby)
450 sender = dupe_first_bold (_("%s through %s has sent back the following meeting response:"), attendee, priv->attendee_sentby);
451 else
452 sender = dupe_first_bold (_("%s has sent back the following meeting response:"), attendee, NULL);
453 break;
454 case ITIP_VIEW_MODE_CANCEL:
455 if (priv->organizer_sentby)
456 sender = dupe_first_bold (_("%s through %s has canceled the following meeting:"), organizer, priv->organizer_sentby);
457 else
458 sender = dupe_first_bold (_("%s has canceled the following meeting:"), organizer, NULL);
459 break;
460 case ITIP_VIEW_MODE_COUNTER:
461 if (priv->attendee_sentby)
462 sender = dupe_first_bold (_("%s through %s has proposed the following meeting changes."), attendee, priv->attendee_sentby);
463 else
464 sender = dupe_first_bold (_("%s has proposed the following meeting changes:"), attendee, NULL);
465 break;
466 case ITIP_VIEW_MODE_DECLINECOUNTER:
467 if (priv->organizer_sentby)
468 sender = dupe_first_bold (_("%s through %s has declined the following meeting changes:"), organizer, priv->organizer_sentby);
469 else
470 sender = dupe_first_bold (_("%s has declined the following meeting changes:"), organizer, NULL);
471 break;
472 default:
473 break;
474 }
475
476 if (sender && on_behalf_of) {
477 gchar *tmp;
478 tmp = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
479 g_free (sender);
480 sender = tmp;
481 }
482
483 g_free (on_behalf_of);
484
485 return sender;
486 }
487
488 static gchar *
489 set_tasklist_sender_text (ItipView *view)
490 {
491 ItipViewPrivate *priv;
492 const gchar *organizer, *attendee;
493 gchar *sender = NULL;
494 gchar *on_behalf_of = NULL;
495
496 priv = view->priv;
497
498 organizer = priv->organizer ? priv->organizer : _("An unknown person");
499 attendee = priv->attendee ? priv->attendee : _("An unknown person");
500
501 /* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
502 if (priv->organizer && priv->proxy)
503 on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
504 else if (priv->attendee && priv->proxy)
505 on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);
506
507 switch (priv->mode) {
508 case ITIP_VIEW_MODE_PUBLISH:
509 if (priv->organizer_sentby)
510 sender = dupe_first_bold (_("%s through %s has published the following task:"), organizer, priv->organizer_sentby);
511 else
512 sender = dupe_first_bold (_("%s has published the following task:"), organizer, NULL);
513 break;
514 case ITIP_VIEW_MODE_REQUEST:
515 /* FIXME is the delegator stuff handled correctly here? */
516 if (priv->delegator) {
517 sender = dupe_first_bold (_("%s requests the assignment of %s to the following task:"), organizer, priv->delegator);
518 } else {
519 if (priv->organizer_sentby)
520 sender = dupe_first_bold (_("%s through %s has assigned you a task:"), organizer, priv->organizer_sentby);
521 else
522 sender = dupe_first_bold (_("%s has assigned you a task:"), organizer, NULL);
523 }
524 break;
525 case ITIP_VIEW_MODE_ADD:
526 /* FIXME What text for this? */
527 if (priv->organizer_sentby)
528 sender = dupe_first_bold (_("%s through %s wishes to add to an existing task:"), organizer, priv->organizer_sentby);
529 else
530 sender = dupe_first_bold (_("%s wishes to add to an existing task:"), organizer, NULL);
531 break;
532 case ITIP_VIEW_MODE_REFRESH:
533 if (priv->attendee_sentby)
534 sender = dupe_first_bold (_("%s through %s wishes to receive the latest information for the following assigned task:"), attendee, priv->attendee_sentby);
535 else
536 sender = dupe_first_bold (_("%s wishes to receive the latest information for the following assigned task:"), attendee, NULL);
537 break;
538 case ITIP_VIEW_MODE_REPLY:
539 if (priv->attendee_sentby)
540 sender = dupe_first_bold (_("%s through %s has sent back the following assigned task response:"), attendee, priv->attendee_sentby);
541 else
542 sender = dupe_first_bold (_("%s has sent back the following assigned task response:"), attendee, NULL);
543 break;
544 case ITIP_VIEW_MODE_CANCEL:
545 if (priv->organizer_sentby)
546 sender = dupe_first_bold (_("%s through %s has canceled the following assigned task:"), organizer, priv->organizer_sentby);
547 else
548 sender = dupe_first_bold (_("%s has canceled the following assigned task:"), organizer, NULL);
549 break;
550 case ITIP_VIEW_MODE_COUNTER:
551 if (priv->attendee_sentby)
552 sender = dupe_first_bold (_("%s through %s has proposed the following task assignment changes:"), attendee, priv->attendee_sentby);
553 else
554 sender = dupe_first_bold (_("%s has proposed the following task assignment changes:"), attendee, NULL);
555 break;
556 case ITIP_VIEW_MODE_DECLINECOUNTER:
557 if (priv->organizer_sentby)
558 sender = dupe_first_bold (_("%s through %s has declined the following assigned task:"), organizer, priv->organizer_sentby);
559 else
560 sender = dupe_first_bold (_("%s has declined the following assigned task:"), organizer, NULL);
561 break;
562 default:
563 break;
564 }
565
566 if (sender && on_behalf_of) {
567 gchar *tmp;
568 tmp = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
569 g_free (sender);
570 sender = tmp;
571 }
572
573 g_free (on_behalf_of);
574
575 return sender;
576 }
577
578 static gchar *
579 set_journal_sender_text (ItipView *view)
580 {
581 ItipViewPrivate *priv;
582 const gchar *organizer;
583 gchar *sender = NULL;
584 gchar *on_behalf_of = NULL;
585
586 priv = view->priv;
587
588 organizer = priv->organizer ? priv->organizer : _("An unknown person");
589
590 /* The current account ID (i.e. the delegatee) is receiving a copy of the request/response. Here we ask the delegatee to respond/accept on behalf of the delegator. */
591 if (priv->organizer && priv->proxy)
592 on_behalf_of = dupe_first_bold (_("Please respond on behalf of %s"), priv->proxy, NULL);
593 else if (priv->attendee && priv->proxy)
594 on_behalf_of = dupe_first_bold (_("Received on behalf of %s"), priv->proxy, NULL);
595
596 switch (priv->mode) {
597 case ITIP_VIEW_MODE_PUBLISH:
598 if (priv->organizer_sentby)
599 sender = dupe_first_bold (_("%s through %s has published the following memo:"), organizer, priv->organizer_sentby);
600 else
601 sender = dupe_first_bold (_("%s has published the following memo:"), organizer, NULL);
602 break;
603 case ITIP_VIEW_MODE_ADD:
604 /* FIXME What text for this? */
605 if (priv->organizer_sentby)
606 sender = dupe_first_bold (_("%s through %s wishes to add to an existing memo:"), organizer, priv->organizer_sentby);
607 else
608 sender = dupe_first_bold (_("%s wishes to add to an existing memo:"), organizer, NULL);
609 break;
610 case ITIP_VIEW_MODE_CANCEL:
611 if (priv->organizer_sentby)
612 sender = dupe_first_bold (_("%s through %s has canceled the following shared memo:"), organizer, priv->organizer_sentby);
613 else
614 sender = dupe_first_bold (_("%s has canceled the following shared memo:"), organizer, NULL);
615 break;
616 default:
617 break;
618 }
619
620 if (sender && on_behalf_of)
621 sender = g_strjoin (NULL, sender, "\n", on_behalf_of, NULL);
622
623 g_free (on_behalf_of);
624
625 return sender;
626 }
627
628 static void
629 set_sender_text (ItipView *view)
630 {
631 ItipViewPrivate *priv;
632 priv = view->priv;
633
634 if (priv->sender)
635 g_free (priv->sender);
636
637 switch (priv->type) {
638 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
639 priv->sender = set_calendar_sender_text (view);
640 break;
641 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
642 priv->sender = set_tasklist_sender_text (view);
643 break;
644 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
645 priv->sender = set_journal_sender_text (view);
646 break;
647 default:
648 priv->sender = NULL;
649 break;
650 }
651
652 if (priv->sender && priv->dom_document) {
653 WebKitDOMElement *div;
654
655 div = webkit_dom_document_get_element_by_id (
656 priv->dom_document, TEXT_ROW_SENDER);
657 webkit_dom_html_element_set_inner_html (
658 WEBKIT_DOM_HTML_ELEMENT (div), priv->sender, NULL);
659 }
660 }
661
662 static void
663 update_start_end_times (ItipView *view)
664 {
665 ItipViewPrivate *priv;
666 WebKitDOMElement *row, *col;
667 gchar buffer[256];
668 time_t now;
669 struct tm *now_tm;
670
671 priv = view->priv;
672
673 now = time (NULL);
674 now_tm = localtime (&now);
675
676 if (priv->start_label)
677 g_free (priv->start_label);
678 if (priv->end_label)
679 g_free (priv->end_label);
680
681 #define is_same(_member) (priv->start_tm->_member == priv->end_tm->_member)
682 if (priv->start_tm && priv->end_tm && priv->start_tm_is_date && priv->end_tm_is_date
683 && is_same (tm_mday) && is_same (tm_mon) && is_same (tm_year)) {
684 /* it's an all day event in one particular day */
685 format_date_and_time_x (priv->start_tm, now_tm, FALSE, TRUE, FALSE, priv->start_tm_is_date, buffer, 256);
686 priv->start_label = g_strdup (buffer);
687 priv->start_header = _("All day:");
688 priv->end_header = NULL;
689 priv->end_label = NULL;
690 } else {
691 if (priv->start_tm) {
692 format_date_and_time_x (priv->start_tm, now_tm, FALSE, TRUE, FALSE, priv->start_tm_is_date, buffer, 256);
693 priv->start_header = priv->start_tm_is_date ? _("Start day:") : _("Start time:");
694 priv->start_label = g_strdup (buffer);
695 } else {
696 priv->start_header = NULL;
697 priv->start_label = NULL;
698 }
699
700 if (priv->end_tm) {
701 format_date_and_time_x (priv->end_tm, now_tm, FALSE, TRUE, FALSE, priv->end_tm_is_date, buffer, 256);
702 priv->end_header = priv->end_tm_is_date ? _("End day:") : _("End time:");
703 priv->end_label = g_strdup (buffer);
704 } else {
705 priv->end_header = NULL;
706 priv->end_label = NULL;
707 }
708 }
709 #undef is_same
710
711 if (priv->dom_document) {
712 row = webkit_dom_document_get_element_by_id (
713 priv->dom_document, TABLE_ROW_START_DATE);
714 if (priv->start_header && priv->start_label) {
715 webkit_dom_html_element_set_hidden (
716 WEBKIT_DOM_HTML_ELEMENT (row), FALSE);
717
718 col = webkit_dom_element_get_first_element_child (row);
719 webkit_dom_html_element_set_inner_html (
720 WEBKIT_DOM_HTML_ELEMENT (col), priv->start_header, NULL);
721
722 col = webkit_dom_element_get_last_element_child (row);
723 webkit_dom_html_element_set_inner_html (
724 WEBKIT_DOM_HTML_ELEMENT (col), priv->start_label, NULL);
725 } else {
726 webkit_dom_html_element_set_hidden (
727 WEBKIT_DOM_HTML_ELEMENT (row), TRUE);
728 }
729
730 row = webkit_dom_document_get_element_by_id (
731 priv->dom_document, TABLE_ROW_END_DATE);
732 if (priv->end_header && priv->end_label) {
733 webkit_dom_html_element_set_hidden (
734 WEBKIT_DOM_HTML_ELEMENT (row), FALSE);
735
736 col = webkit_dom_element_get_first_element_child (row);
737 webkit_dom_html_element_set_inner_html (
738 WEBKIT_DOM_HTML_ELEMENT (col), priv->end_header, NULL);
739
740 col = webkit_dom_element_get_last_element_child (row);
741 webkit_dom_html_element_set_inner_html (
742 WEBKIT_DOM_HTML_ELEMENT (col), priv->end_label, NULL);
743 } else {
744 webkit_dom_html_element_set_hidden (
745 WEBKIT_DOM_HTML_ELEMENT (row), TRUE);
746 }
747 }
748 }
749
750 static void
751 button_clicked_cb (WebKitDOMElement *element,
752 WebKitDOMEvent *event,
753 gpointer data)
754 {
755 ItipViewResponse response;
756 gchar *responseStr;
757
758 responseStr = webkit_dom_html_button_element_get_value (
759 WEBKIT_DOM_HTML_BUTTON_ELEMENT (element));
760
761 response = atoi (responseStr);
762
763 g_signal_emit (data, signals[RESPONSE], 0, response);
764 }
765
766 static void
767 rsvp_toggled_cb (WebKitDOMHTMLInputElement *input,
768 WebKitDOMEvent *event,
769 gpointer data)
770 {
771 WebKitDOMElement *el;
772
773 ItipView *view = data;
774 gboolean rsvp;
775
776 rsvp = webkit_dom_html_input_element_get_checked (input);
777
778 el = webkit_dom_document_get_element_by_id (
779 view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
780 webkit_dom_html_text_area_element_set_disabled (
781 WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp);
782 }
783
784 static void
785 recur_toggled_cb (WebKitDOMHTMLInputElement *input,
786 WebKitDOMEvent *event,
787 gpointer data)
788 {
789 ItipView *view = data;
790
791 itip_view_set_mode (view, view->priv->mode);
792 }
793
794 /*
795 alarm_check_toggled_cb
796 check1 was changed, so make the second available based on state of the first check.
797 */
798 static void
799 alarm_check_toggled_cb (WebKitDOMHTMLInputElement *check1,
800 WebKitDOMEvent *event,
801 ItipView *view)
802 {
803 WebKitDOMElement *check2;
804 gchar *id = webkit_dom_html_element_get_id (WEBKIT_DOM_HTML_ELEMENT (check1));
805
806 if (g_strcmp0 (id, CHECKBOX_INHERIT_ALARM)) {
807 check2 = webkit_dom_document_get_element_by_id (
808 view->priv->dom_document, CHECKBOX_KEEP_ALARM);
809 } else {
810 check2 = webkit_dom_document_get_element_by_id (
811 view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
812 }
813
814 g_free (id);
815
816 webkit_dom_html_input_element_set_disabled (
817 WEBKIT_DOM_HTML_INPUT_ELEMENT (check2),
818 (webkit_dom_html_element_get_hidden (
819 WEBKIT_DOM_HTML_ELEMENT (check1)) &&
820 webkit_dom_html_input_element_get_checked (check1)));
821 }
822
823 static void
824 source_changed_cb (WebKitDOMElement *select,
825 WebKitDOMEvent *event,
826 ItipView *view)
827 {
828 ESource *source;
829
830 source = itip_view_ref_source (view);
831
832 d (printf ("Source changed to '%s'\n", e_source_get_display_name (source)));
833 g_signal_emit (view, signals[SOURCE_SELECTED], 0, source);
834
835 g_object_unref (source);
836 }
837
838 static void
839 append_checkbox_table_row (GString *buffer,
840 const gchar *name,
841 const gchar *label)
842 {
843 gchar *access_key, *html_label;
844
845 html_label = e_mail_formatter_parse_html_mnemonics (label, &access_key);
846
847 g_string_append_printf (
848 buffer,
849 "<tr id=\"table_row_%s\" hidden=\"\"><td colspan=\"2\">"
850 "<input type=\"checkbox\" name=\"%s\" id=\"%s\" value=\"%s\" >"
851 "<label for=\"%s\" accesskey=\"%s\">%s</label>"
852 "</td></tr>\n",
853 name, name, name, name, name,
854 access_key ? access_key : "", html_label);
855
856 g_free (html_label);
857
858 if (access_key)
859 g_free (access_key);
860 }
861
862 static void
863 append_text_table_row (GString *buffer,
864 const gchar *id,
865 const gchar *label,
866 const gchar *value)
867 {
868 if (label && *label) {
869
870 g_string_append_printf (
871 buffer,
872 "<tr id=\"%s\" %s><th>%s</th><td>%s</td></tr>\n",
873 id, (value && *value) ? "" : "hidden=\"\"", label, value ? value : "");
874
875 } else {
876
877 g_string_append_printf (
878 buffer,
879 "<tr id=\"%s\"%s><td colspan=\"2\">%s</td></tr>\n",
880 id, g_strcmp0 (id, TABLE_ROW_SUMMARY) == 0 ? "" : " hidden=\"\"", value ? value : "");
881
882 }
883 }
884
885 static void
886 append_text_table_row_nonempty (GString *buffer,
887 const gchar *id,
888 const gchar *label,
889 const gchar *value)
890 {
891 if (!value || !*value)
892 return;
893
894 append_text_table_row (buffer, id, label, value);
895 }
896
897 static void
898 append_info_item_row (ItipView *view,
899 const gchar *table_id,
900 ItipViewInfoItem *item)
901 {
902 WebKitDOMElement *table;
903 WebKitDOMHTMLElement *row, *cell;
904 const gchar *icon_name;
905 gchar *id;
906
907 table = webkit_dom_document_get_element_by_id (
908 view->priv->dom_document, table_id);
909 row = webkit_dom_html_table_element_insert_row (
910 WEBKIT_DOM_HTML_TABLE_ELEMENT (table), -1, NULL);
911
912 id = g_strdup_printf ("%s_row_%d", table_id, item->id);
913 webkit_dom_html_element_set_id (row, id);
914 g_free (id);
915
916 switch (item->type) {
917 case ITIP_VIEW_INFO_ITEM_TYPE_INFO:
918 icon_name = GTK_STOCK_DIALOG_INFO;
919 break;
920 case ITIP_VIEW_INFO_ITEM_TYPE_WARNING:
921 icon_name = GTK_STOCK_DIALOG_WARNING;
922 break;
923 case ITIP_VIEW_INFO_ITEM_TYPE_ERROR:
924 icon_name = GTK_STOCK_DIALOG_ERROR;
925 break;
926 case ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS:
927 icon_name = GTK_STOCK_FIND;
928 break;
929 case ITIP_VIEW_INFO_ITEM_TYPE_NONE:
930 default:
931 icon_name = NULL;
932 }
933
934 cell = webkit_dom_html_table_row_element_insert_cell (
935 (WebKitDOMHTMLTableRowElement *) row, -1, NULL);
936
937 if (icon_name) {
938 WebKitDOMElement *image;
939 gchar *icon_uri;
940
941 image = webkit_dom_document_create_element (
942 view->priv->dom_document, "IMG", NULL);
943
944 icon_uri = g_strdup_printf ("gtk-stock://%s", icon_name);
945 webkit_dom_html_image_element_set_src (
946 WEBKIT_DOM_HTML_IMAGE_ELEMENT (image), icon_uri);
947 g_free (icon_uri);
948
949 webkit_dom_node_append_child (
950 WEBKIT_DOM_NODE (cell),
951 WEBKIT_DOM_NODE (image),
952 NULL);
953 }
954
955 cell = webkit_dom_html_table_row_element_insert_cell (
956 (WebKitDOMHTMLTableRowElement *) row, -1, NULL);
957
958 webkit_dom_html_element_set_inner_html (cell, item->message, NULL);
959
960 d (printf ("Added row %s_row_%d ('%s')\n", table_id, item->id, item->message));
961 }
962
963 static void
964 remove_info_item_row (ItipView *view,
965 const gchar *table_id,
966 guint id)
967 {
968 WebKitDOMElement *row;
969 gchar *row_id;
970
971 row_id = g_strdup_printf ("%s_row_%d", table_id, id);
972 row = webkit_dom_document_get_element_by_id (
973 view->priv->dom_document, row_id);
974 g_free (row_id);
975
976 webkit_dom_node_remove_child (
977 webkit_dom_node_get_parent_node (WEBKIT_DOM_NODE (row)),
978 WEBKIT_DOM_NODE (row),
979 NULL);
980
981 d (printf ("Removed row %s_row_%d\n", table_id, id));
982 }
983
984 static void
985 buttons_table_write_button (GString *buffer,
986 const gchar *name,
987 const gchar *label,
988 const gchar *icon,
989 ItipViewResponse response)
990 {
991 gchar *access_key, *html_label;
992
993 html_label = e_mail_formatter_parse_html_mnemonics (label, &access_key);
994
995 g_string_append_printf (
996 buffer,
997 "<td><button type=\"button\" name=\"%s\" value=\"%d\" id=\"%s\" accesskey=\"%s\" hidden disabled>"
998 "<div><img src=\"gtk-stock://%s?size=%d\"> <span>%s</span></div>"
999 "</button></td>\n",
1000 name, response, name, access_key ? access_key : "" , icon,
1001 GTK_ICON_SIZE_BUTTON, html_label);
1002
1003 g_free (html_label);
1004
1005 if (access_key)
1006 g_free (access_key);
1007 }
1008
1009 static void
1010 append_buttons_table (GString *buffer)
1011 {
1012 g_string_append (
1013 buffer,
1014 "<table class=\"itip buttons\" border=\"0\" "
1015 "id=\"" TABLE_BUTTONS "\" cellspacing=\"6\" "
1016 "cellpadding=\"0\" >"
1017 "<tr id=\"" TABLE_ROW_BUTTONS "\">");
1018
1019 /* Everything gets the open button */
1020 buttons_table_write_button (
1021 buffer, BUTTON_OPEN_CALENDAR, _("_Open Calendar"),
1022 GTK_STOCK_JUMP_TO, ITIP_VIEW_RESPONSE_OPEN);
1023 buttons_table_write_button (
1024 buffer, BUTTON_DECLINE_ALL, _("_Decline all"),
1025 GTK_STOCK_CANCEL, ITIP_VIEW_RESPONSE_DECLINE);
1026 buttons_table_write_button (
1027 buffer, BUTTON_DECLINE, _("_Decline"),
1028 GTK_STOCK_CANCEL, ITIP_VIEW_RESPONSE_DECLINE);
1029 buttons_table_write_button (
1030 buffer, BUTTON_TENTATIVE_ALL, _("_Tentative all"),
1031 GTK_STOCK_DIALOG_QUESTION, ITIP_VIEW_RESPONSE_TENTATIVE);
1032 buttons_table_write_button (
1033 buffer, BUTTON_TENTATIVE, _("_Tentative"),
1034 GTK_STOCK_DIALOG_QUESTION, ITIP_VIEW_RESPONSE_TENTATIVE);
1035 buttons_table_write_button (
1036 buffer, BUTTON_ACCEPT_ALL, _("A_ccept all"),
1037 GTK_STOCK_APPLY, ITIP_VIEW_RESPONSE_ACCEPT);
1038 buttons_table_write_button (
1039 buffer, BUTTON_ACCEPT, _("A_ccept"),
1040 GTK_STOCK_APPLY, ITIP_VIEW_RESPONSE_ACCEPT);
1041 buttons_table_write_button (
1042 buffer, BUTTON_SEND_INFORMATION, _("_Send Information"),
1043 GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_REFRESH);
1044 buttons_table_write_button (
1045 buffer, BUTTON_UPDATE_ATTENDEE_STATUS, _("_Update Attendee Status"),
1046 GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_UPDATE);
1047 buttons_table_write_button (
1048 buffer, BUTTON_UPDATE, _("_Update"),
1049 GTK_STOCK_REFRESH, ITIP_VIEW_RESPONSE_CANCEL);
1050
1051 g_string_append (buffer, "</tr></table>");
1052 }
1053
1054 static void
1055 itip_view_rebuild_source_list (ItipView *view)
1056 {
1057 ESourceRegistry *registry;
1058 WebKitDOMElement *select;
1059 GList *list, *link;
1060 const gchar *extension_name;
1061 GHashTable *groups;
1062
1063 d (printf ("Assigning a new source list!\n"));
1064
1065 if (!view->priv->dom_document)
1066 return;
1067
1068 registry = itip_view_get_registry (view);
1069 extension_name = itip_view_get_extension_name (view);
1070
1071 select = webkit_dom_document_get_element_by_id (
1072 view->priv->dom_document, SELECT_ESOURCE);
1073
1074 while (webkit_dom_node_has_child_nodes (WEBKIT_DOM_NODE (select))) {
1075 webkit_dom_node_remove_child (
1076 WEBKIT_DOM_NODE (select),
1077 webkit_dom_node_get_last_child (WEBKIT_DOM_NODE (select)),
1078 NULL);
1079 }
1080
1081 if (extension_name == NULL)
1082 return;
1083
1084 list = e_source_registry_list_sources (registry, extension_name);
1085 groups = g_hash_table_new_full (
1086 g_str_hash, g_str_equal,
1087 (GDestroyNotify) g_free, NULL);
1088 for (link = list; link != NULL; link = g_list_next (link)) {
1089 ESource *source = E_SOURCE (link->data);
1090 ESource *parent;
1091 WebKitDOMElement *option;
1092 WebKitDOMHTMLOptGroupElement *optgroup;
1093
1094 parent = e_source_registry_ref_source (
1095 registry, e_source_get_parent (source));
1096
1097 optgroup = g_hash_table_lookup (groups, e_source_get_uid (parent));
1098 if (!optgroup) {
1099 optgroup = WEBKIT_DOM_HTML_OPT_GROUP_ELEMENT (
1100 webkit_dom_document_create_element (
1101 view->priv->dom_document,
1102 "OPTGROUP", NULL));
1103 webkit_dom_html_opt_group_element_set_label (
1104 optgroup, e_source_get_display_name (parent));
1105 g_hash_table_insert (
1106 groups, g_strdup (e_source_get_uid (parent)), optgroup);
1107 }
1108 g_object_unref (parent);
1109
1110 option = webkit_dom_document_create_element (
1111 view->priv->dom_document, "OPTION", NULL);
1112 webkit_dom_html_option_element_set_value (
1113 WEBKIT_DOM_HTML_OPTION_ELEMENT (option),
1114 e_source_get_uid (source));
1115 webkit_dom_html_option_element_set_label (
1116 WEBKIT_DOM_HTML_OPTION_ELEMENT (option),
1117 e_source_get_display_name (source));
1118 webkit_dom_html_element_set_inner_html (
1119 WEBKIT_DOM_HTML_ELEMENT (option),
1120 e_source_get_display_name (source), NULL);
1121
1122 /* See https://bugzilla.gnome.org/show_bug.cgi?id=681400
1123 * FIXME: This can be removed once we require WebKitGtk 1.10+ */
1124 #if WEBKIT_CHECK_VERSION (1, 9, 6)
1125 webkit_dom_element_set_class_name (
1126 WEBKIT_DOM_ELEMENT (option), "calendar");
1127 #else
1128 webkit_dom_html_element_set_class_name (
1129 WEBKIT_DOM_HTML_ELEMENT (option), "calendar");
1130 #endif
1131
1132 if (!e_source_get_writable (source)) {
1133 webkit_dom_html_option_element_set_disabled (
1134 WEBKIT_DOM_HTML_OPTION_ELEMENT (option), TRUE);
1135 }
1136
1137 webkit_dom_node_append_child (
1138 WEBKIT_DOM_NODE (optgroup),
1139 WEBKIT_DOM_NODE (option),
1140 NULL);
1141 }
1142
1143 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1144
1145 list = g_hash_table_get_values (groups);
1146 for (link = list; link != NULL; link = g_list_next (link)) {
1147 WebKitDOMNode *optgroup = link->data;
1148
1149 webkit_dom_node_append_child (
1150 WEBKIT_DOM_NODE (select), optgroup, NULL);
1151 }
1152 g_list_free (list);
1153 g_hash_table_destroy (groups);
1154
1155 source_changed_cb (select, NULL, view);
1156 }
1157
1158 static void
1159 itip_view_source_added_cb (ESourceRegistry *registry,
1160 ESource *source,
1161 ItipView *view)
1162 {
1163 const gchar *extension_name;
1164
1165 extension_name = itip_view_get_extension_name (view);
1166
1167 /* If we don't have an extension name set
1168 * yet then disregard the signal emission. */
1169 if (extension_name == NULL)
1170 return;
1171
1172 if (e_source_has_extension (source, extension_name))
1173 itip_view_rebuild_source_list (view);
1174 }
1175
1176 static void
1177 itip_view_source_removed_cb (ESourceRegistry *registry,
1178 ESource *source,
1179 ItipView *view)
1180 {
1181 const gchar *extension_name;
1182
1183 extension_name = itip_view_get_extension_name (view);
1184
1185 /* If we don't have an extension name set
1186 * yet then disregard the signal emission. */
1187 if (extension_name == NULL)
1188 return;
1189
1190 if (e_source_has_extension (source, extension_name))
1191 itip_view_rebuild_source_list (view);
1192 }
1193
1194 static void
1195 itip_view_set_registry (ItipView *view,
1196 ESourceRegistry *registry)
1197 {
1198 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
1199 g_return_if_fail (view->priv->registry == NULL);
1200
1201 view->priv->registry = g_object_ref (registry);
1202 }
1203
1204 static void
1205 itip_view_set_property (GObject *object,
1206 guint property_id,
1207 const GValue *value,
1208 GParamSpec *pspec)
1209 {
1210 switch (property_id) {
1211 case PROP_EXTENSION_NAME:
1212 itip_view_set_extension_name (
1213 ITIP_VIEW (object),
1214 g_value_get_string (value));
1215 return;
1216
1217 case PROP_REGISTRY:
1218 itip_view_set_registry (
1219 ITIP_VIEW (object),
1220 g_value_get_object (value));
1221 return;
1222 }
1223
1224 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1225 }
1226
1227 static void
1228 itip_view_get_property (GObject *object,
1229 guint property_id,
1230 GValue *value,
1231 GParamSpec *pspec)
1232 {
1233 switch (property_id) {
1234 case PROP_EXTENSION_NAME:
1235 g_value_set_string (
1236 value, itip_view_get_extension_name (
1237 ITIP_VIEW (object)));
1238 return;
1239
1240 case PROP_REGISTRY:
1241 g_value_set_object (
1242 value, itip_view_get_registry (
1243 ITIP_VIEW (object)));
1244 return;
1245 }
1246
1247 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1248 }
1249
1250 static void
1251 itip_view_dispose (GObject *object)
1252 {
1253 ItipViewPrivate *priv;
1254
1255 priv = ITIP_VIEW_GET_PRIVATE (object);
1256
1257 if (priv->registry != NULL) {
1258 g_signal_handler_disconnect (
1259 priv->registry, priv->source_added_id);
1260 g_signal_handler_disconnect (
1261 priv->registry, priv->source_removed_id);
1262 g_object_unref (priv->registry);
1263 priv->registry = NULL;
1264 }
1265
1266 /* Chain up to parent's dispose() method. */
1267 G_OBJECT_CLASS (itip_view_parent_class)->dispose (object);
1268 }
1269
1270 static void
1271 itip_view_finalize (GObject *object)
1272 {
1273 ItipViewPrivate *priv;
1274 GSList *iter;
1275
1276 priv = ITIP_VIEW_GET_PRIVATE (object);
1277
1278 d (printf ("Itip view finalized!\n"));
1279
1280 g_clear_object (&priv->dom_document);
1281 g_free (priv->extension_name);
1282 g_free (priv->sender);
1283 g_free (priv->organizer);
1284 g_free (priv->organizer_sentby);
1285 g_free (priv->delegator);
1286 g_free (priv->attendee);
1287 g_free (priv->attendee_sentby);
1288 g_free (priv->proxy);
1289 g_free (priv->summary);
1290 g_free (priv->location);
1291 g_free (priv->status);
1292 g_free (priv->comment);
1293 g_free (priv->start_tm);
1294 g_free (priv->start_label);
1295 g_free (priv->end_tm);
1296 g_free (priv->end_label);
1297 g_free (priv->description);
1298 g_free (priv->error);
1299
1300 for (iter = priv->lower_info_items; iter; iter = iter->next) {
1301 ItipViewInfoItem *item = iter->data;
1302 g_free (item->message);
1303 g_free (item);
1304 }
1305
1306 g_slist_free (priv->lower_info_items);
1307
1308 for (iter = priv->upper_info_items; iter; iter = iter->next) {
1309 ItipViewInfoItem *item = iter->data;
1310 g_free (item->message);
1311 g_free (item);
1312 }
1313
1314 g_slist_free (priv->upper_info_items);
1315
1316 /* Chain up to parent's finalize() method. */
1317 G_OBJECT_CLASS (itip_view_parent_class)->finalize (object);
1318 }
1319
1320 static void
1321 itip_view_constructed (GObject *object)
1322 {
1323 ItipView *view;
1324 ESourceRegistry *registry;
1325
1326 view = ITIP_VIEW (object);
1327 registry = itip_view_get_registry (view);
1328
1329 view->priv->source_added_id = g_signal_connect (
1330 registry, "source-added",
1331 G_CALLBACK (itip_view_source_added_cb), view);
1332
1333 view->priv->source_removed_id = g_signal_connect (
1334 registry, "source-removed",
1335 G_CALLBACK (itip_view_source_removed_cb), view);
1336
1337 /* Chain up to parent's constructed() method. */
1338 G_OBJECT_CLASS (itip_view_parent_class)->constructed (object);
1339 }
1340
1341 static void
1342 itip_view_class_init (ItipViewClass *class)
1343 {
1344 GObjectClass *object_class;
1345
1346 g_type_class_add_private (class, sizeof (ItipViewPrivate));
1347
1348 object_class = G_OBJECT_CLASS (class);
1349 object_class->set_property = itip_view_set_property;
1350 object_class->get_property = itip_view_get_property;
1351 object_class->dispose = itip_view_dispose;
1352 object_class->finalize = itip_view_finalize;
1353 object_class->constructed = itip_view_constructed;
1354
1355 g_object_class_install_property (
1356 object_class,
1357 PROP_REGISTRY,
1358 g_param_spec_string (
1359 "extension-name",
1360 "Extension Name",
1361 "Show only data sources with this extension",
1362 NULL,
1363 G_PARAM_READWRITE));
1364
1365 g_object_class_install_property (
1366 object_class,
1367 PROP_REGISTRY,
1368 g_param_spec_object (
1369 "registry",
1370 "Registry",
1371 "Data source registry",
1372 E_TYPE_SOURCE_REGISTRY,
1373 G_PARAM_READWRITE |
1374 G_PARAM_CONSTRUCT_ONLY));
1375
1376 signals[SOURCE_SELECTED] = g_signal_new (
1377 "source_selected",
1378 G_TYPE_FROM_CLASS (class),
1379 G_SIGNAL_RUN_LAST,
1380 G_STRUCT_OFFSET (ItipViewClass, source_selected),
1381 NULL, NULL,
1382 g_cclosure_marshal_VOID__OBJECT,
1383 G_TYPE_NONE, 1,
1384 E_TYPE_SOURCE);
1385
1386 signals[RESPONSE] = g_signal_new (
1387 "response",
1388 G_TYPE_FROM_CLASS (class),
1389 G_SIGNAL_RUN_LAST,
1390 G_STRUCT_OFFSET (ItipViewClass, response),
1391 NULL, NULL,
1392 g_cclosure_marshal_VOID__INT,
1393 G_TYPE_NONE, 1,
1394 G_TYPE_INT);
1395 }
1396
1397 EMailPartItip *
1398 itip_view_get_mail_part (ItipView *view)
1399 {
1400 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1401
1402 return view->priv->itip_part;
1403 }
1404
1405 ESourceRegistry *
1406 itip_view_get_registry (ItipView *view)
1407 {
1408 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1409
1410 return view->priv->registry;
1411 }
1412
1413 const gchar *
1414 itip_view_get_extension_name (ItipView *view)
1415 {
1416 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1417
1418 return view->priv->extension_name;
1419 }
1420
1421 void
1422 itip_view_set_extension_name (ItipView *view,
1423 const gchar *extension_name)
1424 {
1425 g_return_if_fail (ITIP_IS_VIEW (view));
1426
1427 /* Avoid unnecessary rebuilds. */
1428 if (g_strcmp0 (extension_name, view->priv->extension_name) == 0)
1429 return;
1430
1431 g_free (view->priv->extension_name);
1432 view->priv->extension_name = g_strdup (extension_name);
1433
1434 g_object_notify (G_OBJECT (view), "extension-name");
1435
1436 itip_view_rebuild_source_list (view);
1437 }
1438
1439 void
1440 itip_view_write (EMailFormatter *formatter,
1441 GString *buffer)
1442 {
1443 gchar *header = e_mail_formatter_get_html_header (formatter);
1444 g_string_append (buffer, header);
1445 g_free (header);
1446
1447 g_string_append_printf (
1448 buffer,
1449 "<img src=\"gtk-stock://%s?size=%d\" class=\"itip icon\" />\n",
1450 MEETING_ICON, GTK_ICON_SIZE_BUTTON);
1451
1452 g_string_append (
1453 buffer,
1454 "<div class=\"itip content\" id=\"" DIV_ITIP_CONTENT "\">\n");
1455
1456 /* The first section listing the sender */
1457 /* FIXME What to do if the send and organizer do not match */
1458 g_string_append (
1459 buffer,
1460 "<div id=\"" TEXT_ROW_SENDER "\" class=\"itip sender\"></div>\n");
1461
1462 g_string_append (buffer, "<hr>\n");
1463
1464 /* Elementary event information */
1465 g_string_append (
1466 buffer,
1467 "<table class=\"itip table\" border=\"0\" "
1468 "cellspacing=\"5\" cellpadding=\"0\">\n");
1469
1470 append_text_table_row (buffer, TABLE_ROW_SUMMARY, NULL, NULL);
1471 append_text_table_row (buffer, TABLE_ROW_LOCATION, _("Location:"), NULL);
1472 append_text_table_row (buffer, TABLE_ROW_START_DATE, _("Start time:"), NULL);
1473 append_text_table_row (buffer, TABLE_ROW_END_DATE, _("End time:"), NULL);
1474 append_text_table_row (buffer, TABLE_ROW_STATUS, _("Status:"), NULL);
1475 append_text_table_row (buffer, TABLE_ROW_COMMENT, _("Comment:"), NULL);
1476
1477 g_string_append (buffer, "</table>\n");
1478
1479 /* Upper Info items */
1480 g_string_append (
1481 buffer,
1482 "<table class=\"itip info\" id=\"" TABLE_UPPER_ITIP_INFO "\" border=\"0\" "
1483 "cellspacing=\"5\" cellpadding=\"0\">");
1484
1485 /* Description */
1486 g_string_append (
1487 buffer,
1488 "<div id=\"" TABLE_ROW_DESCRIPTION "\" class=\"itip description\" hidden=\"\"></div>\n");
1489
1490 g_string_append (buffer, "<hr>\n");
1491
1492 /* Lower Info items */
1493 g_string_append (
1494 buffer,
1495 "<table class=\"itip info\" id=\"" TABLE_LOWER_ITIP_INFO "\" border=\"0\" "
1496 "cellspacing=\"5\" cellpadding=\"0\">");
1497
1498 g_string_append (
1499 buffer,
1500 "<table class=\"itip table\" border=\"0\" "
1501 "cellspacing=\"5\" cellpadding=\"0\">\n");
1502
1503 g_string_append (
1504 buffer,
1505 "<tr id=\"" TABLE_ROW_ESCB "\" hidden=\"\""">"
1506 "<th><label id=\"" TABLE_ROW_ESCB_LABEL "\" for=\"" SELECT_ESOURCE "\"></label></th>"
1507 "<td><select name=\"" SELECT_ESOURCE "\" id=\"" SELECT_ESOURCE "\"></select></td>"
1508 "</tr>\n");
1509
1510 /* RSVP area */
1511 append_checkbox_table_row (buffer, CHECKBOX_RSVP, _("Send reply to sender"));
1512
1513 /* Comments */
1514 g_string_append_printf (
1515 buffer,
1516 "<tr id=\"" TABLE_ROW_RSVP_COMMENT "\" hidden=\"\">"
1517 "<th>%s</th>"
1518 "<td><textarea name=\"" TEXTAREA_RSVP_COMMENT "\" "
1519 "id=\"" TEXTAREA_RSVP_COMMENT "\" "
1520 "rows=\"3\" cols=\"40\" disabled=\"\">"
1521 "</textarea></td>\n"
1522 "</tr>\n",
1523 _("Comment:"));
1524
1525 /* Updates */
1526 append_checkbox_table_row (buffer, CHECKBOX_UPDATE, _("Send _updates to attendees"));
1527
1528 /* The recurrence check button */
1529 append_checkbox_table_row (buffer, CHECKBOX_RECUR, _("_Apply to all instances"));
1530 append_checkbox_table_row (buffer, CHECKBOX_FREE_TIME, _("Show time as _free"));
1531 append_checkbox_table_row (buffer, CHECKBOX_KEEP_ALARM, _("_Preserve my reminder"));
1532 append_checkbox_table_row (buffer, CHECKBOX_INHERIT_ALARM, _("_Inherit reminder"));
1533
1534 g_string_append (buffer, "</table>\n");
1535
1536 /* Buttons table */
1537 append_buttons_table (buffer);
1538
1539 /* <div class="itip content" > */
1540 g_string_append (buffer, "</div>\n");
1541
1542 g_string_append (buffer, "<div class=\"itip error\" id=\"" DIV_ITIP_ERROR "\"></div>");
1543
1544 g_string_append (buffer, "</body></html>");
1545 }
1546
1547 void
1548 itip_view_write_for_printing (ItipView *view,
1549 GString *buffer)
1550 {
1551 if (view->priv->error && *view->priv->error) {
1552 g_string_append (buffer, view->priv->error);
1553 return;
1554 }
1555
1556 g_string_append (
1557 buffer,
1558 "<div class=\"itip print_content\" id=\"" DIV_ITIP_CONTENT "\">\n");
1559
1560 /* The first section listing the sender */
1561 if (view->priv->sender && *view->priv->sender) {
1562 /* FIXME What to do if the send and organizer do not match */
1563 g_string_append_printf (
1564 buffer,
1565 "<div id=\"" TEXT_ROW_SENDER "\" class=\"itip sender\">%s</div>\n",
1566 view->priv->sender);
1567
1568 g_string_append (buffer, "<hr>\n");
1569 }
1570
1571 /* Elementary event information */
1572 g_string_append (
1573 buffer,
1574 "<table class=\"itip table\" border=\"0\" "
1575 "cellspacing=\"5\" cellpadding=\"0\">\n");
1576
1577 append_text_table_row_nonempty (
1578 buffer, TABLE_ROW_SUMMARY,
1579 NULL, view->priv->summary);
1580 append_text_table_row_nonempty (
1581 buffer, TABLE_ROW_LOCATION,
1582 _("Location:"), view->priv->location);
1583 append_text_table_row_nonempty (
1584 buffer, TABLE_ROW_START_DATE,
1585 view->priv->start_header, view->priv->start_label);
1586 append_text_table_row_nonempty (
1587 buffer, TABLE_ROW_END_DATE,
1588 view->priv->end_header, view->priv->end_label);
1589 append_text_table_row_nonempty (
1590 buffer, TABLE_ROW_STATUS,
1591 _("Status:"), view->priv->status);
1592 append_text_table_row_nonempty (
1593 buffer, TABLE_ROW_COMMENT,
1594 _("Comment:"), view->priv->comment);
1595
1596 g_string_append (buffer, "</table><br>\n");
1597
1598 /* Description */
1599 if (view->priv->description && *view->priv->description) {
1600 g_string_append_printf (
1601 buffer,
1602 "<div id=\"" TABLE_ROW_DESCRIPTION "\" "
1603 "class=\"itip description\" %s>%s</div>\n",
1604 view->priv->description ? "" : "hidden=\"\"", view->priv->description);
1605
1606 g_string_append (buffer, "</div>");
1607 }
1608 }
1609
1610 void
1611 itip_view_create_dom_bindings (ItipView *view,
1612 WebKitDOMElement *element)
1613 {
1614 WebKitDOMElement *el;
1615 WebKitDOMDocument *doc;
1616
1617 doc = webkit_dom_node_get_owner_document (WEBKIT_DOM_NODE (element));
1618 view->priv->dom_document = g_object_ref (doc);
1619
1620 el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RECUR);
1621 if (el) {
1622 webkit_dom_event_target_add_event_listener (
1623 WEBKIT_DOM_EVENT_TARGET (el), "click",
1624 G_CALLBACK (recur_toggled_cb), FALSE, view);
1625 }
1626
1627 el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_RSVP);
1628 if (el) {
1629 webkit_dom_event_target_add_event_listener (
1630 WEBKIT_DOM_EVENT_TARGET (el), "click",
1631 G_CALLBACK (rsvp_toggled_cb), FALSE, view);
1632 }
1633
1634 el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_INHERIT_ALARM);
1635 if (el) {
1636 webkit_dom_event_target_add_event_listener (
1637 WEBKIT_DOM_EVENT_TARGET (el), "click",
1638 G_CALLBACK (alarm_check_toggled_cb), FALSE, view);
1639 }
1640
1641 el = webkit_dom_document_get_element_by_id (doc, CHECKBOX_KEEP_ALARM);
1642 if (el) {
1643 webkit_dom_event_target_add_event_listener (
1644 WEBKIT_DOM_EVENT_TARGET (el), "click",
1645 G_CALLBACK (alarm_check_toggled_cb), FALSE, view);
1646 }
1647
1648 el = webkit_dom_document_get_element_by_id (doc, BUTTON_OPEN_CALENDAR);
1649 if (el) {
1650 webkit_dom_event_target_add_event_listener (
1651 WEBKIT_DOM_EVENT_TARGET (el), "click",
1652 G_CALLBACK (button_clicked_cb), FALSE, view);
1653 }
1654
1655 el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT);
1656 if (el) {
1657 webkit_dom_event_target_add_event_listener (
1658 WEBKIT_DOM_EVENT_TARGET (el), "click",
1659 G_CALLBACK (button_clicked_cb), FALSE, view);
1660 }
1661
1662 el = webkit_dom_document_get_element_by_id (doc, BUTTON_ACCEPT_ALL);
1663 if (el) {
1664 webkit_dom_event_target_add_event_listener (
1665 WEBKIT_DOM_EVENT_TARGET (el), "click",
1666 G_CALLBACK (button_clicked_cb), FALSE, view);
1667 }
1668
1669 el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE);
1670 if (el) {
1671 webkit_dom_event_target_add_event_listener (
1672 WEBKIT_DOM_EVENT_TARGET (el), "click",
1673 G_CALLBACK (button_clicked_cb), FALSE, view);
1674 }
1675
1676 el = webkit_dom_document_get_element_by_id (doc, BUTTON_TENTATIVE_ALL);
1677 if (el) {
1678 webkit_dom_event_target_add_event_listener (
1679 WEBKIT_DOM_EVENT_TARGET (el), "click",
1680 G_CALLBACK (button_clicked_cb), FALSE, view);
1681 }
1682
1683 el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE);
1684 if (el) {
1685 webkit_dom_event_target_add_event_listener (
1686 WEBKIT_DOM_EVENT_TARGET (el), "click",
1687 G_CALLBACK (button_clicked_cb), FALSE, view);
1688 }
1689
1690 el = webkit_dom_document_get_element_by_id (doc, BUTTON_DECLINE_ALL);
1691 if (el) {
1692 webkit_dom_event_target_add_event_listener (
1693 WEBKIT_DOM_EVENT_TARGET (el), "click",
1694 G_CALLBACK (button_clicked_cb), FALSE, view);
1695 }
1696
1697 el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE);
1698 if (el) {
1699 webkit_dom_event_target_add_event_listener (
1700 WEBKIT_DOM_EVENT_TARGET (el), "click",
1701 G_CALLBACK (button_clicked_cb), FALSE, view);
1702 }
1703
1704 el = webkit_dom_document_get_element_by_id (doc, BUTTON_UPDATE_ATTENDEE_STATUS);
1705 if (el) {
1706 webkit_dom_event_target_add_event_listener (
1707 WEBKIT_DOM_EVENT_TARGET (el), "click",
1708 G_CALLBACK (button_clicked_cb), FALSE, view);
1709 }
1710
1711 el = webkit_dom_document_get_element_by_id (doc, BUTTON_SEND_INFORMATION);
1712 if (el) {
1713 webkit_dom_event_target_add_event_listener (
1714 WEBKIT_DOM_EVENT_TARGET (el), "click",
1715 G_CALLBACK (button_clicked_cb), FALSE, view);
1716 }
1717
1718 el = webkit_dom_document_get_element_by_id (doc, SELECT_ESOURCE);
1719 if (el) {
1720 webkit_dom_event_target_add_event_listener (
1721 WEBKIT_DOM_EVENT_TARGET (el), "change",
1722 G_CALLBACK (source_changed_cb), FALSE, view);
1723 }
1724 }
1725
1726 static void
1727 itip_view_init (ItipView *view)
1728 {
1729 view->priv = ITIP_VIEW_GET_PRIVATE (view);
1730
1731 }
1732
1733 ItipView *
1734 itip_view_new (EMailPartItip *puri,
1735 ESourceRegistry *registry)
1736 {
1737 ItipView *view;
1738
1739 view = ITIP_VIEW (g_object_new (
1740 ITIP_TYPE_VIEW,
1741 "registry", registry,
1742 NULL));
1743 view->priv->itip_part = puri;
1744
1745 return view;
1746 }
1747
1748 static void
1749 show_button (ItipView *view,
1750 const gchar *id)
1751 {
1752 WebKitDOMElement *button;
1753
1754 button = webkit_dom_document_get_element_by_id (
1755 view->priv->dom_document, id);
1756 webkit_dom_html_element_set_hidden (
1757 WEBKIT_DOM_HTML_ELEMENT (button), FALSE);
1758 }
1759
1760 void
1761 itip_view_set_mode (ItipView *view,
1762 ItipViewMode mode)
1763 {
1764 WebKitDOMElement *row, *cell;
1765 WebKitDOMElement *button;
1766
1767 g_return_if_fail (ITIP_IS_VIEW (view));
1768
1769 view->priv->mode = mode;
1770
1771 set_sender_text (view);
1772
1773 if (!view->priv->dom_document)
1774 return;
1775
1776 row = webkit_dom_document_get_element_by_id (
1777 view->priv->dom_document, TABLE_ROW_BUTTONS);
1778 cell = webkit_dom_element_get_first_element_child (row);
1779 do {
1780 button = webkit_dom_element_get_first_element_child (cell);
1781 webkit_dom_html_element_set_hidden (
1782 WEBKIT_DOM_HTML_ELEMENT (button), TRUE);
1783 } while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL);
1784
1785 view->priv->is_recur_set = itip_view_get_recur_check_state (view);
1786
1787 /* Always visible */
1788 show_button (view, BUTTON_OPEN_CALENDAR);
1789
1790 switch (mode) {
1791 case ITIP_VIEW_MODE_PUBLISH:
1792 if (view->priv->needs_decline) {
1793 show_button (view, BUTTON_DECLINE);
1794 }
1795 show_button (view, BUTTON_ACCEPT);
1796 break;
1797 case ITIP_VIEW_MODE_REQUEST:
1798 show_button (view, view->priv->is_recur_set ? BUTTON_DECLINE_ALL : BUTTON_DECLINE);
1799 show_button (view, view->priv->is_recur_set ? BUTTON_TENTATIVE_ALL : BUTTON_TENTATIVE);
1800 show_button (view, view->priv->is_recur_set ? BUTTON_ACCEPT_ALL : BUTTON_ACCEPT);
1801 break;
1802 case ITIP_VIEW_MODE_ADD:
1803 if (view->priv->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
1804 show_button (view, BUTTON_DECLINE);
1805 show_button (view, BUTTON_TENTATIVE);
1806 }
1807 show_button (view, BUTTON_ACCEPT);
1808 break;
1809 case ITIP_VIEW_MODE_REFRESH:
1810 show_button (view, BUTTON_SEND_INFORMATION);
1811 break;
1812 case ITIP_VIEW_MODE_REPLY:
1813 show_button (view, BUTTON_UPDATE_ATTENDEE_STATUS);
1814 break;
1815 case ITIP_VIEW_MODE_CANCEL:
1816 show_button (view, BUTTON_UPDATE);
1817 break;
1818 case ITIP_VIEW_MODE_COUNTER:
1819 case ITIP_VIEW_MODE_DECLINECOUNTER:
1820 show_button (view, BUTTON_DECLINE);
1821 show_button (view, BUTTON_TENTATIVE);
1822 show_button (view, BUTTON_ACCEPT);
1823 break;
1824 default:
1825 break;
1826 }
1827 }
1828
1829 ItipViewMode
1830 itip_view_get_mode (ItipView *view)
1831 {
1832 g_return_val_if_fail (ITIP_IS_VIEW (view), ITIP_VIEW_MODE_NONE);
1833
1834 return view->priv->mode;
1835 }
1836
1837 void
1838 itip_view_set_item_type (ItipView *view,
1839 ECalClientSourceType type)
1840 {
1841 WebKitDOMElement *label;
1842 const gchar *header;
1843 gchar *access_key, *html_label;
1844
1845 g_return_if_fail (ITIP_IS_VIEW (view));
1846
1847 view->priv->type = type;
1848
1849 if (!view->priv->dom_document)
1850 return;
1851
1852 label = webkit_dom_document_get_element_by_id (
1853 view->priv->dom_document, TABLE_ROW_ESCB_LABEL);
1854
1855 switch (view->priv->type) {
1856 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
1857 header = _("_Calendar:");
1858 break;
1859 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
1860 header = _("_Tasks:");
1861 break;
1862 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
1863 header = _("_Memos:");
1864 break;
1865 default:
1866 header = NULL;
1867 break;
1868 }
1869
1870 if (!header) {
1871 set_sender_text (view);
1872 return;
1873 }
1874
1875 html_label = e_mail_formatter_parse_html_mnemonics (header, &access_key);
1876
1877 webkit_dom_html_element_set_access_key (
1878 WEBKIT_DOM_HTML_ELEMENT (label), access_key);
1879 webkit_dom_html_element_set_inner_html (
1880 WEBKIT_DOM_HTML_ELEMENT (label), html_label, NULL);
1881
1882 g_free (html_label);
1883
1884 if (access_key)
1885 g_free (access_key);
1886
1887 set_sender_text (view);
1888 }
1889
1890 ECalClientSourceType
1891 itip_view_get_item_type (ItipView *view)
1892 {
1893 g_return_val_if_fail (ITIP_IS_VIEW (view), ITIP_VIEW_MODE_NONE);
1894
1895 return view->priv->type;
1896 }
1897
1898 void
1899 itip_view_set_organizer (ItipView *view,
1900 const gchar *organizer)
1901 {
1902 g_return_if_fail (ITIP_IS_VIEW (view));
1903
1904 if (view->priv->organizer)
1905 g_free (view->priv->organizer);
1906
1907 view->priv->organizer = e_utf8_ensure_valid (organizer);
1908
1909 set_sender_text (view);
1910 }
1911
1912 const gchar *
1913 itip_view_get_organizer (ItipView *view)
1914 {
1915 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1916
1917 return view->priv->organizer;
1918 }
1919
1920 void
1921 itip_view_set_organizer_sentby (ItipView *view,
1922 const gchar *sentby)
1923 {
1924 g_return_if_fail (ITIP_IS_VIEW (view));
1925
1926 if (view->priv->organizer_sentby)
1927 g_free (view->priv->organizer_sentby);
1928
1929 view->priv->organizer_sentby = e_utf8_ensure_valid (sentby);
1930
1931 set_sender_text (view);
1932 }
1933
1934 const gchar *
1935 itip_view_get_organizer_sentby (ItipView *view)
1936 {
1937 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1938
1939 return view->priv->organizer_sentby;
1940 }
1941
1942 void
1943 itip_view_set_attendee (ItipView *view,
1944 const gchar *attendee)
1945 {
1946 g_return_if_fail (ITIP_IS_VIEW (view));
1947
1948 if (view->priv->attendee)
1949 g_free (view->priv->attendee);
1950
1951 view->priv->attendee = e_utf8_ensure_valid (attendee);
1952
1953 set_sender_text (view);
1954 }
1955
1956 const gchar *
1957 itip_view_get_attendee (ItipView *view)
1958 {
1959 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1960
1961 return view->priv->attendee;
1962 }
1963
1964 void
1965 itip_view_set_attendee_sentby (ItipView *view,
1966 const gchar *sentby)
1967 {
1968 g_return_if_fail (ITIP_IS_VIEW (view));
1969
1970 if (view->priv->attendee_sentby)
1971 g_free (view->priv->attendee_sentby);
1972
1973 view->priv->attendee_sentby = e_utf8_ensure_valid (sentby);
1974
1975 set_sender_text (view);
1976 }
1977
1978 const gchar *
1979 itip_view_get_attendee_sentby (ItipView *view)
1980 {
1981 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
1982
1983 return view->priv->attendee_sentby;
1984 }
1985
1986 void
1987 itip_view_set_proxy (ItipView *view,
1988 const gchar *proxy)
1989 {
1990 g_return_if_fail (ITIP_IS_VIEW (view));
1991
1992 if (view->priv->proxy)
1993 g_free (view->priv->proxy);
1994
1995 view->priv->proxy = e_utf8_ensure_valid (proxy);
1996
1997 set_sender_text (view);
1998 }
1999
2000 const gchar *
2001 itip_view_get_proxy (ItipView *view)
2002 {
2003 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2004
2005 return view->priv->proxy;
2006 }
2007
2008 void
2009 itip_view_set_delegator (ItipView *view,
2010 const gchar *delegator)
2011 {
2012 g_return_if_fail (ITIP_IS_VIEW (view));
2013
2014 if (view->priv->delegator)
2015 g_free (view->priv->delegator);
2016
2017 view->priv->delegator = e_utf8_ensure_valid (delegator);
2018
2019 set_sender_text (view);
2020 }
2021
2022 const gchar *
2023 itip_view_get_delegator (ItipView *view)
2024 {
2025 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2026
2027 return view->priv->delegator;
2028 }
2029
2030 void
2031 itip_view_set_summary (ItipView *view,
2032 const gchar *summary)
2033 {
2034 WebKitDOMElement *row, *col;
2035
2036 g_return_if_fail (ITIP_IS_VIEW (view));
2037
2038 if (view->priv->summary)
2039 g_free (view->priv->summary);
2040
2041 view->priv->summary = summary ? g_strstrip (e_utf8_ensure_valid (summary)) : NULL;
2042
2043 if (!view->priv->dom_document)
2044 return;
2045
2046 row = webkit_dom_document_get_element_by_id (
2047 view->priv->dom_document, TABLE_ROW_SUMMARY);
2048 webkit_dom_html_element_set_hidden (
2049 WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->summary == NULL));
2050
2051 col = webkit_dom_element_get_last_element_child (row);
2052 webkit_dom_html_element_set_inner_html (
2053 WEBKIT_DOM_HTML_ELEMENT (col),
2054 view->priv->summary ? view->priv->summary : "",
2055 NULL);
2056 }
2057
2058 const gchar *
2059 itip_view_get_summary (ItipView *view)
2060 {
2061 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2062
2063 return view->priv->summary;
2064 }
2065
2066 void
2067 itip_view_set_location (ItipView *view,
2068 const gchar *location)
2069 {
2070 WebKitDOMElement *row, *col;
2071
2072 g_return_if_fail (ITIP_IS_VIEW (view));
2073
2074 if (view->priv->location)
2075 g_free (view->priv->location);
2076
2077 view->priv->location = location ? g_strstrip (e_utf8_ensure_valid (location)) : NULL;
2078
2079 if (!view->priv->dom_document)
2080 return;
2081
2082 row = webkit_dom_document_get_element_by_id (
2083 view->priv->dom_document, TABLE_ROW_LOCATION);
2084 webkit_dom_html_element_set_hidden (
2085 WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->location == NULL));
2086
2087 col = webkit_dom_element_get_last_element_child (row);
2088 webkit_dom_html_element_set_inner_html (
2089 WEBKIT_DOM_HTML_ELEMENT (col),
2090 view->priv->location ? view->priv->location : "",
2091 NULL);
2092 }
2093
2094 const gchar *
2095 itip_view_get_location (ItipView *view)
2096 {
2097 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2098
2099 return view->priv->location;
2100 }
2101
2102 void
2103 itip_view_set_status (ItipView *view,
2104 const gchar *status)
2105 {
2106 WebKitDOMElement *row, *col;
2107
2108 g_return_if_fail (ITIP_IS_VIEW (view));
2109
2110 if (view->priv->status)
2111 g_free (view->priv->status);
2112
2113 view->priv->status = status ? g_strstrip (e_utf8_ensure_valid (status)) : NULL;
2114
2115 if (!view->priv->dom_document)
2116 return;
2117
2118 row = webkit_dom_document_get_element_by_id (
2119 view->priv->dom_document, TABLE_ROW_STATUS);
2120 webkit_dom_html_element_set_hidden (
2121 WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->status == NULL));
2122
2123 col = webkit_dom_element_get_last_element_child (row);
2124 webkit_dom_html_element_set_inner_html (
2125 WEBKIT_DOM_HTML_ELEMENT (col),
2126 view->priv->status ? view->priv->status : "",
2127 NULL);
2128 }
2129
2130 const gchar *
2131 itip_view_get_status (ItipView *view)
2132 {
2133 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2134
2135 return view->priv->status;
2136 }
2137
2138 void
2139 itip_view_set_comment (ItipView *view,
2140 const gchar *comment)
2141 {
2142 WebKitDOMElement *row, *col;
2143
2144 g_return_if_fail (ITIP_IS_VIEW (view));
2145
2146 if (view->priv->comment)
2147 g_free (view->priv->comment);
2148
2149 view->priv->comment = comment ? g_strstrip (e_utf8_ensure_valid (comment)) : NULL;
2150
2151 if (!view->priv->dom_document)
2152 return;
2153
2154 row = webkit_dom_document_get_element_by_id (
2155 view->priv->dom_document, TABLE_ROW_COMMENT);
2156 webkit_dom_html_element_set_hidden (
2157 WEBKIT_DOM_HTML_ELEMENT (row), (view->priv->comment == NULL));
2158
2159 col = webkit_dom_element_get_last_element_child (row);
2160 webkit_dom_html_element_set_inner_html (
2161 WEBKIT_DOM_HTML_ELEMENT (col),
2162 view->priv->comment ? view->priv->comment : "",
2163 NULL);
2164 }
2165
2166 const gchar *
2167 itip_view_get_comment (ItipView *view)
2168 {
2169 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2170
2171 return view->priv->comment;
2172 }
2173
2174 void
2175 itip_view_set_description (ItipView *view,
2176 const gchar *description)
2177 {
2178 WebKitDOMElement *div;
2179
2180 g_return_if_fail (ITIP_IS_VIEW (view));
2181
2182 if (view->priv->description)
2183 g_free (view->priv->description);
2184
2185 view->priv->description = description ? g_strstrip (e_utf8_ensure_valid (description)) : NULL;
2186
2187 if (!view->priv->dom_document)
2188 return;
2189
2190 div = webkit_dom_document_get_element_by_id (
2191 view->priv->dom_document, TABLE_ROW_DESCRIPTION);
2192 webkit_dom_html_element_set_hidden (
2193 WEBKIT_DOM_HTML_ELEMENT (div), (view->priv->description == NULL));
2194
2195 webkit_dom_html_element_set_inner_html (
2196 WEBKIT_DOM_HTML_ELEMENT (div),
2197 view->priv->description ? view->priv->description : "",
2198 NULL);
2199 }
2200
2201 const gchar *
2202 itip_view_get_description (ItipView *view)
2203 {
2204 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2205
2206 return view->priv->description;
2207 }
2208
2209 void
2210 itip_view_set_start (ItipView *view,
2211 struct tm *start,
2212 gboolean is_date)
2213 {
2214 ItipViewPrivate *priv;
2215
2216 g_return_if_fail (ITIP_IS_VIEW (view));
2217
2218 priv = view->priv;
2219
2220 if (priv->start_tm && !start) {
2221 g_free (priv->start_tm);
2222 priv->start_tm = NULL;
2223 } else if (start) {
2224 if (!priv->start_tm)
2225 priv->start_tm = g_new0 (struct tm, 1);
2226
2227 *priv->start_tm = *start;
2228 }
2229
2230 priv->start_tm_is_date = is_date && start;
2231
2232 update_start_end_times (view);
2233 }
2234
2235 const struct tm *
2236 itip_view_get_start (ItipView *view,
2237 gboolean *is_date)
2238 {
2239 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2240
2241 if (is_date)
2242 *is_date = view->priv->start_tm_is_date;
2243
2244 return view->priv->start_tm;
2245 }
2246
2247 void
2248 itip_view_set_end (ItipView *view,
2249 struct tm *end,
2250 gboolean is_date)
2251 {
2252 ItipViewPrivate *priv;
2253
2254 g_return_if_fail (ITIP_IS_VIEW (view));
2255
2256 priv = view->priv;
2257
2258 if (priv->end_tm && !end) {
2259 g_free (priv->end_tm);
2260 priv->end_tm = NULL;
2261 } else if (end) {
2262 if (!priv->end_tm)
2263 priv->end_tm = g_new0 (struct tm, 1);
2264
2265 *priv->end_tm = *end;
2266 }
2267
2268 priv->end_tm_is_date = is_date && end;
2269
2270 update_start_end_times (view);
2271 }
2272
2273 const struct tm *
2274 itip_view_get_end (ItipView *view,
2275 gboolean *is_date)
2276 {
2277 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2278
2279 if (is_date)
2280 *is_date = view->priv->end_tm_is_date;
2281
2282 return view->priv->end_tm;
2283 }
2284
2285 guint
2286 itip_view_add_upper_info_item (ItipView *view,
2287 ItipViewInfoItemType type,
2288 const gchar *message)
2289 {
2290 ItipViewPrivate *priv;
2291 ItipViewInfoItem *item;
2292
2293 g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
2294
2295 priv = view->priv;
2296
2297 item = g_new0 (ItipViewInfoItem, 1);
2298
2299 item->type = type;
2300 item->message = e_utf8_ensure_valid (message);
2301 item->id = priv->next_info_item_id++;
2302
2303 priv->upper_info_items = g_slist_append (priv->upper_info_items, item);
2304
2305 if (!view->priv->dom_document)
2306 return item->id;
2307
2308 append_info_item_row (view, TABLE_UPPER_ITIP_INFO, item);
2309
2310 return item->id;
2311 }
2312
2313 guint
2314 itip_view_add_upper_info_item_printf (ItipView *view,
2315 ItipViewInfoItemType type,
2316 const gchar *format,
2317 ...)
2318 {
2319 va_list args;
2320 gchar *message;
2321 guint id;
2322
2323 g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
2324
2325 va_start (args, format);
2326 message = g_strdup_vprintf (format, args);
2327 va_end (args);
2328
2329 id = itip_view_add_upper_info_item (view, type, message);
2330 g_free (message);
2331
2332 return id;
2333 }
2334
2335 void
2336 itip_view_remove_upper_info_item (ItipView *view,
2337 guint id)
2338 {
2339 ItipViewPrivate *priv;
2340 GSList *l;
2341
2342 g_return_if_fail (ITIP_IS_VIEW (view));
2343
2344 priv = view->priv;
2345
2346 for (l = priv->upper_info_items; l; l = l->next) {
2347 ItipViewInfoItem *item = l->data;
2348
2349 if (item->id == id) {
2350 priv->upper_info_items = g_slist_remove (priv->upper_info_items, item);
2351
2352 g_free (item->message);
2353 g_free (item);
2354
2355 if (!view->priv->dom_document)
2356 remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, id);
2357
2358 return;
2359 }
2360 }
2361 }
2362
2363 void
2364 itip_view_clear_upper_info_items (ItipView *view)
2365 {
2366 ItipViewPrivate *priv;
2367 GSList *l;
2368
2369 g_return_if_fail (ITIP_IS_VIEW (view));
2370
2371 priv = view->priv;
2372
2373 for (l = priv->upper_info_items; l; l = l->next) {
2374 ItipViewInfoItem *item = l->data;
2375
2376 if (view->priv->dom_document)
2377 remove_info_item_row (view, TABLE_UPPER_ITIP_INFO, item->id);
2378
2379 g_free (item->message);
2380 g_free (item);
2381 }
2382
2383 g_slist_free (priv->upper_info_items);
2384 priv->upper_info_items = NULL;
2385 }
2386
2387 guint
2388 itip_view_add_lower_info_item (ItipView *view,
2389 ItipViewInfoItemType type,
2390 const gchar *message)
2391 {
2392 ItipViewPrivate *priv;
2393 ItipViewInfoItem *item;
2394
2395 g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
2396
2397 priv = view->priv;
2398
2399 item = g_new0 (ItipViewInfoItem, 1);
2400
2401 item->type = type;
2402 item->message = e_utf8_ensure_valid (message);
2403 item->id = priv->next_info_item_id++;
2404
2405 priv->lower_info_items = g_slist_append (priv->lower_info_items, item);
2406
2407 if (!view->priv->dom_document)
2408 return item->id;
2409
2410 append_info_item_row (view, TABLE_LOWER_ITIP_INFO, item);
2411
2412 return item->id;
2413 }
2414
2415 guint
2416 itip_view_add_lower_info_item_printf (ItipView *view,
2417 ItipViewInfoItemType type,
2418 const gchar *format,
2419 ...)
2420 {
2421 va_list args;
2422 gchar *message;
2423 guint id;
2424
2425 g_return_val_if_fail (ITIP_IS_VIEW (view), 0);
2426
2427 va_start (args, format);
2428 message = g_strdup_vprintf (format, args);
2429 va_end (args);
2430
2431 id = itip_view_add_lower_info_item (view, type, message);
2432 g_free (message);
2433
2434 return id;
2435 }
2436
2437 void
2438 itip_view_remove_lower_info_item (ItipView *view,
2439 guint id)
2440 {
2441 ItipViewPrivate *priv;
2442 GSList *l;
2443
2444 g_return_if_fail (ITIP_IS_VIEW (view));
2445
2446 priv = view->priv;
2447
2448 for (l = priv->lower_info_items; l; l = l->next) {
2449 ItipViewInfoItem *item = l->data;
2450
2451 if (item->id == id) {
2452 priv->lower_info_items = g_slist_remove (priv->lower_info_items, item);
2453
2454 g_free (item->message);
2455 g_free (item);
2456
2457 if (view->priv->dom_document)
2458 remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, id);
2459
2460 return;
2461 }
2462 }
2463 }
2464
2465 void
2466 itip_view_clear_lower_info_items (ItipView *view)
2467 {
2468 ItipViewPrivate *priv;
2469 GSList *l;
2470
2471 g_return_if_fail (ITIP_IS_VIEW (view));
2472
2473 priv = view->priv;
2474
2475 for (l = priv->lower_info_items; l; l = l->next) {
2476 ItipViewInfoItem *item = l->data;
2477
2478 if (view->priv->dom_document)
2479 remove_info_item_row (view, TABLE_LOWER_ITIP_INFO, item->id);
2480
2481 g_free (item->message);
2482 g_free (item);
2483 }
2484
2485 g_slist_free (priv->lower_info_items);
2486 priv->lower_info_items = NULL;
2487 }
2488
2489 void
2490 itip_view_set_source (ItipView *view,
2491 ESource *source)
2492 {
2493 WebKitDOMElement *select;
2494 WebKitDOMElement *row;
2495 ESource *selected_source;
2496 gulong i, len;
2497
2498 g_return_if_fail (ITIP_IS_VIEW (view));
2499
2500 d (printf ("Settings default source '%s'\n", e_source_get_display_name (source)));
2501
2502 if (!view->priv->dom_document)
2503 return;
2504
2505 row = webkit_dom_document_get_element_by_id (
2506 view->priv->dom_document, TABLE_ROW_ESCB);
2507 webkit_dom_html_element_set_hidden (
2508 WEBKIT_DOM_HTML_ELEMENT (row), (source == NULL));
2509 if (source == NULL)
2510 return;
2511
2512 select = webkit_dom_document_get_element_by_id (
2513 view->priv->dom_document, SELECT_ESOURCE);
2514
2515 /* <select> does not emit 'change' event when already selected
2516 * <option> is re-selected, but we need to notify itip formatter,
2517 * so that it would make all the buttons sensitive */
2518 selected_source = itip_view_ref_source (view);
2519 if (source == selected_source) {
2520 source_changed_cb (select, NULL, view);
2521 return;
2522 }
2523
2524 if (selected_source != NULL)
2525 g_object_unref (selected_source);
2526
2527 if (webkit_dom_html_select_element_get_disabled (
2528 WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) {
2529 webkit_dom_html_select_element_set_disabled (
2530 WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE);
2531 }
2532
2533 len = webkit_dom_html_select_element_get_length (
2534 WEBKIT_DOM_HTML_SELECT_ELEMENT (select));
2535 for (i = 0; i < len; i++) {
2536
2537 WebKitDOMNode *node;
2538 WebKitDOMHTMLOptionElement *option;
2539 gchar *value;
2540
2541 node = webkit_dom_html_select_element_item (
2542 WEBKIT_DOM_HTML_SELECT_ELEMENT (select), i);
2543 option = WEBKIT_DOM_HTML_OPTION_ELEMENT (node);
2544
2545 value = webkit_dom_html_option_element_get_value (option);
2546 if (g_strcmp0 (value, e_source_get_uid (source)) == 0) {
2547 webkit_dom_html_option_element_set_selected (
2548 option, TRUE);
2549
2550 g_free (value);
2551 break;
2552 }
2553
2554 g_free (value);
2555 }
2556
2557 source_changed_cb (select, NULL, view);
2558 }
2559
2560 ESource *
2561 itip_view_ref_source (ItipView *view)
2562 {
2563 ESourceRegistry *registry;
2564 WebKitDOMElement *select;
2565 gchar *uid;
2566 ESource *source;
2567 gboolean disable = FALSE;
2568
2569 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2570
2571 if (!view->priv->dom_document)
2572 return NULL;
2573
2574 select = webkit_dom_document_get_element_by_id (
2575 view->priv->dom_document, SELECT_ESOURCE);
2576 if (webkit_dom_html_select_element_get_disabled (
2577 WEBKIT_DOM_HTML_SELECT_ELEMENT (select))) {
2578 webkit_dom_html_select_element_set_disabled (
2579 WEBKIT_DOM_HTML_SELECT_ELEMENT (select), FALSE);
2580 disable = TRUE;
2581 }
2582
2583 uid = webkit_dom_html_select_element_get_value (
2584 WEBKIT_DOM_HTML_SELECT_ELEMENT (select));
2585
2586 registry = itip_view_get_registry (view);
2587 source = e_source_registry_ref_source (registry, uid);
2588
2589 g_free (uid);
2590
2591 if (disable) {
2592 webkit_dom_html_select_element_set_disabled (
2593 WEBKIT_DOM_HTML_SELECT_ELEMENT (select), TRUE);
2594 }
2595
2596 return source;
2597 }
2598
2599 void
2600 itip_view_set_rsvp (ItipView *view,
2601 gboolean rsvp)
2602 {
2603 WebKitDOMElement *el;
2604
2605 g_return_if_fail (ITIP_IS_VIEW (view));
2606
2607 if (!view->priv->dom_document)
2608 return;
2609
2610 el = webkit_dom_document_get_element_by_id (
2611 view->priv->dom_document, CHECKBOX_RSVP);
2612 webkit_dom_html_input_element_set_checked (
2613 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), rsvp);
2614
2615 el = webkit_dom_document_get_element_by_id (
2616 view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
2617 webkit_dom_html_text_area_element_set_disabled (
2618 WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !rsvp);
2619 }
2620
2621 gboolean
2622 itip_view_get_rsvp (ItipView *view)
2623 {
2624 WebKitDOMElement *el;
2625
2626 g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2627
2628 if (!view->priv->dom_document)
2629 return FALSE;
2630
2631 el = webkit_dom_document_get_element_by_id (
2632 view->priv->dom_document, CHECKBOX_RSVP);
2633 return webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
2634 }
2635
2636 void
2637 itip_view_set_show_rsvp_check (ItipView *view,
2638 gboolean show)
2639 {
2640 WebKitDOMElement *label;
2641 WebKitDOMElement *el;
2642
2643 g_return_if_fail (ITIP_IS_VIEW (view));
2644
2645 if (!view->priv->dom_document)
2646 return;
2647
2648 el = webkit_dom_document_get_element_by_id (
2649 view->priv->dom_document, "table_row_" CHECKBOX_RSVP);
2650 webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2651
2652 el = webkit_dom_document_get_element_by_id (
2653 view->priv->dom_document, CHECKBOX_RSVP);
2654 label = webkit_dom_element_get_next_element_sibling (el);
2655 webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2656
2657 if (!show) {
2658 webkit_dom_html_input_element_set_checked (
2659 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
2660 }
2661
2662 el = webkit_dom_document_get_element_by_id (
2663 view->priv->dom_document, TABLE_ROW_RSVP_COMMENT);
2664 webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2665 }
2666
2667 gboolean
2668 itip_view_get_show_rsvp_check (ItipView *view)
2669 {
2670 WebKitDOMElement *el;
2671
2672 g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2673
2674 if (!view->priv->dom_document)
2675 return FALSE;
2676
2677 el = webkit_dom_document_get_element_by_id (
2678 view->priv->dom_document, CHECKBOX_RSVP);
2679 return !webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el));
2680 }
2681
2682 void
2683 itip_view_set_update (ItipView *view,
2684 gboolean update)
2685 {
2686 WebKitDOMElement *el;
2687
2688 g_return_if_fail (ITIP_IS_VIEW (view));
2689
2690 if (!view->priv->dom_document)
2691 return;
2692
2693 el = webkit_dom_document_get_element_by_id (
2694 view->priv->dom_document, CHECKBOX_UPDATE);
2695
2696 webkit_dom_html_input_element_set_checked (
2697 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), update);
2698 }
2699
2700 gboolean
2701 itip_view_get_update (ItipView *view)
2702 {
2703 WebKitDOMElement *el;
2704
2705 g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2706
2707 if (!view->priv->dom_document)
2708 return FALSE;
2709
2710 el = webkit_dom_document_get_element_by_id (
2711 view->priv->dom_document, CHECKBOX_UPDATE);
2712 return webkit_dom_html_input_element_get_checked (WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
2713 }
2714
2715 void
2716 itip_view_set_show_update_check (ItipView *view,
2717 gboolean show)
2718 {
2719 WebKitDOMElement *label;
2720 WebKitDOMElement *el;
2721
2722 g_return_if_fail (ITIP_IS_VIEW (view));
2723
2724 if (!view->priv->dom_document)
2725 return;
2726
2727 el = webkit_dom_document_get_element_by_id (
2728 view->priv->dom_document, "table_row_" CHECKBOX_UPDATE);
2729 webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2730
2731 el = webkit_dom_document_get_element_by_id (
2732 view->priv->dom_document, CHECKBOX_UPDATE);
2733 label = webkit_dom_element_get_next_element_sibling (el);
2734 webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2735
2736 if (!show) {
2737 webkit_dom_html_input_element_set_checked (
2738 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
2739 }
2740 }
2741
2742 gboolean
2743 itip_view_get_show_update_check (ItipView *view)
2744 {
2745 WebKitDOMElement *el;
2746
2747 g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2748
2749 if (!view->priv->dom_document)
2750 return FALSE;
2751
2752 el = webkit_dom_document_get_element_by_id (
2753 view->priv->dom_document, CHECKBOX_UPDATE);
2754 return !webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el));
2755 }
2756
2757 void
2758 itip_view_set_rsvp_comment (ItipView *view,
2759 const gchar *comment)
2760 {
2761 WebKitDOMElement *el;
2762
2763 g_return_if_fail (ITIP_IS_VIEW (view));
2764
2765 if (!view->priv->dom_document)
2766 return;
2767
2768 el = webkit_dom_document_get_element_by_id (
2769 view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
2770 webkit_dom_html_element_set_hidden (
2771 WEBKIT_DOM_HTML_ELEMENT (el), (comment == NULL));
2772
2773 if (comment) {
2774 webkit_dom_html_text_area_element_set_value (
2775 WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), comment);
2776 }
2777 }
2778
2779 gchar *
2780 itip_view_get_rsvp_comment (ItipView *view)
2781 {
2782 WebKitDOMElement *el;
2783
2784 g_return_val_if_fail (ITIP_IS_VIEW (view), NULL);
2785
2786 if (!view->priv->dom_document)
2787 return NULL;
2788
2789 el = webkit_dom_document_get_element_by_id (
2790 view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
2791
2792 if (webkit_dom_html_element_get_hidden (WEBKIT_DOM_HTML_ELEMENT (el))) {
2793 return NULL;
2794 }
2795
2796 return webkit_dom_html_text_area_element_get_value (
2797 WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el));
2798 }
2799
2800 void
2801 itip_view_set_needs_decline (ItipView *view,
2802 gboolean needs_decline)
2803 {
2804 g_return_if_fail (ITIP_IS_VIEW (view));
2805
2806 view->priv->needs_decline = needs_decline;
2807 }
2808
2809 void
2810 itip_view_set_buttons_sensitive (ItipView *view,
2811 gboolean sensitive)
2812 {
2813 WebKitDOMElement *el, *cell;
2814
2815 g_return_if_fail (ITIP_IS_VIEW (view));
2816
2817 d (printf ("Settings buttons %s\n", sensitive ? "sensitive" : "insensitive"));
2818
2819 view->priv->buttons_sensitive = sensitive;
2820
2821 if (!view->priv->dom_document)
2822 return;
2823
2824 el = webkit_dom_document_get_element_by_id (
2825 view->priv->dom_document, CHECKBOX_UPDATE);
2826 webkit_dom_html_input_element_set_disabled (
2827 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2828
2829 el = webkit_dom_document_get_element_by_id (
2830 view->priv->dom_document, CHECKBOX_RECUR);
2831 webkit_dom_html_input_element_set_disabled (
2832 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2833
2834 el = webkit_dom_document_get_element_by_id (
2835 view->priv->dom_document, CHECKBOX_FREE_TIME);
2836 webkit_dom_html_input_element_set_disabled (
2837 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2838
2839 el = webkit_dom_document_get_element_by_id (
2840 view->priv->dom_document, CHECKBOX_KEEP_ALARM);
2841 webkit_dom_html_input_element_set_disabled (
2842 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2843
2844 el = webkit_dom_document_get_element_by_id (
2845 view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
2846 webkit_dom_html_input_element_set_disabled (
2847 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2848
2849 el = webkit_dom_document_get_element_by_id (
2850 view->priv->dom_document, CHECKBOX_RSVP);
2851 webkit_dom_html_input_element_set_disabled (
2852 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), !sensitive);
2853
2854 el = webkit_dom_document_get_element_by_id (
2855 view->priv->dom_document, TEXTAREA_RSVP_COMMENT);
2856 webkit_dom_html_text_area_element_set_disabled (
2857 WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (el), !sensitive);
2858
2859 el = webkit_dom_document_get_element_by_id (
2860 view->priv->dom_document, TABLE_ROW_BUTTONS);
2861 cell = webkit_dom_element_get_first_element_child (el);
2862 do {
2863 WebKitDOMElement *btn;
2864 btn = webkit_dom_element_get_first_element_child (cell);
2865 if (!webkit_dom_html_element_get_hidden (
2866 WEBKIT_DOM_HTML_ELEMENT (btn))) {
2867 webkit_dom_html_button_element_set_disabled (
2868 WEBKIT_DOM_HTML_BUTTON_ELEMENT (btn), !sensitive);
2869 }
2870 } while ((cell = webkit_dom_element_get_next_element_sibling (cell)) != NULL);
2871 }
2872
2873 gboolean
2874 itip_view_get_buttons_sensitive (ItipView *view)
2875 {
2876 g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2877
2878 return view->priv->buttons_sensitive;
2879 }
2880
2881 gboolean
2882 itip_view_get_recur_check_state (ItipView *view)
2883 {
2884 WebKitDOMElement *el;
2885
2886 g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2887
2888 if (!view->priv->dom_document)
2889 return FALSE;
2890
2891 el = webkit_dom_document_get_element_by_id (
2892 view->priv->dom_document, CHECKBOX_RECUR);
2893 return webkit_dom_html_input_element_get_checked (
2894 WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
2895 }
2896
2897 void
2898 itip_view_set_show_recur_check (ItipView *view,
2899 gboolean show)
2900 {
2901 WebKitDOMElement *label;
2902 WebKitDOMElement *el;
2903
2904 g_return_if_fail (ITIP_IS_VIEW (view));
2905
2906 if (!view->priv->dom_document)
2907 return;
2908
2909 el = webkit_dom_document_get_element_by_id (
2910 view->priv->dom_document, "table_row_" CHECKBOX_RECUR);
2911 webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2912
2913 el = webkit_dom_document_get_element_by_id (
2914 view->priv->dom_document, CHECKBOX_RECUR);
2915 label = webkit_dom_element_get_next_element_sibling (el);
2916 webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2917
2918 if (!show) {
2919 webkit_dom_html_input_element_set_checked (
2920 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
2921 }
2922
2923 /* and update state of the second check */
2924 alarm_check_toggled_cb (
2925 WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
2926 NULL, view);
2927 }
2928
2929 void
2930 itip_view_set_show_free_time_check (ItipView *view,
2931 gboolean show)
2932 {
2933 WebKitDOMElement *label;
2934 WebKitDOMElement *el;
2935
2936 g_return_if_fail (ITIP_IS_VIEW (view));
2937
2938 if (!view->priv->dom_document)
2939 return;
2940
2941 el = webkit_dom_document_get_element_by_id (
2942 view->priv->dom_document, "table_row_" CHECKBOX_FREE_TIME);
2943 webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2944
2945 el = webkit_dom_document_get_element_by_id (
2946 view->priv->dom_document, CHECKBOX_FREE_TIME);
2947 label = webkit_dom_element_get_next_element_sibling (el);
2948 webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2949
2950 if (!show) {
2951 webkit_dom_html_input_element_set_checked (
2952 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
2953 }
2954
2955 /* and update state of the second check */
2956 alarm_check_toggled_cb (
2957 WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
2958 NULL, view);
2959 }
2960
2961 gboolean
2962 itip_view_get_free_time_check_state (ItipView *view)
2963 {
2964 WebKitDOMElement *el;
2965
2966 g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
2967
2968 if (!view->priv->dom_document)
2969 return FALSE;
2970
2971 el = webkit_dom_document_get_element_by_id (
2972 view->priv->dom_document, CHECKBOX_FREE_TIME);
2973 return webkit_dom_html_input_element_get_checked (
2974 WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
2975 }
2976
2977 void
2978 itip_view_set_show_keep_alarm_check (ItipView *view,
2979 gboolean show)
2980 {
2981 WebKitDOMElement *label;
2982 WebKitDOMElement *el;
2983
2984 g_return_if_fail (ITIP_IS_VIEW (view));
2985
2986 if (!view->priv->dom_document)
2987 return;
2988
2989 el = webkit_dom_document_get_element_by_id (
2990 view->priv->dom_document, "table_row_" CHECKBOX_KEEP_ALARM);
2991 webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
2992
2993 el = webkit_dom_document_get_element_by_id (
2994 view->priv->dom_document, CHECKBOX_KEEP_ALARM);
2995 label = webkit_dom_element_get_next_element_sibling (el);
2996 webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
2997
2998 if (!show) {
2999 webkit_dom_html_input_element_set_checked (
3000 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
3001 }
3002
3003 /* and update state of the second check */
3004 alarm_check_toggled_cb (
3005 WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
3006 NULL, view);
3007 }
3008
3009 gboolean
3010 itip_view_get_keep_alarm_check_state (ItipView *view)
3011 {
3012 WebKitDOMElement *el;
3013
3014 g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
3015
3016 if (!view->priv->dom_document)
3017 return FALSE;
3018
3019 el = webkit_dom_document_get_element_by_id (
3020 view->priv->dom_document, CHECKBOX_KEEP_ALARM);
3021 return webkit_dom_html_input_element_get_checked (
3022 WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
3023 }
3024
3025 void
3026 itip_view_set_show_inherit_alarm_check (ItipView *view,
3027 gboolean show)
3028 {
3029 WebKitDOMElement *label;
3030 WebKitDOMElement *el;
3031
3032 g_return_if_fail (ITIP_IS_VIEW (view));
3033
3034 if (!view->priv->dom_document)
3035 return;
3036
3037 el = webkit_dom_document_get_element_by_id (
3038 view->priv->dom_document, "table_row_" CHECKBOX_INHERIT_ALARM);
3039 webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (el), !show);
3040
3041 el = webkit_dom_document_get_element_by_id (
3042 view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
3043 label = webkit_dom_element_get_next_element_sibling (el);
3044 webkit_dom_html_element_set_hidden (WEBKIT_DOM_HTML_ELEMENT (label), !show);
3045
3046 if (!show) {
3047 webkit_dom_html_input_element_set_checked (
3048 WEBKIT_DOM_HTML_INPUT_ELEMENT (el), FALSE);
3049 }
3050
3051 /* and update state of the second check */
3052 alarm_check_toggled_cb (
3053 WEBKIT_DOM_HTML_INPUT_ELEMENT (el),
3054 NULL, view);
3055 }
3056
3057 gboolean
3058 itip_view_get_inherit_alarm_check_state (ItipView *view)
3059 {
3060 WebKitDOMElement *el;
3061
3062 g_return_val_if_fail (ITIP_IS_VIEW (view), FALSE);
3063
3064 if (!view->priv->dom_document)
3065 return FALSE;
3066
3067 el = webkit_dom_document_get_element_by_id (
3068 view->priv->dom_document, CHECKBOX_INHERIT_ALARM);
3069 return webkit_dom_html_input_element_get_checked (
3070 WEBKIT_DOM_HTML_INPUT_ELEMENT (el));
3071 }
3072
3073 void
3074 itip_view_set_error (ItipView *view,
3075 const gchar *error_html,
3076 gboolean show_save_btn)
3077 {
3078 WebKitDOMElement *content, *error;
3079 GString *str;
3080
3081 g_return_if_fail (ITIP_IS_VIEW (view));
3082 g_return_if_fail (error_html);
3083
3084 str = g_string_new (error_html);
3085
3086 if (show_save_btn) {
3087 g_string_append (
3088 str,
3089 "<table border=\"0\" width=\"100%\">"
3090 "<tr width=\"100%\" id=\"" TABLE_ROW_BUTTONS "\">");
3091
3092 buttons_table_write_button (
3093 str, BUTTON_SAVE, _("_Save"),
3094 GTK_STOCK_SAVE, ITIP_VIEW_RESPONSE_SAVE);
3095
3096 g_string_append (str, "</tr></table>");
3097 }
3098
3099 view->priv->error = str->str;
3100 g_string_free (str, FALSE);
3101
3102 if (!view->priv->dom_document)
3103 return;
3104
3105 content = webkit_dom_document_get_element_by_id (
3106 view->priv->dom_document, DIV_ITIP_CONTENT);
3107 webkit_dom_html_element_set_hidden (
3108 WEBKIT_DOM_HTML_ELEMENT (content), TRUE);
3109
3110 error = webkit_dom_document_get_element_by_id (
3111 view->priv->dom_document, DIV_ITIP_ERROR);
3112 webkit_dom_html_element_set_hidden (
3113 WEBKIT_DOM_HTML_ELEMENT (error), FALSE);
3114
3115 webkit_dom_html_element_set_inner_html (
3116 WEBKIT_DOM_HTML_ELEMENT (error), view->priv->error, NULL);
3117
3118 if (show_save_btn) {
3119 WebKitDOMElement *el;
3120
3121 show_button (view, BUTTON_SAVE);
3122
3123 el = webkit_dom_document_get_element_by_id (
3124 view->priv->dom_document, BUTTON_SAVE);
3125 webkit_dom_html_button_element_set_disabled (
3126 WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), FALSE);
3127 webkit_dom_event_target_add_event_listener (
3128 WEBKIT_DOM_EVENT_TARGET (el), "click",
3129 G_CALLBACK (button_clicked_cb), FALSE, view);
3130 }
3131 }
3132
3133 /******************************************************************************/
3134
3135 typedef struct {
3136 EMailPartItip *puri;
3137 ItipView *view;
3138 GCancellable *itip_cancellable;
3139 GCancellable *cancellable;
3140 gulong cancelled_id;
3141 gboolean keep_alarm_check;
3142 GHashTable *conflicts;
3143
3144 gchar *uid;
3145 gchar *rid;
3146
3147 gchar *sexp;
3148
3149 gint count;
3150 } FormatItipFindData;
3151
3152 static gboolean check_is_instance (icalcomponent *icalcomp);
3153
3154 static icalproperty *
3155 find_attendee (icalcomponent *ical_comp,
3156 const gchar *address)
3157 {
3158 icalproperty *prop;
3159
3160 if (address == NULL)
3161 return NULL;
3162
3163 for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
3164 prop != NULL;
3165 prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
3166 gchar *attendee;
3167 gchar *text;
3168
3169 attendee = icalproperty_get_value_as_string_r (prop);
3170
3171 if (!attendee)
3172 continue;
3173
3174 text = g_strdup (itip_strip_mailto (attendee));
3175 text = g_strstrip (text);
3176 if (text && !g_ascii_strcasecmp (address, text)) {
3177 g_free (text);
3178 g_free (attendee);
3179 break;
3180 }
3181 g_free (text);
3182 g_free (attendee);
3183 }
3184
3185 return prop;
3186 }
3187
3188 static icalproperty *
3189 find_attendee_if_sentby (icalcomponent *ical_comp,
3190 const gchar *address)
3191 {
3192 icalproperty *prop;
3193
3194 if (address == NULL)
3195 return NULL;
3196
3197 for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
3198 prop != NULL;
3199 prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
3200 icalparameter *param;
3201 const gchar *attendee_sentby;
3202 gchar *text;
3203
3204 param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
3205 if (!param)
3206 continue;
3207
3208 attendee_sentby = icalparameter_get_sentby (param);
3209
3210 if (!attendee_sentby)
3211 continue;
3212
3213 text = g_strdup (itip_strip_mailto (attendee_sentby));
3214 text = g_strstrip (text);
3215 if (text && !g_ascii_strcasecmp (address, text)) {
3216 g_free (text);
3217 break;
3218 }
3219 g_free (text);
3220 }
3221
3222 return prop;
3223 }
3224
3225 static void
3226 find_to_address (EMailPartItip *itip_part,
3227 icalcomponent *ical_comp,
3228 icalparameter_partstat *status)
3229 {
3230 ESourceRegistry *registry;
3231 ESourceMailIdentity *extension;
3232 GList *list, *link;
3233 const gchar *extension_name;
3234
3235 registry = itip_part->registry;
3236 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
3237
3238 if (itip_part->to_address != NULL)
3239 return;
3240
3241 if (itip_part->msg != NULL && itip_part->folder != NULL) {
3242 ESource *source;
3243
3244 source = em_utils_guess_mail_identity (
3245 registry, itip_part->msg, itip_part->folder, itip_part->uid);
3246
3247 if (source != NULL) {
3248 extension = e_source_get_extension (source, extension_name);
3249
3250 itip_part->to_address = e_source_mail_identity_dup_address (extension);
3251
3252 g_object_unref (source);
3253 }
3254 }
3255
3256 if (itip_part->to_address != NULL)
3257 return;
3258
3259 /* Look through the list of attendees to find the user's address */
3260 list = e_source_registry_list_sources (registry, extension_name);
3261
3262 for (link = list; link != NULL; link = g_list_next (link)) {
3263 ESource *source = E_SOURCE (link->data);
3264 icalproperty *prop = NULL;
3265 icalparameter *param;
3266 const gchar *address;
3267 gchar *text;
3268
3269 if (!e_source_get_enabled (source))
3270 continue;
3271
3272 extension = e_source_get_extension (source, extension_name);
3273 address = e_source_mail_identity_get_address (extension);
3274
3275 prop = find_attendee (ical_comp, address);
3276 if (prop == NULL)
3277 continue;
3278
3279 param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
3280 if (param != NULL)
3281 itip_part->to_name = g_strdup (icalparameter_get_cn (param));
3282
3283 text = icalproperty_get_value_as_string_r (prop);
3284
3285 itip_part->to_address = g_strdup (itip_strip_mailto (text));
3286 g_free (text);
3287 g_strstrip (itip_part->to_address);
3288
3289 itip_part->my_address = g_strdup (address);
3290
3291 param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
3292 if (param != NULL &&
3293 icalparameter_get_rsvp (param) == ICAL_RSVP_FALSE)
3294 itip_part->no_reply_wanted = TRUE;
3295
3296 if (status) {
3297 param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
3298 *status = param ? icalparameter_get_partstat (param) : ICAL_PARTSTAT_NEEDSACTION;
3299 }
3300
3301 break;
3302 }
3303
3304 g_list_free_full (list, (GDestroyNotify) g_object_unref);
3305
3306 if (itip_part->to_address != NULL)
3307 return;
3308
3309 /* If the user's address was not found in the attendee's list,
3310 * then the user might be responding on behalf of his/her delegator.
3311 * In this case, we would want to go through the SENT-BY fields of
3312 * the attendees to find the user's address.
3313 *
3314 *
3315 * Note: This functionality could have been (easily) implemented
3316 * in the previous loop, but it would hurt the performance for all
3317 * providers in general. Hence, we choose to iterate through the
3318 * accounts list again.
3319 */
3320
3321 list = e_source_registry_list_sources (registry, extension_name);
3322
3323 for (link = list; link != NULL; link = g_list_next (link)) {
3324 ESource *source = E_SOURCE (link->data);
3325 icalproperty *prop = NULL;
3326 icalparameter *param;
3327 const gchar *address;
3328 gchar *text;
3329
3330 if (!e_source_get_enabled (source))
3331 continue;
3332
3333 extension = e_source_get_extension (source, extension_name);
3334 address = e_source_mail_identity_get_address (extension);
3335
3336 prop = find_attendee_if_sentby (ical_comp, address);
3337 if (prop == NULL)
3338 continue;
3339
3340 param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
3341 if (param != NULL)
3342 itip_part->to_name = g_strdup (icalparameter_get_cn (param));
3343
3344 text = icalproperty_get_value_as_string_r (prop);
3345
3346 itip_part->to_address = g_strdup (itip_strip_mailto (text));
3347 g_free (text);
3348 g_strstrip (itip_part->to_address);
3349
3350 itip_part->my_address = g_strdup (address);
3351
3352 param = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER);
3353 if (param != NULL &&
3354 ICAL_RSVP_FALSE == icalparameter_get_rsvp (param))
3355 itip_part->no_reply_wanted = TRUE;
3356
3357 if (status) {
3358 param = icalproperty_get_first_parameter (prop, ICAL_PARTSTAT_PARAMETER);
3359 *status = param ? icalparameter_get_partstat (param) : ICAL_PARTSTAT_NEEDSACTION;
3360 }
3361
3362 break;
3363 }
3364
3365 g_list_free_full (list, (GDestroyNotify) g_object_unref);
3366 }
3367
3368 static void
3369 find_from_address (EMailPartItip *pitip,
3370 icalcomponent *ical_comp)
3371 {
3372 GList *list, *link;
3373 icalproperty *prop;
3374 gchar *organizer;
3375 icalparameter *param;
3376 const gchar *extension_name;
3377 const gchar *organizer_sentby;
3378 gchar *organizer_clean = NULL;
3379 gchar *organizer_sentby_clean = NULL;
3380
3381 prop = icalcomponent_get_first_property (ical_comp, ICAL_ORGANIZER_PROPERTY);
3382
3383 if (!prop)
3384 return;
3385
3386 organizer = icalproperty_get_value_as_string_r (prop);
3387 if (organizer) {
3388 organizer_clean = g_strdup (itip_strip_mailto (organizer));
3389 organizer_clean = g_strstrip (organizer_clean);
3390 g_free (organizer);
3391 }
3392
3393 param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
3394 if (param) {
3395 organizer_sentby = icalparameter_get_sentby (param);
3396 if (organizer_sentby) {
3397 organizer_sentby_clean = g_strdup (itip_strip_mailto (organizer_sentby));
3398 organizer_sentby_clean = g_strstrip (organizer_sentby_clean);
3399 }
3400 }
3401
3402 if (!(organizer_sentby_clean || organizer_clean))
3403 return;
3404
3405 pitip->from_address = g_strdup (organizer_clean);
3406
3407 param = icalproperty_get_first_parameter (prop, ICAL_CN_PARAMETER);
3408 if (param)
3409 pitip->from_name = g_strdup (icalparameter_get_cn (param));
3410
3411 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
3412 list = e_source_registry_list_sources (pitip->registry, extension_name);
3413
3414 for (link = list; link != NULL; link = g_list_next (link)) {
3415 ESource *source = E_SOURCE (link->data);
3416 ESourceMailIdentity *extension;
3417 const gchar *address;
3418
3419 if (!e_source_get_enabled (source))
3420 continue;
3421
3422 extension = e_source_get_extension (source, extension_name);
3423 address = e_source_mail_identity_get_address (extension);
3424
3425 if (address == NULL)
3426 continue;
3427
3428 if ((organizer_clean && !g_ascii_strcasecmp (organizer_clean, address))
3429 || (organizer_sentby_clean && !g_ascii_strcasecmp (organizer_sentby_clean, address))) {
3430 pitip->my_address = g_strdup (address);
3431
3432 break;
3433 }
3434 }
3435
3436 g_list_free_full (list, (GDestroyNotify) g_object_unref);
3437
3438 g_free (organizer_sentby_clean);
3439 g_free (organizer_clean);
3440 }
3441
3442 static ECalComponent *
3443 get_real_item (EMailPartItip *pitip)
3444 {
3445 ECalComponent *comp = NULL;
3446 ESource *source;
3447
3448 source = e_client_get_source (E_CLIENT (pitip->current_client));
3449 if (source)
3450 comp = g_hash_table_lookup (pitip->real_comps, e_source_get_uid (source));
3451
3452 if (!comp) {
3453 return NULL;
3454 }
3455
3456 return e_cal_component_clone (comp);
3457 }
3458
3459 static void
3460 adjust_item (EMailPartItip *pitip,
3461 ECalComponent *comp)
3462 {
3463 ECalComponent *real_comp;
3464
3465 real_comp = get_real_item (pitip);
3466 if (real_comp != NULL) {
3467 ECalComponentText text;
3468 const gchar *string;
3469 GSList *l;
3470
3471 e_cal_component_get_summary (real_comp, &text);
3472 e_cal_component_set_summary (comp, &text);
3473 e_cal_component_get_location (real_comp, &string);
3474 e_cal_component_set_location (comp, string);
3475 e_cal_component_get_description_list (real_comp, &l);
3476 e_cal_component_set_description_list (comp, l);
3477 e_cal_component_free_text_list (l);
3478
3479 g_object_unref (real_comp);
3480 } else {
3481 ECalComponentText text = {_("Unknown"), NULL};
3482
3483 e_cal_component_set_summary (comp, &text);
3484 }
3485 }
3486
3487 static void
3488 set_buttons_sensitive (EMailPartItip *pitip,
3489 ItipView *view)
3490 {
3491 gboolean read_only = TRUE;
3492
3493 if (pitip->current_client)
3494 read_only = e_client_is_readonly (E_CLIENT (pitip->current_client));
3495
3496 itip_view_set_buttons_sensitive (view, pitip->current_client != NULL && !read_only);
3497 }
3498
3499 static void
3500 add_failed_to_load_msg (ItipView *view,
3501 ESource *source,
3502 const GError *error)
3503 {
3504 gchar *msg;
3505
3506 g_return_if_fail (view != NULL);
3507 g_return_if_fail (source != NULL);
3508 g_return_if_fail (error != NULL);
3509
3510 /* Translators: The first '%s' is replaced with a calendar name,
3511 * the second '%s' with an error message */
3512 msg = g_strdup_printf (_("Failed to load the calendar '%s' (%s)"), e_source_get_display_name (source), error->message);
3513
3514 itip_view_add_lower_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING, msg);
3515
3516 g_free (msg);
3517 }
3518
3519 static void
3520 cal_opened_cb (GObject *source_object,
3521 GAsyncResult *result,
3522 gpointer user_data)
3523 {
3524 ESource *source = E_SOURCE (source_object);
3525 ItipView *view = user_data;
3526 EMailPartItip *pitip = itip_view_get_mail_part (view);
3527 ECalClientSourceType source_type;
3528 EClient *client = NULL;
3529 ECalClient *cal_client;
3530 const gchar *uid;
3531 GError *error = NULL;
3532
3533 e_client_utils_open_new_finish (source, result, &client, &error);
3534
3535 /* Ignore cancellations. */
3536 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
3537 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
3538 g_warn_if_fail (client == NULL);
3539 g_error_free (error);
3540 return;
3541
3542 } else if (error != NULL) {
3543 g_warn_if_fail (client == NULL);
3544 add_failed_to_load_msg (view, source, error);
3545 g_error_free (error);
3546 return;
3547 }
3548
3549 g_return_if_fail (E_IS_CAL_CLIENT (client));
3550
3551 cal_client = E_CAL_CLIENT (client);
3552 g_return_if_fail (cal_client != NULL);
3553
3554 uid = e_source_get_uid (source);
3555 source_type = e_cal_client_get_source_type (cal_client);
3556 g_hash_table_insert (
3557 pitip->clients[source_type], g_strdup (uid), cal_client);
3558
3559 if (e_cal_client_check_recurrences_no_master (cal_client)) {
3560 icalcomponent *icalcomp;
3561 gboolean show_recur_check;
3562
3563 icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
3564
3565 show_recur_check = check_is_instance (icalcomp);
3566 itip_view_set_show_recur_check (view, show_recur_check);
3567 }
3568
3569 if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
3570 gboolean needs_decline;
3571
3572 needs_decline = e_client_check_capability (
3573 E_CLIENT (client),
3574 CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING);
3575 itip_view_set_needs_decline (view, needs_decline);
3576 itip_view_set_mode (view, ITIP_VIEW_MODE_PUBLISH);
3577 }
3578
3579 pitip->current_client = cal_client;
3580
3581 set_buttons_sensitive (pitip, view);
3582 }
3583
3584 static void
3585 start_calendar_server (EMailPartItip *pitip,
3586 ItipView *view,
3587 ESource *source,
3588 ECalClientSourceType type,
3589 GAsyncReadyCallback func,
3590 gpointer data)
3591 {
3592 ECalClient *client;
3593
3594 g_return_if_fail (source != NULL);
3595
3596 client = g_hash_table_lookup (pitip->clients[type], e_source_get_uid (source));
3597 if (client) {
3598 pitip->current_client = client;
3599
3600 itip_view_remove_lower_info_item (view, pitip->progress_info_id);
3601 pitip->progress_info_id = 0;
3602
3603 set_buttons_sensitive (pitip, view);
3604
3605 return;
3606 }
3607
3608 e_client_utils_open_new (
3609 source,
3610 type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS ? E_CLIENT_SOURCE_TYPE_EVENTS :
3611 type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS ? E_CLIENT_SOURCE_TYPE_MEMOS :
3612 type == E_CAL_CLIENT_SOURCE_TYPE_TASKS ? E_CLIENT_SOURCE_TYPE_TASKS : E_CLIENT_SOURCE_TYPE_LAST,
3613 TRUE, pitip->cancellable,
3614 func, data);
3615 }
3616
3617 static void
3618 start_calendar_server_by_uid (EMailPartItip *pitip,
3619 ItipView *view,
3620 const gchar *uid,
3621 ECalClientSourceType type)
3622 {
3623 ESource *source;
3624
3625 itip_view_set_buttons_sensitive (view, FALSE);
3626
3627 source = e_source_registry_ref_source (pitip->registry, uid);
3628 if (source != NULL) {
3629 start_calendar_server (
3630 pitip, view, source, type, cal_opened_cb, view);
3631 g_object_unref (source);
3632 }
3633 }
3634
3635 static void
3636 source_selected_cb (ItipView *view,
3637 ESource *source,
3638 gpointer data)
3639 {
3640 EMailPartItip *pitip = data;
3641
3642 itip_view_set_buttons_sensitive (view, FALSE);
3643
3644 g_return_if_fail (source != NULL);
3645
3646 start_calendar_server (pitip, view, source, pitip->type, cal_opened_cb, view);
3647 }
3648
3649 static void
3650 find_cal_update_ui (FormatItipFindData *fd,
3651 ECalClient *cal_client)
3652 {
3653 EMailPartItip *pitip;
3654 ItipView *view;
3655 ESource *source;
3656
3657 g_return_if_fail (fd != NULL);
3658
3659 pitip = fd->puri;
3660 view = fd->view;
3661
3662 /* UI part gone */
3663 if (g_cancellable_is_cancelled (fd->cancellable))
3664 return;
3665
3666 source = cal_client ? e_client_get_source (E_CLIENT (cal_client)) : NULL;
3667
3668 if (cal_client && g_hash_table_lookup (fd->conflicts, cal_client)) {
3669 itip_view_add_upper_info_item_printf (
3670 view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
3671 _("An appointment in the calendar "
3672 "'%s' conflicts with this meeting"),
3673 e_source_get_display_name (source));
3674 }
3675
3676 /* search for a master object if the detached object doesn't exist in the calendar */
3677 if (pitip->current_client && pitip->current_client == cal_client) {
3678 gboolean rsvp_enabled = FALSE;
3679
3680 itip_view_set_show_keep_alarm_check (view, fd->keep_alarm_check);
3681
3682 pitip->current_client = cal_client;
3683
3684 /* Provide extra info, since its not in the component */
3685 /* FIXME Check sequence number of meeting? */
3686 /* FIXME Do we need to adjust elsewhere for the delegated calendar item? */
3687 /* FIXME Need to update the fields in the view now */
3688 if (pitip->method == ICAL_METHOD_REPLY || pitip->method == ICAL_METHOD_REFRESH)
3689 adjust_item (pitip, pitip->comp);
3690
3691 /* We clear everything because we don't really care
3692 * about any other info/warnings now we found an
3693 * existing versions */
3694 itip_view_clear_lower_info_items (view);
3695 pitip->progress_info_id = 0;
3696
3697 /* FIXME Check read only state of calendar? */
3698 itip_view_add_lower_info_item_printf (
3699 view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
3700 _("Found the appointment in the calendar '%s'"), e_source_get_display_name (source));
3701
3702 /*
3703 * Only allow replies if backend doesn't do that automatically.
3704 * Only enable it for forwarded invitiations (PUBLISH) or direct
3705 * invitiations (REQUEST), but not replies (REPLY).
3706 * Replies only make sense for events with an organizer.
3707 */
3708 if ((!pitip->current_client || !e_cal_client_check_save_schedules (pitip->current_client)) &&
3709 (pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST) &&
3710 pitip->has_organizer) {
3711 rsvp_enabled = TRUE;
3712 }
3713 itip_view_set_show_rsvp_check (view, rsvp_enabled);
3714
3715 /* default is chosen in extract_itip_data() based on content of the VEVENT */
3716 itip_view_set_rsvp (view, !pitip->no_reply_wanted);
3717
3718 set_buttons_sensitive (pitip, view);
3719
3720 g_cancellable_cancel (fd->cancellable);
3721 } else if (!pitip->current_client)
3722 itip_view_set_show_keep_alarm_check (view, FALSE);
3723
3724 if (pitip->current_client && pitip->current_client == cal_client) {
3725 if (e_cal_client_check_recurrences_no_master (pitip->current_client)) {
3726 icalcomponent *icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
3727
3728 if (check_is_instance (icalcomp))
3729 itip_view_set_show_recur_check (view, TRUE);
3730 else
3731 itip_view_set_show_recur_check (view, FALSE);
3732 }
3733
3734 if (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_MEMOS) {
3735 /* TODO The static capability should be made generic to convey that the calendar contains unaccepted items */
3736 if (e_client_check_capability (E_CLIENT (pitip->current_client), CAL_STATIC_CAPABILITY_HAS_UNACCEPTED_MEETING))
3737 itip_view_set_needs_decline (view, TRUE);
3738 else
3739 itip_view_set_needs_decline (view, FALSE);
3740
3741 itip_view_set_mode (view, ITIP_VIEW_MODE_PUBLISH);
3742 }
3743 }
3744 }
3745
3746 static void
3747 decrease_find_data (FormatItipFindData *fd)
3748 {
3749 g_return_if_fail (fd != NULL);
3750
3751 fd->count--;
3752 d (printf ("Decreasing itip formatter search count to %d\n", fd->count));
3753
3754 if (fd->count == 0 && !g_cancellable_is_cancelled (fd->cancellable)) {
3755 gboolean rsvp_enabled = FALSE;
3756 EMailPartItip *pitip = fd->puri;
3757 ItipView *view = fd->view;
3758
3759 itip_view_remove_lower_info_item (view, pitip->progress_info_id);
3760 pitip->progress_info_id = 0;
3761
3762 /*
3763 * Only allow replies if backend doesn't do that automatically.
3764 * Only enable it for forwarded invitiations (PUBLISH) or direct
3765 * invitiations (REQUEST), but not replies (REPLY).
3766 * Replies only make sense for events with an organizer.
3767 */
3768 if ((!pitip->current_client || !e_cal_client_check_save_schedules (pitip->current_client)) &&
3769 (pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST) &&
3770 pitip->has_organizer) {
3771 rsvp_enabled = TRUE;
3772 }
3773 itip_view_set_show_rsvp_check (view, rsvp_enabled);
3774
3775 /* default is chosen in extract_itip_data() based on content of the VEVENT */
3776 itip_view_set_rsvp (view, !pitip->no_reply_wanted);
3777
3778 if ((pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST)
3779 && !pitip->current_client) {
3780 /* Reuse already declared one or rename? */
3781 ESource *source = NULL;
3782 const gchar *extension_name;
3783
3784 switch (pitip->type) {
3785 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
3786 extension_name = E_SOURCE_EXTENSION_CALENDAR;
3787 break;
3788 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
3789 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
3790 break;
3791 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
3792 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
3793 break;
3794 default:
3795 g_return_if_reached ();
3796 }
3797
3798 source = e_source_registry_ref_default_for_extension_name (
3799 pitip->registry, extension_name);
3800
3801 itip_view_set_extension_name (view, extension_name);
3802
3803 g_signal_connect (
3804 view, "source_selected",
3805 G_CALLBACK (source_selected_cb), pitip);
3806
3807 if (source != NULL) {
3808 itip_view_set_source (view, source);
3809 g_object_unref (source);
3810
3811 /* FIXME Shouldn't the buttons be sensitized here? */
3812 } else {
3813 itip_view_add_lower_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR, _("Unable to find any calendars"));
3814 itip_view_set_buttons_sensitive (view, FALSE);
3815 }
3816 } else if (!pitip->current_client) {
3817 switch (pitip->type) {
3818 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
3819 itip_view_add_lower_info_item_printf (
3820 view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
3821 _("Unable to find this meeting in any calendar"));
3822 break;
3823 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
3824 itip_view_add_lower_info_item_printf (
3825 view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
3826 _("Unable to find this task in any task list"));
3827 break;
3828 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
3829 itip_view_add_lower_info_item_printf (
3830 view, ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
3831 _("Unable to find this memo in any memo list"));
3832 break;
3833 default:
3834 g_assert_not_reached ();
3835 break;
3836 }
3837 }
3838 }
3839
3840 if (fd->count == 0) {
3841 g_hash_table_destroy (fd->conflicts);
3842 g_cancellable_disconnect (fd->itip_cancellable, fd->cancelled_id);
3843 g_object_unref (fd->cancellable);
3844 g_object_unref (fd->itip_cancellable);
3845 g_object_unref (fd->view);
3846 g_free (fd->uid);
3847 g_free (fd->rid);
3848 if (fd->sexp)
3849 g_free (fd->sexp);
3850 g_free (fd);
3851 }
3852 }
3853
3854 static void
3855 get_object_without_rid_ready_cb (GObject *source_object,
3856 GAsyncResult *result,
3857 gpointer user_data)
3858 {
3859 ECalClient *cal_client = E_CAL_CLIENT (source_object);
3860 FormatItipFindData *fd = user_data;
3861 icalcomponent *icalcomp = NULL;
3862 GError *error = NULL;
3863
3864 if (!e_cal_client_get_object_finish (cal_client, result, &icalcomp, &error))
3865 icalcomp = NULL;
3866
3867 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
3868 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
3869 g_cancellable_is_cancelled (fd->cancellable)) {
3870 g_clear_error (&error);
3871 find_cal_update_ui (fd, cal_client);
3872 decrease_find_data (fd);
3873 return;
3874 }
3875
3876 g_clear_error (&error);
3877
3878 if (icalcomp) {
3879 ECalComponent *comp;
3880
3881 fd->puri->current_client = cal_client;
3882 fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method == ICAL_METHOD_REQUEST) &&
3883 (icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) ||
3884 icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) ||
3885 icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) ||
3886 icalcomponent_get_first_component (icalcomp, ICAL_XPROCEDUREALARM_COMPONENT) ||
3887 icalcomponent_get_first_component (icalcomp, ICAL_XEMAILALARM_COMPONENT));
3888
3889 comp = e_cal_component_new_from_icalcomponent (icalcomp);
3890 if (comp) {
3891 ESource *source = e_client_get_source (E_CLIENT (cal_client));
3892
3893 g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), comp);
3894 }
3895
3896 find_cal_update_ui (fd, cal_client);
3897 decrease_find_data (fd);
3898 return;
3899 }
3900
3901 find_cal_update_ui (fd, cal_client);
3902 decrease_find_data (fd);
3903 }
3904
3905 static void
3906 get_object_with_rid_ready_cb (GObject *source_object,
3907 GAsyncResult *result,
3908 gpointer user_data)
3909 {
3910 ECalClient *cal_client = E_CAL_CLIENT (source_object);
3911 FormatItipFindData *fd = user_data;
3912 icalcomponent *icalcomp = NULL;
3913 GError *error = NULL;
3914
3915 if (!e_cal_client_get_object_finish (cal_client, result, &icalcomp, &error))
3916 icalcomp = NULL;
3917
3918 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
3919 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
3920 g_cancellable_is_cancelled (fd->cancellable)) {
3921 g_clear_error (&error);
3922 find_cal_update_ui (fd, cal_client);
3923 decrease_find_data (fd);
3924 return;
3925 }
3926
3927 g_clear_error (&error);
3928
3929 if (icalcomp) {
3930 ECalComponent *comp;
3931
3932 fd->puri->current_client = cal_client;
3933 fd->keep_alarm_check = (fd->puri->method == ICAL_METHOD_PUBLISH || fd->puri->method == ICAL_METHOD_REQUEST) &&
3934 (icalcomponent_get_first_component (icalcomp, ICAL_VALARM_COMPONENT) ||
3935 icalcomponent_get_first_component (icalcomp, ICAL_XAUDIOALARM_COMPONENT) ||
3936 icalcomponent_get_first_component (icalcomp, ICAL_XDISPLAYALARM_COMPONENT) ||
3937 icalcomponent_get_first_component (icalcomp, ICAL_XPROCEDUREALARM_COMPONENT) ||
3938 icalcomponent_get_first_component (icalcomp, ICAL_XEMAILALARM_COMPONENT));
3939
3940 comp = e_cal_component_new_from_icalcomponent (icalcomp);
3941 if (comp) {
3942 ESource *source = e_client_get_source (E_CLIENT (cal_client));
3943
3944 g_hash_table_insert (fd->puri->real_comps, g_strdup (e_source_get_uid (source)), comp);
3945 }
3946
3947 find_cal_update_ui (fd, cal_client);
3948 decrease_find_data (fd);
3949 return;
3950 }
3951
3952 if (fd->rid && *fd->rid) {
3953 e_cal_client_get_object (cal_client, fd->uid, NULL, fd->cancellable, get_object_without_rid_ready_cb, fd);
3954 return;
3955 }
3956
3957 find_cal_update_ui (fd, cal_client);
3958 decrease_find_data (fd);
3959 }
3960
3961 static void
3962 get_object_list_ready_cb (GObject *source_object,
3963 GAsyncResult *result,
3964 gpointer user_data)
3965 {
3966 ECalClient *cal_client = E_CAL_CLIENT (source_object);
3967 FormatItipFindData *fd = user_data;
3968 GSList *objects = NULL;
3969 GError *error = NULL;
3970
3971 if (!e_cal_client_get_object_list_finish (cal_client, result, &objects, &error))
3972 objects = NULL;
3973
3974 if (g_cancellable_is_cancelled (fd->cancellable)) {
3975 g_clear_error (&error);
3976 decrease_find_data (fd);
3977 return;
3978 }
3979
3980 if (error) {
3981 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
3982 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
3983 g_error_free (error);
3984 decrease_find_data (fd);
3985 return;
3986 }
3987
3988 g_error_free (error);
3989 } else {
3990 g_hash_table_insert (fd->conflicts, cal_client, GINT_TO_POINTER (g_slist_length (objects)));
3991 e_cal_client_free_icalcomp_slist (objects);
3992 }
3993
3994 e_cal_client_get_object (cal_client, fd->uid, fd->rid, fd->cancellable, get_object_with_rid_ready_cb, fd);
3995 }
3996
3997 static void
3998 find_cal_opened_cb (GObject *source_object,
3999 GAsyncResult *result,
4000 gpointer user_data)
4001 {
4002 ESource *source = E_SOURCE (source_object);
4003 FormatItipFindData *fd = user_data;
4004 EMailPartItip *pitip = fd->puri;
4005 ItipView *view = fd->view;
4006 ECalClientSourceType source_type;
4007 EClient *client = NULL;
4008 ECalClient *cal_client;
4009 gboolean search_for_conflicts = FALSE;
4010 const gchar *extension_name;
4011 const gchar *uid;
4012 GError *error = NULL;
4013
4014 e_client_utils_open_new_finish (source, result, &client, &error);
4015
4016 /* Ignore cancellations. */
4017 if (g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
4018 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
4019 g_warn_if_fail (client == NULL);
4020 decrease_find_data (fd);
4021 g_error_free (error);
4022 return;
4023 }
4024
4025 if (g_cancellable_is_cancelled (fd->cancellable)) {
4026 g_clear_error (&error);
4027 decrease_find_data (fd);
4028 return;
4029 }
4030
4031 if (error) {
4032 /* FIXME Do we really want to warn here? If we fail
4033 * to find the item, this won't be cleared but the
4034 * selector might be shown */
4035 g_warn_if_fail (client == NULL);
4036 add_failed_to_load_msg (view, source, error);
4037 decrease_find_data (fd);
4038 g_error_free (error);
4039 return;
4040 }
4041
4042 g_return_if_fail (E_IS_CAL_CLIENT (client));
4043
4044 /* Do not process read-only calendars */
4045 if (e_client_is_readonly (client)) {
4046 g_object_unref (client);
4047 decrease_find_data (fd);
4048 return;
4049 }
4050
4051 cal_client = E_CAL_CLIENT (client);
4052 source_type = e_cal_client_get_source_type (cal_client);
4053
4054 uid = e_source_get_uid (source);
4055 g_hash_table_insert (
4056 pitip->clients[source_type], g_strdup (uid), cal_client);
4057
4058 extension_name = E_SOURCE_EXTENSION_CONFLICT_SEARCH;
4059 if (e_source_has_extension (source, extension_name)) {
4060 ESourceConflictSearch *extension;
4061
4062 extension = e_source_get_extension (source, extension_name);
4063 search_for_conflicts =
4064 (pitip->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) &&
4065 e_source_conflict_search_get_include_me (extension);
4066 }
4067
4068 /* Check for conflicts */
4069 /* If the query fails, we'll just ignore it */
4070 /* FIXME What happens for recurring conflicts? */
4071 if (search_for_conflicts) {
4072 e_cal_client_get_object_list (
4073 cal_client, fd->sexp,
4074 fd->cancellable,
4075 get_object_list_ready_cb, fd);
4076 return;
4077 }
4078
4079 if (!pitip->current_client) {
4080 e_cal_client_get_object (
4081 cal_client, fd->uid, fd->rid,
4082 fd->cancellable,
4083 get_object_with_rid_ready_cb, fd);
4084 return;
4085 }
4086
4087 decrease_find_data (fd);
4088 }
4089
4090 static void
4091 itip_cancellable_cancelled (GCancellable *itip_cancellable,
4092 GCancellable *fd_cancellable)
4093 {
4094 g_cancellable_cancel (fd_cancellable);
4095 }
4096
4097 static void
4098 find_server (EMailPartItip *pitip,
4099 ItipView *view,
4100 ECalComponent *comp)
4101 {
4102 FormatItipFindData *fd = NULL;
4103 const gchar *uid;
4104 gchar *rid = NULL;
4105 CamelStore *parent_store;
4106 ESourceRegistry *registry;
4107 ESource *current_source = NULL;
4108 GList *list, *link;
4109 GList *conflict_list = NULL;
4110 const gchar *extension_name;
4111 const gchar *store_uid;
4112
4113 g_return_if_fail (pitip->folder != NULL);
4114
4115 switch (pitip->type) {
4116 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
4117 extension_name = E_SOURCE_EXTENSION_CALENDAR;
4118 break;
4119 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
4120 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
4121 break;
4122 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
4123 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
4124 break;
4125 default:
4126 g_return_if_reached ();
4127 }
4128
4129 registry = pitip->registry;
4130 list = e_source_registry_list_sources (registry, extension_name);
4131
4132 e_cal_component_get_uid (comp, &uid);
4133 rid = e_cal_component_get_recurid_as_string (comp);
4134
4135 /* XXX Not sure what this was trying to do,
4136 * but it propbably doesn't work anymore.
4137 * Some comments would have been helpful. */
4138 parent_store = camel_folder_get_parent_store (pitip->folder);
4139
4140 store_uid = camel_service_get_uid (CAMEL_SERVICE (parent_store));
4141
4142 itip_view_set_buttons_sensitive (view, FALSE);
4143
4144 for (link = list; link != NULL; link = g_list_next (link)) {
4145 ESource *source = E_SOURCE (link->data);
4146 gboolean search_for_conflicts = FALSE;
4147 const gchar *source_uid;
4148
4149 extension_name = E_SOURCE_EXTENSION_CONFLICT_SEARCH;
4150 if (e_source_has_extension (source, extension_name)) {
4151 ESourceConflictSearch *extension;
4152
4153 extension =
4154 e_source_get_extension (source, extension_name);
4155 search_for_conflicts =
4156 e_source_conflict_search_get_include_me (extension);
4157 }
4158
4159 if (search_for_conflicts)
4160 conflict_list = g_list_prepend (
4161 conflict_list, g_object_ref (source));
4162
4163 if (current_source != NULL)
4164 continue;
4165
4166 source_uid = e_source_get_uid (source);
4167 if (g_strcmp0 (source_uid, store_uid) == 0) {
4168 current_source = source;
4169 conflict_list = g_list_prepend (
4170 conflict_list, g_object_ref (source));
4171
4172 continue;
4173 }
4174 }
4175
4176 if (current_source) {
4177 link = conflict_list;
4178
4179 pitip->progress_info_id = itip_view_add_lower_info_item (
4180 view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
4181 _("Opening the calendar. Please wait..."));
4182 } else {
4183 link = list;
4184 pitip->progress_info_id = itip_view_add_lower_info_item (
4185 view, ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
4186 _("Searching for an existing version of this appointment"));
4187 }
4188
4189 for (; link != NULL; link = g_list_next (link)) {
4190 ESource *source = E_SOURCE (link->data);
4191
4192 if (!fd) {
4193 gchar *start = NULL, *end = NULL;
4194
4195 fd = g_new0 (FormatItipFindData, 1);
4196 fd->puri = pitip;
4197 fd->view = g_object_ref (view);
4198 fd->itip_cancellable = g_object_ref (pitip->cancellable);
4199 fd->cancellable = g_cancellable_new ();
4200 fd->cancelled_id = g_cancellable_connect (
4201 fd->itip_cancellable,
4202 G_CALLBACK (itip_cancellable_cancelled), fd->cancellable, NULL);
4203 fd->conflicts = g_hash_table_new (g_direct_hash, g_direct_equal);
4204 fd->uid = g_strdup (uid);
4205 fd->rid = rid;
4206 /* avoid free this at the end */
4207 rid = NULL;
4208
4209 if (pitip->start_time && pitip->end_time) {
4210 start = isodate_from_time_t (pitip->start_time);
4211 end = isodate_from_time_t (pitip->end_time);
4212
4213 fd->sexp = g_strdup_printf (
4214 "(and (occur-in-time-range? "
4215 "(make-time \"%s\") "
4216 "(make-time \"%s\")) "
4217 "(not (uid? \"%s\")))",
4218 start, end,
4219 icalcomponent_get_uid (pitip->ical_comp));
4220 }
4221
4222 g_free (start);
4223 g_free (end);
4224 }
4225 fd->count++;
4226 d (printf ("Increasing itip formatter search count to %d\n", fd->count));
4227
4228 if (current_source == source)
4229 start_calendar_server (
4230 pitip, view, source, pitip->type,
4231 find_cal_opened_cb, fd);
4232 else
4233 start_calendar_server (
4234 pitip, view, source, pitip->type,
4235 find_cal_opened_cb, fd);
4236 }
4237
4238 g_list_free_full (conflict_list, (GDestroyNotify) g_object_unref);
4239 g_list_free_full (list, (GDestroyNotify) g_object_unref);
4240
4241 g_free (rid);
4242 }
4243
4244 static gboolean
4245 change_status (ESourceRegistry *registry,
4246 icalcomponent *ical_comp,
4247 const gchar *address,
4248 icalparameter_partstat status)
4249 {
4250 icalproperty *prop;
4251
4252 prop = find_attendee (ical_comp, address);
4253 if (prop) {
4254 icalparameter *param;
4255
4256 icalproperty_remove_parameter (prop, ICAL_PARTSTAT_PARAMETER);
4257 param = icalparameter_new_partstat (status);
4258 icalproperty_add_parameter (prop, param);
4259 } else {
4260 icalparameter *param;
4261
4262 if (address != NULL) {
4263 prop = icalproperty_new_attendee (address);
4264 icalcomponent_add_property (ical_comp, prop);
4265
4266 param = icalparameter_new_role (ICAL_ROLE_OPTPARTICIPANT);
4267 icalproperty_add_parameter (prop, param);
4268
4269 param = icalparameter_new_partstat (status);
4270 icalproperty_add_parameter (prop, param);
4271 } else {
4272 gchar *default_name = NULL;
4273 gchar *default_address = NULL;
4274
4275 itip_get_default_name_and_address (
4276 registry, &default_name, &default_address);
4277
4278 prop = icalproperty_new_attendee (default_address);
4279 icalcomponent_add_property (ical_comp, prop);
4280
4281 param = icalparameter_new_cn (default_name);
4282 icalproperty_add_parameter (prop, param);
4283
4284 param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT);
4285 icalproperty_add_parameter (prop, param);
4286
4287 param = icalparameter_new_partstat (status);
4288 icalproperty_add_parameter (prop, param);
4289
4290 g_free (default_name);
4291 g_free (default_address);
4292 }
4293 }
4294
4295 return TRUE;
4296 }
4297
4298 static void
4299 message_foreach_part (CamelMimePart *part,
4300 GSList **part_list)
4301 {
4302 CamelDataWrapper *containee;
4303 gint parts, i;
4304 gint go = TRUE;
4305
4306 if (!part)
4307 return;
4308
4309 *part_list = g_slist_append (*part_list, part);
4310
4311 containee = camel_medium_get_content (CAMEL_MEDIUM (part));
4312
4313 if (containee == NULL)
4314 return;
4315
4316 /* using the object types is more accurate than using the mime/types */
4317 if (CAMEL_IS_MULTIPART (containee)) {
4318 parts = camel_multipart_get_number (CAMEL_MULTIPART (containee));
4319 for (i = 0; go && i < parts; i++) {
4320 /* Reuse already declared *parts? */
4321 CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (containee), i);
4322
4323 message_foreach_part (part, part_list);
4324 }
4325 } else if (CAMEL_IS_MIME_MESSAGE (containee)) {
4326 message_foreach_part ((CamelMimePart *) containee, part_list);
4327 }
4328 }
4329
4330 static void
4331 attachment_load_finished (EAttachment *attachment,
4332 GAsyncResult *result,
4333 gpointer user_data)
4334 {
4335 struct {
4336 GFile *file;
4337 gboolean done;
4338 } *status = user_data;
4339
4340 /* Should be no need to check for error here. */
4341 e_attachment_load_finish (attachment, result, NULL);
4342
4343 status->done = TRUE;
4344 }
4345
4346 static void
4347 attachment_save_finished (EAttachment *attachment,
4348 GAsyncResult *result,
4349 gpointer user_data)
4350 {
4351 GError *error = NULL;
4352
4353 struct {
4354 GFile *file;
4355 gboolean done;
4356 } *status = user_data;
4357
4358 status->file = e_attachment_save_finish (attachment, result, &error);
4359 status->done = TRUE;
4360
4361 /* XXX Error handling needs improvement. */
4362 if (error != NULL) {
4363 g_warning ("%s", error->message);
4364 g_error_free (error);
4365 }
4366 }
4367
4368 static gchar *
4369 get_uri_for_part (CamelMimePart *mime_part)
4370 {
4371 EAttachment *attachment;
4372 GFile *temp_directory;
4373 gchar *template;
4374 gchar *path;
4375
4376 struct {
4377 GFile *file;
4378 gboolean done;
4379 } status;
4380
4381 /* XXX Error handling leaves much to be desired. */
4382
4383 template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ());
4384 path = e_mkdtemp (template);
4385 g_free (template);
4386
4387 if (path == NULL)
4388 return NULL;
4389
4390 temp_directory = g_file_new_for_path (path);
4391 g_free (path);
4392
4393 attachment = e_attachment_new ();
4394 e_attachment_set_mime_part (attachment, mime_part);
4395
4396 status.done = FALSE;
4397
4398 e_attachment_load_async (
4399 attachment, (GAsyncReadyCallback)
4400 attachment_load_finished, &status);
4401
4402 /* Loading should be instantaneous since we already have
4403 * the full content, but we still have to crank the main
4404 * loop until the callback gets triggered. */
4405 while (!status.done)
4406 gtk_main_iteration ();
4407
4408 status.file = NULL;
4409 status.done = FALSE;
4410
4411 e_attachment_save_async (
4412 attachment, temp_directory, (GAsyncReadyCallback)
4413 attachment_save_finished, &status);
4414
4415 /* We can't return until we have results, so crank
4416 * the main loop until the callback gets triggered. */
4417 while (!status.done)
4418 gtk_main_iteration ();
4419
4420 if (status.file != NULL) {
4421 path = g_file_get_path (status.file);
4422 g_object_unref (status.file);
4423 } else
4424 path = NULL;
4425
4426 g_object_unref (attachment);
4427 g_object_unref (temp_directory);
4428
4429 return path;
4430 }
4431
4432 static void
4433 update_item_progress_info (EMailPartItip *pitip,
4434 ItipView *view,
4435 const gchar *message)
4436 {
4437 if (pitip->update_item_progress_info_id) {
4438 itip_view_remove_lower_info_item (view, pitip->update_item_progress_info_id);
4439 pitip->update_item_progress_info_id = 0;
4440
4441 if (!message)
4442 itip_view_set_buttons_sensitive (view, TRUE);
4443 }
4444
4445 if (pitip->update_item_error_info_id) {
4446 itip_view_remove_lower_info_item (view, pitip->update_item_error_info_id);
4447 pitip->update_item_error_info_id = 0;
4448 }
4449
4450 if (message) {
4451 itip_view_set_buttons_sensitive (view, FALSE);
4452 pitip->update_item_progress_info_id =
4453 itip_view_add_lower_info_item (
4454 view,
4455 ITIP_VIEW_INFO_ITEM_TYPE_PROGRESS,
4456 message);
4457 }
4458 }
4459
4460 static void
4461 finish_message_delete_with_rsvp (EMailPartItip *pitip,
4462 ItipView *view,
4463 ECalClient *client)
4464 {
4465 if (pitip->delete_message && pitip->folder)
4466 camel_folder_delete_message (pitip->folder, pitip->uid);
4467
4468 if (itip_view_get_rsvp (view)) {
4469 ECalComponent *comp = NULL;
4470 icalcomponent *ical_comp;
4471 icalproperty *prop;
4472 icalvalue *value;
4473 const gchar *attendee;
4474 gchar *comment;
4475 GSList *l, *list = NULL;
4476 gboolean found;
4477
4478 comp = e_cal_component_clone (pitip->comp);
4479 if (comp == NULL)
4480 return;
4481
4482 if (pitip->to_address == NULL)
4483 find_to_address (pitip, pitip->ical_comp, NULL);
4484 g_assert (pitip->to_address != NULL);
4485
4486 ical_comp = e_cal_component_get_icalcomponent (comp);
4487
4488 /* Remove all attendees except the one we are responding as */
4489 found = FALSE;
4490 for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY);
4491 prop != NULL;
4492 prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) {
4493 gchar *text;
4494
4495 value = icalproperty_get_value (prop);
4496 if (!value)
4497 continue;
4498
4499 attendee = icalvalue_get_string (value);
4500
4501 text = g_strdup (itip_strip_mailto (attendee));
4502 text = g_strstrip (text);
4503
4504 /* We do this to ensure there is at most one
4505 * attendee in the response */
4506 if (found || g_ascii_strcasecmp (pitip->to_address, text))
4507 list = g_slist_prepend (list, prop);
4508 else if (!g_ascii_strcasecmp (pitip->to_address, text))
4509 found = TRUE;
4510 g_free (text);
4511 }
4512
4513 for (l = list; l; l = l->next) {
4514 prop = l->data;
4515 icalcomponent_remove_property (ical_comp, prop);
4516 icalproperty_free (prop);
4517 }
4518 g_slist_free (list);
4519
4520 /* Add a comment if there user set one */
4521 comment = itip_view_get_rsvp_comment (view);
4522 if (comment) {
4523 GSList comments;
4524 ECalComponentText text;
4525
4526 text.value = comment;
4527 text.altrep = NULL;
4528
4529 comments.data = &text;
4530 comments.next = NULL;
4531
4532 e_cal_component_set_comment_list (comp, &comments);
4533
4534 g_free (comment);
4535 }
4536
4537 e_cal_component_rescan (comp);
4538 if (itip_send_comp (
4539 pitip->registry,
4540 E_CAL_COMPONENT_METHOD_REPLY,
4541 comp, pitip->current_client,
4542 pitip->top_level, NULL, NULL, TRUE, FALSE) &&
4543 pitip->folder) {
4544 camel_folder_set_message_flags (
4545 pitip->folder, pitip->uid,
4546 CAMEL_MESSAGE_ANSWERED,
4547 CAMEL_MESSAGE_ANSWERED);
4548 }
4549
4550 g_object_unref (comp);
4551 }
4552
4553 update_item_progress_info (pitip, view, NULL);
4554 }
4555
4556 static void
4557 receive_objects_ready_cb (GObject *ecalclient,
4558 GAsyncResult *result,
4559 gpointer user_data)
4560 {
4561 ECalClient *client = E_CAL_CLIENT (ecalclient);
4562 ESource *source = e_client_get_source (E_CLIENT (client));
4563 ItipView *view = user_data;
4564 EMailPartItip *pitip = itip_view_get_mail_part (view);
4565 GError *error = NULL;
4566
4567 e_cal_client_receive_objects_finish (client, result, &error);
4568
4569 if (error != NULL) {
4570 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
4571 !g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
4572 update_item_progress_info (pitip, view, NULL);
4573 pitip->update_item_error_info_id =
4574 itip_view_add_lower_info_item_printf (
4575 view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4576 _("Unable to send item to calendar '%s'. %s"),
4577 e_source_get_display_name (source),
4578 error->message);
4579 }
4580 g_error_free (error);
4581 return;
4582 }
4583
4584 itip_view_set_extension_name (view, NULL);
4585
4586 itip_view_clear_lower_info_items (view);
4587
4588 switch (pitip->update_item_response) {
4589 case ITIP_VIEW_RESPONSE_ACCEPT:
4590 itip_view_add_lower_info_item_printf (
4591 view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4592 _("Sent to calendar '%s' as accepted"), e_source_get_display_name (source));
4593 break;
4594 case ITIP_VIEW_RESPONSE_TENTATIVE:
4595 itip_view_add_lower_info_item_printf (
4596 view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4597 _("Sent to calendar '%s' as tentative"), e_source_get_display_name (source));
4598 break;
4599 case ITIP_VIEW_RESPONSE_DECLINE:
4600 /* FIXME some calendars just might not save it at all, is this accurate? */
4601 itip_view_add_lower_info_item_printf (
4602 view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4603 _("Sent to calendar '%s' as declined"), e_source_get_display_name (source));
4604 break;
4605 case ITIP_VIEW_RESPONSE_CANCEL:
4606 /* FIXME some calendars just might not save it at all, is this accurate? */
4607 itip_view_add_lower_info_item_printf (
4608 view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4609 _("Sent to calendar '%s' as canceled"), e_source_get_display_name (source));
4610 break;
4611 default:
4612 g_assert_not_reached ();
4613 break;
4614 }
4615
4616 finish_message_delete_with_rsvp (pitip, view, client);
4617 }
4618
4619 static void
4620 update_item (EMailPartItip *pitip,
4621 ItipView *view,
4622 ItipViewResponse response)
4623 {
4624 struct icaltimetype stamp;
4625 icalproperty *prop;
4626 icalcomponent *clone;
4627 ECalComponent *clone_comp;
4628 gchar *str;
4629
4630 update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));
4631
4632 /* Set X-MICROSOFT-CDO-REPLYTIME to record the time at which
4633 * the user accepted/declined the request. (Outlook ignores
4634 * SEQUENCE in REPLY reponses and instead requires that each
4635 * updated response have a later REPLYTIME than the previous
4636 * one.) This also ends up getting saved in our own copy of
4637 * the meeting, though there's currently no way to see that
4638 * information (unless it's being saved to an Exchange folder
4639 * and you then look at it in Outlook).
4640 */
4641 stamp = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
4642 str = icaltime_as_ical_string_r (stamp);
4643 prop = icalproperty_new_x (str);
4644 g_free (str);
4645 icalproperty_set_x_name (prop, "X-MICROSOFT-CDO-REPLYTIME");
4646 icalcomponent_add_property (pitip->ical_comp, prop);
4647
4648 clone = icalcomponent_new_clone (pitip->ical_comp);
4649 icalcomponent_add_component (pitip->top_level, clone);
4650 icalcomponent_set_method (pitip->top_level, pitip->method);
4651
4652 if (!itip_view_get_inherit_alarm_check_state (view)) {
4653 icalcomponent *alarm_comp;
4654 icalcompiter alarm_iter;
4655
4656 alarm_iter = icalcomponent_begin_component (clone, ICAL_VALARM_COMPONENT);
4657 while ((alarm_comp = icalcompiter_deref (&alarm_iter)) != NULL) {
4658 icalcompiter_next (&alarm_iter);
4659
4660 icalcomponent_remove_component (clone, alarm_comp);
4661 icalcomponent_free (alarm_comp);
4662 }
4663 }
4664
4665 clone_comp = e_cal_component_new ();
4666 if (!e_cal_component_set_icalcomponent (clone_comp, clone)) {
4667 update_item_progress_info (pitip, view, NULL);
4668 pitip->update_item_error_info_id =
4669 itip_view_add_lower_info_item (
4670 view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
4671 _("Unable to parse item"));
4672 goto cleanup;
4673 }
4674
4675 if (itip_view_get_keep_alarm_check_state (view)) {
4676 ECalComponent *real_comp;
4677 GList *alarms, *l;
4678 ECalComponentAlarm *alarm;
4679
4680 real_comp = get_real_item (pitip);
4681 if (real_comp != NULL) {
4682 alarms = e_cal_component_get_alarm_uids (real_comp);
4683
4684 for (l = alarms; l; l = l->next) {
4685 alarm = e_cal_component_get_alarm (
4686 real_comp, (const gchar *) l->data);
4687
4688 if (alarm) {
4689 ECalComponentAlarm *aclone = e_cal_component_alarm_clone (alarm);
4690
4691 if (aclone) {
4692 e_cal_component_add_alarm (clone_comp, aclone);
4693 e_cal_component_alarm_free (aclone);
4694 }
4695
4696 e_cal_component_alarm_free (alarm);
4697 }
4698 }
4699
4700 cal_obj_uid_list_free (alarms);
4701 g_object_unref (real_comp);
4702 }
4703 }
4704
4705 if ((response != ITIP_VIEW_RESPONSE_CANCEL)
4706 && (response != ITIP_VIEW_RESPONSE_DECLINE)) {
4707 GSList *attachments = NULL, *new_attachments = NULL, *l;
4708 CamelMimeMessage *msg = pitip->msg;
4709
4710 e_cal_component_get_attachment_list (clone_comp, &attachments);
4711
4712 for (l = attachments; l; l = l->next) {
4713 GSList *parts = NULL, *m;
4714 gchar *uri, *new_uri;
4715 CamelMimePart *part;
4716
4717 uri = l->data;
4718
4719 if (!g_ascii_strncasecmp (uri, "cid:...", 7)) {
4720 message_foreach_part ((CamelMimePart *) msg, &parts);
4721
4722 for (m = parts; m; m = m->next) {
4723 part = m->data;
4724
4725 /* Skip the actual message and the text/calendar part */
4726 /* FIXME Do we need to skip anything else? */
4727 if (part == (CamelMimePart *) msg || part == pitip->part)
4728 continue;
4729
4730 new_uri = get_uri_for_part (part);
4731 if (new_uri != NULL)
4732 new_attachments = g_slist_append (new_attachments, new_uri);
4733 }
4734
4735 g_slist_free (parts);
4736
4737 } else if (!g_ascii_strncasecmp (uri, "cid:", 4)) {
4738 part = camel_mime_message_get_part_by_content_id (msg, uri + 4);
4739 if (part) {
4740 new_uri = get_uri_for_part (part);
4741 if (new_uri != NULL)
4742 new_attachments = g_slist_append (new_attachments, new_uri);
4743 }
4744
4745 } else {
4746 /* Preserve existing non-cid ones */
4747 new_attachments = g_slist_append (new_attachments, g_strdup (uri));
4748 }
4749 }
4750
4751 g_slist_foreach (attachments, (GFunc) g_free, NULL);
4752 g_slist_free (attachments);
4753
4754 e_cal_component_set_attachment_list (clone_comp, new_attachments);
4755 }
4756
4757 pitip->update_item_response = response;
4758
4759 e_cal_client_receive_objects (
4760 pitip->current_client,
4761 pitip->top_level,
4762 pitip->cancellable,
4763 receive_objects_ready_cb,
4764 view);
4765
4766 cleanup:
4767 icalcomponent_remove_component (pitip->top_level, clone);
4768 g_object_unref (clone_comp);
4769 }
4770
4771 /* TODO These operations should be available in e-cal-component.c */
4772 static void
4773 set_attendee (ECalComponent *comp,
4774 const gchar *address)
4775 {
4776 icalproperty *prop;
4777 icalcomponent *icalcomp;
4778 gboolean found = FALSE;
4779
4780 icalcomp = e_cal_component_get_icalcomponent (comp);
4781
4782 for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY);
4783 prop;
4784 prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) {
4785 const gchar *attendee = icalproperty_get_attendee (prop);
4786
4787 if (!(g_str_equal (itip_strip_mailto (attendee), address)))
4788 icalcomponent_remove_property (icalcomp, prop);
4789 else
4790 found = TRUE;
4791 }
4792
4793 if (!found) {
4794 icalparameter *param;
4795 gchar *temp = g_strdup_printf ("MAILTO:%s", address);
4796
4797 prop = icalproperty_new_attendee ((const gchar *) temp);
4798 icalcomponent_add_property (icalcomp, prop);
4799
4800 param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION);
4801 icalproperty_add_parameter (prop, param);
4802
4803 param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT);
4804 icalproperty_add_parameter (prop, param);
4805
4806 param = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL);
4807 icalproperty_add_parameter (prop, param);
4808
4809 param = icalparameter_new_rsvp (ICAL_RSVP_TRUE);
4810 icalproperty_add_parameter (prop, param);
4811
4812 g_free (temp);
4813 }
4814
4815 }
4816
4817 static gboolean
4818 send_comp_to_attendee (ESourceRegistry *registry,
4819 ECalComponentItipMethod method,
4820 ECalComponent *comp,
4821 const gchar *user,
4822 ECalClient *client,
4823 const gchar *comment)
4824 {
4825 gboolean status;
4826 ECalComponent *send_comp = e_cal_component_clone (comp);
4827
4828 set_attendee (send_comp, user);
4829
4830 if (comment) {
4831 GSList comments;
4832 ECalComponentText text;
4833
4834 text.value = comment;
4835 text.altrep = NULL;
4836
4837 comments.data = &text;
4838 comments.next = NULL;
4839
4840 e_cal_component_set_comment_list (send_comp, &comments);
4841 }
4842
4843 /* FIXME send the attachments in the request */
4844 status = itip_send_comp (
4845 registry, method, send_comp,
4846 client, NULL, NULL, NULL, TRUE, FALSE);
4847
4848 g_object_unref (send_comp);
4849
4850 return status;
4851 }
4852
4853 static void
4854 remove_delegate (EMailPartItip *pitip,
4855 ItipView *view,
4856 const gchar *delegate,
4857 const gchar *delegator,
4858 ECalComponent *comp)
4859 {
4860 gboolean status;
4861 gchar *comment = g_strdup_printf (_("Organizer has removed the delegate %s "), itip_strip_mailto (delegate));
4862
4863 /* send cancellation notice to delegate */
4864 status = send_comp_to_attendee (
4865 pitip->registry,
4866 E_CAL_COMPONENT_METHOD_CANCEL, pitip->comp,
4867 delegate, pitip->current_client, comment);
4868 if (status)
4869 send_comp_to_attendee (
4870 pitip->registry,
4871 E_CAL_COMPONENT_METHOD_REQUEST, pitip->comp,
4872 delegator, pitip->current_client, comment);
4873 if (status) {
4874 itip_view_add_lower_info_item (
4875 view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4876 _("Sent a cancelation notice to the delegate"));
4877 } else
4878 itip_view_add_lower_info_item (
4879 view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4880 _("Could not send the cancelation notice to the delegate"));
4881
4882 g_free (comment);
4883
4884 }
4885
4886 static void
4887 update_x (ECalComponent *pitip_comp,
4888 ECalComponent *comp)
4889 {
4890 icalcomponent *itip_icalcomp = e_cal_component_get_icalcomponent (pitip_comp);
4891 icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
4892
4893 icalproperty *prop = icalcomponent_get_first_property (itip_icalcomp, ICAL_X_PROPERTY);
4894 while (prop) {
4895 const gchar *name = icalproperty_get_x_name (prop);
4896 if (!g_ascii_strcasecmp (name, "X-EVOLUTION-IS-REPLY")) {
4897 icalproperty *new_prop = icalproperty_new_x (icalproperty_get_x (prop));
4898 icalproperty_set_x_name (new_prop, "X-EVOLUTION-IS-REPLY");
4899 icalcomponent_add_property (icalcomp, new_prop);
4900 }
4901 prop = icalcomponent_get_next_property (itip_icalcomp, ICAL_X_PROPERTY);
4902 }
4903
4904 e_cal_component_set_icalcomponent (comp, icalcomp);
4905 }
4906
4907 static void
4908 modify_object_cb (GObject *ecalclient,
4909 GAsyncResult *result,
4910 gpointer user_data)
4911 {
4912 ECalClient *client = E_CAL_CLIENT (ecalclient);
4913 ItipView *view = user_data;
4914 EMailPartItip *pitip = itip_view_get_mail_part (view);
4915 GError *error = NULL;
4916
4917 e_cal_client_modify_object_finish (client, result, &error);
4918
4919 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
4920 g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
4921 g_error_free (error);
4922 return;
4923 }
4924
4925 if (error != NULL) {
4926 update_item_progress_info (pitip, view, NULL);
4927 pitip->update_item_error_info_id =
4928 itip_view_add_lower_info_item_printf (
4929 view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
4930 _("Unable to update attendee. %s"),
4931 error->message);
4932 g_error_free (error);
4933 } else {
4934 update_item_progress_info (pitip, view, NULL);
4935 itip_view_add_lower_info_item (
4936 view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
4937 _("Attendee status updated"));
4938 }
4939 }
4940
4941 static void
4942 update_attendee_status_icalcomp (EMailPartItip *pitip,
4943 ItipView *view,
4944 icalcomponent *icalcomp)
4945 {
4946 ECalComponent *comp;
4947 const gchar *uid = NULL;
4948 gchar *rid;
4949 GSList *attendees;
4950
4951 e_cal_component_get_uid (pitip->comp, &uid);
4952 rid = e_cal_component_get_recurid_as_string (pitip->comp);
4953
4954 comp = e_cal_component_new ();
4955 if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
4956 icalcomponent_free (icalcomp);
4957
4958 itip_view_add_lower_info_item (
4959 view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
4960 _("The meeting is invalid and cannot be updated"));
4961 } else {
4962 icalcomponent *org_icalcomp;
4963 const gchar *delegate;
4964
4965 org_icalcomp = e_cal_component_get_icalcomponent (pitip->comp);
4966
4967 e_cal_component_get_attendee_list (pitip->comp, &attendees);
4968 if (attendees != NULL) {
4969 ECalComponentAttendee *a = attendees->data;
4970 icalproperty *prop, *del_prop;
4971 EShell *shell = e_shell_get_default ();
4972
4973 prop = find_attendee (icalcomp, itip_strip_mailto (a->value));
4974 if ((a->status == ICAL_PARTSTAT_DELEGATED) && (del_prop = find_attendee (org_icalcomp, itip_strip_mailto (a->delto))) && !(find_attendee (icalcomp, itip_strip_mailto (a->delto)))) {
4975 gint response;
4976 delegate = icalproperty_get_attendee (del_prop);
4977 response = e_alert_run_dialog_for_args (
4978 e_shell_get_active_window (shell),
4979 "org.gnome.itip-formatter:add-delegate",
4980 itip_strip_mailto (a->value),
4981 itip_strip_mailto (delegate), NULL);
4982 if (response == GTK_RESPONSE_YES) {
4983 icalcomponent_add_property (icalcomp, icalproperty_new_clone (del_prop));
4984 e_cal_component_rescan (comp);
4985 } else if (response == GTK_RESPONSE_NO) {
4986 remove_delegate (pitip, view, delegate, itip_strip_mailto (a->value), comp);
4987 goto cleanup;
4988 } else {
4989 goto cleanup;
4990 }
4991 }
4992
4993 if (prop == NULL) {
4994 gint response;
4995
4996 if (a->delfrom && *a->delfrom) {
4997 response = e_alert_run_dialog_for_args (
4998 e_shell_get_active_window (shell),
4999 "org.gnome.itip-formatter:add-delegate",
5000 itip_strip_mailto (a->delfrom),
5001 itip_strip_mailto (a->value), NULL);
5002 if (response == GTK_RESPONSE_YES) {
5003 /* Already declared in this function */
5004 icalproperty *prop = find_attendee (icalcomp, itip_strip_mailto (a->value));
5005 icalcomponent_add_property (icalcomp,icalproperty_new_clone (prop));
5006 e_cal_component_rescan (comp);
5007 } else if (response == GTK_RESPONSE_NO) {
5008 remove_delegate (
5009 pitip,
5010 view,
5011 itip_strip_mailto (a->value),
5012 itip_strip_mailto (a->delfrom),
5013 comp);
5014 goto cleanup;
5015 } else {
5016 goto cleanup;
5017 }
5018 }
5019
5020 response = e_alert_run_dialog_for_args (
5021 e_shell_get_active_window (shell),
5022 "org.gnome.itip-formatter:add-unknown-attendee", NULL);
5023
5024 if (response == GTK_RESPONSE_YES) {
5025 change_status (
5026 pitip->registry, icalcomp,
5027 itip_strip_mailto (a->value),
5028 a->status);
5029 e_cal_component_rescan (comp);
5030 } else {
5031 goto cleanup;
5032 }
5033 } else if (a->status == ICAL_PARTSTAT_NONE || a->status == ICAL_PARTSTAT_X) {
5034 itip_view_add_lower_info_item (
5035 view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
5036 _("Attendee status could not be updated because the status is invalid"));
5037 goto cleanup;
5038 } else {
5039 if (a->status == ICAL_PARTSTAT_DELEGATED) {
5040 /* *prop already declared in this function */
5041 icalproperty *prop, *new_prop;
5042
5043 prop = find_attendee (icalcomp, itip_strip_mailto (a->value));
5044 icalcomponent_remove_property (icalcomp, prop);
5045
5046 new_prop = find_attendee (org_icalcomp, itip_strip_mailto (a->value));
5047 icalcomponent_add_property (icalcomp, icalproperty_new_clone (new_prop));
5048 } else {
5049 change_status (
5050 pitip->registry, icalcomp,
5051 itip_strip_mailto (a->value),
5052 a->status);
5053 }
5054
5055 e_cal_component_rescan (comp);
5056 }
5057 }
5058 }
5059
5060 update_x (pitip->comp, comp);
5061
5062 if (itip_view_get_update (view)) {
5063 e_cal_component_commit_sequence (comp);
5064 itip_send_comp (
5065 pitip->registry,
5066 E_CAL_COMPONENT_METHOD_REQUEST,
5067 comp, pitip->current_client,
5068 NULL, NULL, NULL, TRUE, FALSE);
5069 }
5070
5071 update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));
5072
5073 e_cal_client_modify_object (
5074 pitip->current_client,
5075 icalcomp, rid ? CALOBJ_MOD_THIS : CALOBJ_MOD_ALL,
5076 pitip->cancellable,
5077 modify_object_cb,
5078 view);
5079
5080 cleanup:
5081 g_object_unref (comp);
5082 }
5083
5084 static void
5085 update_attendee_status_get_object_without_rid_cb (GObject *ecalclient,
5086 GAsyncResult *result,
5087 gpointer user_data)
5088 {
5089 ECalClient *client = E_CAL_CLIENT (ecalclient);
5090 ItipView *view = user_data;
5091 EMailPartItip *pitip = itip_view_get_mail_part (view);
5092 icalcomponent *icalcomp = NULL;
5093 GError *error = NULL;
5094
5095 if (!e_cal_client_get_object_finish (client, result, &icalcomp, &error)) {
5096 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
5097 g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
5098 g_clear_error (&error);
5099 return;
5100 }
5101
5102 g_clear_error (&error);
5103
5104 update_item_progress_info (pitip, view, NULL);
5105 pitip->update_item_error_info_id = itip_view_add_lower_info_item (
5106 view,
5107 ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
5108 _("Attendee status can not be updated because the item no longer exists"));
5109 return;
5110 }
5111
5112 update_attendee_status_icalcomp (pitip, view, icalcomp);
5113 }
5114
5115 static void
5116 update_attendee_status_get_object_with_rid_cb (GObject *ecalclient,
5117 GAsyncResult *result,
5118 gpointer user_data)
5119 {
5120 ECalClient *client = E_CAL_CLIENT (ecalclient);
5121 ItipView *view = user_data;
5122 EMailPartItip *pitip = itip_view_get_mail_part (view);
5123 icalcomponent *icalcomp = NULL;
5124 GError *error = NULL;
5125
5126 if (!e_cal_client_get_object_finish (client, result, &icalcomp, &error)) {
5127 const gchar *uid;
5128 gchar *rid;
5129
5130 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
5131 g_error_matches (error, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED)) {
5132 g_clear_error (&error);
5133 return;
5134 }
5135
5136 g_clear_error (&error);
5137
5138 e_cal_component_get_uid (pitip->comp, &uid);
5139 rid = e_cal_component_get_recurid_as_string (pitip->comp);
5140
5141 if (!rid || !*rid) {
5142 g_free (rid);
5143
5144 update_item_progress_info (pitip, view, NULL);
5145 pitip->update_item_error_info_id = itip_view_add_lower_info_item (
5146 view,
5147 ITIP_VIEW_INFO_ITEM_TYPE_WARNING,
5148 _("Attendee status can not be updated because the item no longer exists"));
5149 return;
5150 }
5151
5152 e_cal_client_get_object (
5153 pitip->current_client,
5154 uid,
5155 NULL,
5156 pitip->cancellable,
5157 update_attendee_status_get_object_without_rid_cb,
5158 view);
5159
5160 g_free (rid);
5161 return;
5162 }
5163
5164 update_attendee_status_icalcomp (pitip, view, icalcomp);
5165 }
5166
5167 static void
5168 update_attendee_status (EMailPartItip *pitip,
5169 ItipView *view)
5170 {
5171 const gchar *uid = NULL;
5172 gchar *rid;
5173
5174 /* Obtain our version */
5175 e_cal_component_get_uid (pitip->comp, &uid);
5176 rid = e_cal_component_get_recurid_as_string (pitip->comp);
5177
5178 update_item_progress_info (pitip, view, _("Saving changes to the calendar. Please wait..."));
5179
5180 /* search for a master object if the detached object doesn't exist in the calendar */
5181 e_cal_client_get_object (
5182 pitip->current_client,
5183 uid, rid,
5184 pitip->cancellable,
5185 update_attendee_status_get_object_with_rid_cb,
5186 view);
5187
5188 g_free (rid);
5189 }
5190
5191 static void
5192 send_item (EMailPartItip *pitip,
5193 ItipView *view)
5194 {
5195 ECalComponent *comp;
5196
5197 comp = get_real_item (pitip);
5198
5199 if (comp != NULL) {
5200 itip_send_comp (
5201 pitip->registry,
5202 E_CAL_COMPONENT_METHOD_REQUEST,
5203 comp, pitip->current_client,
5204 NULL, NULL, NULL, TRUE, FALSE);
5205 g_object_unref (comp);
5206
5207 switch (pitip->type) {
5208 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
5209 itip_view_add_lower_info_item (
5210 view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
5211 _("Meeting information sent"));
5212 break;
5213 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
5214 itip_view_add_lower_info_item (
5215 view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
5216 _("Task information sent"));
5217 break;
5218 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
5219 itip_view_add_lower_info_item (
5220 view, ITIP_VIEW_INFO_ITEM_TYPE_INFO,
5221 _("Memo information sent"));
5222 break;
5223 default:
5224 g_assert_not_reached ();
5225 break;
5226 }
5227 } else {
5228 switch (pitip->type) {
5229 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
5230 itip_view_add_lower_info_item (
5231 view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
5232 _("Unable to send meeting information, the meeting does not exist"));
5233 break;
5234 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
5235 itip_view_add_lower_info_item (
5236 view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
5237 _("Unable to send task information, the task does not exist"));
5238 break;
5239 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
5240 itip_view_add_lower_info_item (
5241 view, ITIP_VIEW_INFO_ITEM_TYPE_ERROR,
5242 _("Unable to send memo information, the memo does not exist"));
5243 break;
5244 default:
5245 g_assert_not_reached ();
5246 break;
5247 }
5248 }
5249 }
5250
5251 static icalcomponent *
5252 get_next (icalcompiter *iter)
5253 {
5254 icalcomponent *ret = NULL;
5255 icalcomponent_kind kind;
5256
5257 do {
5258 icalcompiter_next (iter);
5259 ret = icalcompiter_deref (iter);
5260 if (ret == NULL)
5261 break;
5262 kind = icalcomponent_isa (ret);
5263 } while (ret != NULL
5264 && kind != ICAL_VEVENT_COMPONENT
5265 && kind != ICAL_VTODO_COMPONENT
5266 && kind != ICAL_VFREEBUSY_COMPONENT);
5267
5268 return ret;
5269 }
5270
5271 static void
5272 attachment_load_finish (EAttachment *attachment,
5273 GAsyncResult *result,
5274 GFile *file)
5275 {
5276 EShell *shell;
5277 GtkWindow *parent;
5278
5279 /* XXX Theoretically, this should never fail. */
5280 e_attachment_load_finish (attachment, result, NULL);
5281
5282 shell = e_shell_get_default ();
5283 parent = e_shell_get_active_window (shell);
5284
5285 e_attachment_save_async (
5286 attachment, file, (GAsyncReadyCallback)
5287 e_attachment_save_handle_error, parent);
5288
5289 g_object_unref (file);
5290 }
5291
5292 static void
5293 save_vcalendar_cb (EMailPartItip *pitip)
5294 {
5295 EAttachment *attachment;
5296 EShell *shell;
5297 GFile *file;
5298 const gchar *suggestion;
5299
5300 g_return_if_fail (pitip != NULL);
5301 g_return_if_fail (pitip->vcalendar != NULL);
5302 g_return_if_fail (pitip->part != NULL);
5303
5304 suggestion = camel_mime_part_get_filename (pitip->part);
5305 if (suggestion == NULL) {
5306 /* Translators: This is a default filename for a calendar. */
5307 suggestion = _("calendar.ics");
5308 }
5309
5310 shell = e_shell_get_default ();
5311 file = e_shell_run_save_dialog (
5312 shell, _("Save Calendar"), suggestion, "*.ics:text/calendar", NULL, NULL);
5313 if (file == NULL)
5314 return;
5315
5316 attachment = e_attachment_new ();
5317 e_attachment_set_mime_part (attachment, pitip->part);
5318
5319 e_attachment_load_async (
5320 attachment, (GAsyncReadyCallback)
5321 attachment_load_finish, file);
5322 }
5323
5324 static void
5325 set_itip_error (ItipView *view,
5326 const gchar *primary,
5327 const gchar *secondary,
5328 gboolean save_btn)
5329 {
5330 gchar *error;
5331
5332 error = g_strdup_printf (
5333 "<div class=\"error\">"
5334 "<p><b>%s</b></p>"
5335 "<p>%s</p>",
5336 primary, secondary);
5337
5338 itip_view_set_error (view, error, save_btn);
5339
5340 g_free (error);
5341 }
5342
5343 static gboolean
5344 extract_itip_data (EMailPartItip *pitip,
5345 ItipView *view,
5346 gboolean *have_alarms)
5347 {
5348 EShell *shell;
5349 EShellSettings *shell_settings;
5350 icalproperty *prop;
5351 icalcomponent_kind kind = ICAL_NO_COMPONENT;
5352 icalcomponent *tz_comp;
5353 icalcompiter tz_iter;
5354 icalcomponent *alarm_comp;
5355 icalcompiter alarm_iter;
5356 ECalComponent *comp;
5357 gboolean use_default_reminder;
5358
5359 shell = e_shell_get_default ();
5360 shell_settings = e_shell_get_shell_settings (shell);
5361
5362 if (!pitip->vcalendar) {
5363 set_itip_error (
5364 view,
5365 _("The calendar attached is not valid"),
5366 _("The message claims to contain a calendar, but the calendar is not a valid iCalendar."),
5367 FALSE);
5368
5369 return FALSE;
5370 }
5371
5372 pitip->top_level = e_cal_util_new_top_level ();
5373
5374 pitip->main_comp = icalparser_parse_string (pitip->vcalendar);
5375 if (pitip->main_comp == NULL || !is_icalcomp_valid (pitip->main_comp)) {
5376 set_itip_error (
5377 view,
5378 _("The calendar attached is not valid"),
5379 _("The message claims to contain a calendar, but the calendar is not a valid iCalendar."),
5380 FALSE);
5381
5382 if (pitip->main_comp) {
5383 icalcomponent_free (pitip->main_comp);
5384 pitip->main_comp = NULL;
5385 }
5386
5387 return FALSE;
5388 }
5389
5390 prop = icalcomponent_get_first_property (pitip->main_comp, ICAL_METHOD_PROPERTY);
5391 if (prop == NULL) {
5392 pitip->method = ICAL_METHOD_PUBLISH;
5393 } else {
5394 pitip->method = icalproperty_get_method (prop);
5395 }
5396
5397 tz_iter = icalcomponent_begin_component (pitip->main_comp, ICAL_VTIMEZONE_COMPONENT);
5398 while ((tz_comp = icalcompiter_deref (&tz_iter)) != NULL) {
5399 icalcomponent *clone;
5400
5401 clone = icalcomponent_new_clone (tz_comp);
5402 icalcomponent_add_component (pitip->top_level, clone);
5403
5404 icalcompiter_next (&tz_iter);
5405 }
5406
5407 pitip->iter = icalcomponent_begin_component (pitip->main_comp, ICAL_ANY_COMPONENT);
5408 pitip->ical_comp = icalcompiter_deref (&pitip->iter);
5409 if (pitip->ical_comp != NULL) {
5410 kind = icalcomponent_isa (pitip->ical_comp);
5411 if (kind != ICAL_VEVENT_COMPONENT
5412 && kind != ICAL_VTODO_COMPONENT
5413 && kind != ICAL_VFREEBUSY_COMPONENT
5414 && kind != ICAL_VJOURNAL_COMPONENT)
5415 pitip->ical_comp = get_next (&pitip->iter);
5416 }
5417
5418 if (pitip->ical_comp == NULL) {
5419 set_itip_error (
5420 view,
5421 _("The item in the calendar is not valid"),
5422 _("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"),
5423 FALSE);
5424
5425 return FALSE;
5426 }
5427
5428 switch (icalcomponent_isa (pitip->ical_comp)) {
5429 case ICAL_VEVENT_COMPONENT:
5430 pitip->type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
5431 pitip->has_organizer = icalcomponent_get_first_property (pitip->ical_comp, ICAL_ORGANIZER_PROPERTY) != NULL;
5432 if (icalcomponent_get_first_property (pitip->ical_comp, ICAL_ATTENDEE_PROPERTY) == NULL) {
5433 /* no attendees: assume that that this is not a meeting and organizer doesn't want a reply */
5434 pitip->no_reply_wanted = TRUE;
5435 } else {
5436 /*
5437 * if we have attendees, then find_to_address() will check for our RSVP
5438 * and set no_reply_wanted=TRUE if RSVP=FALSE for the current user
5439 */
5440 }
5441 break;
5442 case ICAL_VTODO_COMPONENT:
5443 pitip->type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
5444 break;
5445 case ICAL_VJOURNAL_COMPONENT:
5446 pitip->type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS;
5447 break;
5448 default:
5449 set_itip_error (
5450 view,
5451 _("The item in the calendar is not valid"),
5452 _("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"),
5453 FALSE);
5454
5455 return FALSE;
5456 }
5457
5458 pitip->total = icalcomponent_count_components (pitip->main_comp, ICAL_VEVENT_COMPONENT);
5459 pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VTODO_COMPONENT);
5460 pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VFREEBUSY_COMPONENT);
5461 pitip->total += icalcomponent_count_components (pitip->main_comp, ICAL_VJOURNAL_COMPONENT);
5462
5463 if (pitip->total > 1) {
5464
5465 set_itip_error (
5466 view,
5467 _("The calendar attached contains multiple items"),
5468 _("To process all of these items, the file should be saved and the calendar imported"),
5469 TRUE);
5470
5471 } if (pitip->total > 0) {
5472 pitip->current = 1;
5473 } else {
5474 pitip->current = 0;
5475 }
5476
5477 if (icalcomponent_isa (pitip->ical_comp) != ICAL_VJOURNAL_COMPONENT) {
5478 gchar *my_address;
5479
5480 prop = NULL;
5481 comp = e_cal_component_new ();
5482 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (pitip->ical_comp));
5483 my_address = itip_get_comp_attendee (pitip->registry, comp, NULL);
5484 g_object_unref (comp);
5485 comp = NULL;
5486
5487 if (!prop)
5488 prop = find_attendee (pitip->ical_comp, my_address);
5489 if (!prop)
5490 prop = find_attendee_if_sentby (pitip->ical_comp, my_address);
5491 if (prop) {
5492 icalparameter *param;
5493 const gchar * delfrom;
5494
5495 if ((param = icalproperty_get_first_parameter (prop, ICAL_DELEGATEDFROM_PARAMETER))) {
5496 delfrom = icalparameter_get_delegatedfrom (param);
5497
5498 pitip->delegator_address = g_strdup (itip_strip_mailto (delfrom));
5499 }
5500 }
5501 g_free (my_address);
5502 prop = NULL;
5503
5504 /* Determine any delegate sections */
5505 prop = icalcomponent_get_first_property (pitip->ical_comp, ICAL_X_PROPERTY);
5506 while (prop) {
5507 const gchar *x_name, *x_val;
5508
5509 x_name = icalproperty_get_x_name (prop);
5510 x_val = icalproperty_get_x (prop);
5511
5512 if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-UID"))
5513 pitip->calendar_uid = g_strdup (x_val);
5514 else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-CALENDAR-URI"))
5515 g_warning (G_STRLOC ": X-EVOLUTION-DELEGATOR-CALENDAR-URI used");
5516 else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-ADDRESS"))
5517 pitip->delegator_address = g_strdup (x_val);
5518 else if (!strcmp (x_name, "X-EVOLUTION-DELEGATOR-NAME"))
5519 pitip->delegator_name = g_strdup (x_val);
5520
5521 prop = icalcomponent_get_next_property (pitip->ical_comp, ICAL_X_PROPERTY);
5522 }
5523
5524 /* Strip out procedural alarms for security purposes */
5525 alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT);
5526 while ((alarm_comp = icalcompiter_deref (&alarm_iter)) != NULL) {
5527 icalproperty *p;
5528
5529 icalcompiter_next (&alarm_iter);
5530
5531 p = icalcomponent_get_first_property (alarm_comp, ICAL_ACTION_PROPERTY);
5532 if (!p || icalproperty_get_action (p) == ICAL_ACTION_PROCEDURE)
5533 icalcomponent_remove_component (pitip->ical_comp, alarm_comp);
5534
5535 icalcomponent_free (alarm_comp);
5536 }
5537
5538 if (have_alarms) {
5539 alarm_iter = icalcomponent_begin_component (pitip->ical_comp, ICAL_VALARM_COMPONENT);
5540 *have_alarms = icalcompiter_deref (&alarm_iter) != NULL;
5541 }
5542 }
5543
5544 pitip->comp = e_cal_component_new ();
5545 if (!e_cal_component_set_icalcomponent (pitip->comp, pitip->ical_comp)) {
5546 g_object_unref (pitip->comp);
5547 pitip->comp = NULL;
5548
5549 set_itip_error (
5550 view,
5551 _("The item in the calendar is not valid"),
5552 _("The message does contain a calendar, but the calendar contains no events, tasks or free/busy information"),
5553 FALSE);
5554
5555 return FALSE;
5556 };
5557
5558 /* Add default reminder if the config says so */
5559
5560 use_default_reminder = e_shell_settings_get_boolean (
5561 shell_settings, "cal-use-default-reminder");
5562
5563 if (use_default_reminder) {
5564 ECalComponentAlarm *acomp;
5565 gint interval;
5566 EDurationType units;
5567 ECalComponentAlarmTrigger trigger;
5568
5569 interval = e_shell_settings_get_int (
5570 shell_settings, "cal-default-reminder-interval");
5571 units = e_shell_settings_get_int (
5572 shell_settings, "cal-default-reminder-units");
5573
5574 acomp = e_cal_component_alarm_new ();
5575
5576 e_cal_component_alarm_set_action (acomp, E_CAL_COMPONENT_ALARM_DISPLAY);
5577
5578 trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
5579 memset (&trigger.u.rel_duration, 0, sizeof (trigger.u.rel_duration));
5580
5581 trigger.u.rel_duration.is_neg = TRUE;
5582
5583 switch (units) {
5584 case E_DURATION_MINUTES:
5585 trigger.u.rel_duration.minutes = interval;
5586 break;
5587 case E_DURATION_HOURS:
5588 trigger.u.rel_duration.hours = interval;
5589 break;
5590 case E_DURATION_DAYS:
5591 trigger.u.rel_duration.days = interval;
5592 break;
5593 default:
5594 g_assert_not_reached ();
5595 }
5596
5597 e_cal_component_alarm_set_trigger (acomp, trigger);
5598 e_cal_component_add_alarm (pitip->comp, acomp);
5599
5600 e_cal_component_alarm_free (acomp);
5601 }
5602
5603 find_from_address (pitip, pitip->ical_comp);
5604 find_to_address (pitip, pitip->ical_comp, NULL);
5605
5606 return TRUE;
5607 }
5608
5609 static gboolean
5610 idle_open_cb (gpointer data)
5611 {
5612 EMailPartItip *pitip = data;
5613 EShell *shell;
5614 const gchar *uris[2];
5615 gchar *start, *end, *shell_uri;
5616
5617 start = isodate_from_time_t (pitip->start_time ? pitip->start_time : time (NULL));
5618 end = isodate_from_time_t (pitip->end_time ? pitip->end_time : time (NULL));
5619 shell_uri = g_strdup_printf ("calendar:///?startdate=%s&enddate=%s", start, end);
5620
5621 uris[0] = shell_uri;
5622 uris[1] = NULL;
5623
5624 shell = e_shell_get_default ();
5625 e_shell_handle_uris (shell, uris, FALSE);
5626
5627 g_free (shell_uri);
5628 g_free (start);
5629 g_free (end);
5630
5631 return FALSE;
5632 }
5633
5634 static void
5635 view_response_cb (ItipView *view,
5636 ItipViewResponse response,
5637 gpointer data)
5638 {
5639 EMailPartItip *pitip = data;
5640 gboolean status = FALSE;
5641 icalproperty *prop;
5642 ECalComponentTransparency trans;
5643
5644 if (response == ITIP_VIEW_RESPONSE_SAVE) {
5645 save_vcalendar_cb (pitip);
5646 return;
5647 }
5648
5649 if (pitip->method == ICAL_METHOD_PUBLISH || pitip->method == ICAL_METHOD_REQUEST) {
5650 if (itip_view_get_free_time_check_state (view))
5651 e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_TRANSPARENT);
5652 else
5653 e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
5654 } else {
5655 e_cal_component_get_transparency (pitip->comp, &trans);
5656
5657 if (trans == E_CAL_COMPONENT_TRANSP_NONE)
5658 e_cal_component_set_transparency (pitip->comp, E_CAL_COMPONENT_TRANSP_OPAQUE);
5659 }
5660
5661 if (!pitip->to_address && pitip->current_client != NULL)
5662 e_client_get_backend_property_sync (E_CLIENT (pitip->current_client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &pitip->to_address, NULL, NULL);
5663
5664 /* check if it is a recur instance (no master object) and
5665 * add a property */
5666 if (itip_view_get_recur_check_state (view)) {
5667 prop = icalproperty_new_x ("All");
5668 icalproperty_set_x_name (prop, "X-GW-RECUR-INSTANCES-MOD-TYPE");
5669 icalcomponent_add_property (pitip->ical_comp, prop);
5670 }
5671
5672 switch (response) {
5673 case ITIP_VIEW_RESPONSE_ACCEPT:
5674 if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
5675 status = change_status (
5676 pitip->registry,
5677 pitip->ical_comp,
5678 pitip->to_address,
5679 ICAL_PARTSTAT_ACCEPTED);
5680 else
5681 status = TRUE;
5682 if (status) {
5683 e_cal_component_rescan (pitip->comp);
5684 update_item (pitip, view, response);
5685 }
5686 break;
5687 case ITIP_VIEW_RESPONSE_TENTATIVE:
5688 status = change_status (
5689 pitip->registry,
5690 pitip->ical_comp,
5691 pitip->to_address,
5692 ICAL_PARTSTAT_TENTATIVE);
5693 if (status) {
5694 e_cal_component_rescan (pitip->comp);
5695 update_item (pitip, view, response);
5696 }
5697 break;
5698 case ITIP_VIEW_RESPONSE_DECLINE:
5699 if (pitip->type != E_CAL_CLIENT_SOURCE_TYPE_MEMOS)
5700 status = change_status (
5701 pitip->registry,
5702 pitip->ical_comp,
5703 pitip->to_address,
5704 ICAL_PARTSTAT_DECLINED);
5705 else {
5706 prop = icalproperty_new_x ("1");
5707 icalproperty_set_x_name (prop, "X-GW-DECLINED");
5708 icalcomponent_add_property (pitip->ical_comp, prop);
5709 status = TRUE;
5710 }
5711
5712 if (status) {
5713 e_cal_component_rescan (pitip->comp);
5714 update_item (pitip, view, response);
5715 }
5716 break;
5717 case ITIP_VIEW_RESPONSE_UPDATE:
5718 update_attendee_status (pitip, view);
5719 break;
5720 case ITIP_VIEW_RESPONSE_CANCEL:
5721 update_item (pitip, view, response);
5722 break;
5723 case ITIP_VIEW_RESPONSE_REFRESH:
5724 send_item (pitip, view);
5725 break;
5726 case ITIP_VIEW_RESPONSE_OPEN:
5727 /* Prioritize ahead of GTK+ redraws. */
5728 g_idle_add_full (
5729 G_PRIORITY_HIGH_IDLE,
5730 idle_open_cb, pitip, NULL);
5731 return;
5732 default:
5733 break;
5734 }
5735 }
5736
5737 static gboolean
5738 check_is_instance (icalcomponent *icalcomp)
5739 {
5740 icalproperty *icalprop;
5741
5742 icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
5743 while (icalprop) {
5744 const gchar *x_name;
5745
5746 x_name = icalproperty_get_x_name (icalprop);
5747 if (!strcmp (x_name, "X-GW-RECURRENCE-KEY")) {
5748 return TRUE;
5749 }
5750 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
5751 }
5752
5753 return FALSE;
5754 }
5755
5756 static gboolean
5757 in_proper_folder (ESourceRegistry *registry,
5758 CamelFolder *folder)
5759 {
5760 EShell *shell;
5761 EShellBackend *shell_backend;
5762 EMailBackend *backend;
5763 EMailSession *session;
5764 MailFolderCache *folder_cache;
5765 gboolean res = TRUE;
5766 CamelFolderInfoFlags flags = 0;
5767
5768 if (!folder)
5769 return FALSE;
5770
5771 shell = e_shell_get_default ();
5772 shell_backend = e_shell_get_backend_by_name (shell, "mail");
5773 backend = E_MAIL_BACKEND (shell_backend);
5774 session = e_mail_backend_get_session (backend);
5775 folder_cache = e_mail_session_get_folder_cache (session);
5776
5777 if (mail_folder_cache_get_folder_info_flags (folder_cache, folder, &flags)) {
5778 /* it should be neither trash nor junk folder, */
5779 res = ((flags & CAMEL_FOLDER_TYPE_MASK) != CAMEL_FOLDER_TYPE_TRASH &&
5780 (flags & CAMEL_FOLDER_TYPE_MASK) != CAMEL_FOLDER_TYPE_JUNK &&
5781 /* it can be Inbox */
5782 ((flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX ||
5783 /* or any other virtual folder */
5784 CAMEL_IS_VEE_FOLDER (folder) ||
5785 /* or anything else except of sent, outbox or drafts folder */
5786 (!em_utils_folder_is_sent (registry, folder) &&
5787 !em_utils_folder_is_outbox (registry, folder) &&
5788 !em_utils_folder_is_drafts (registry, folder))
5789 ));
5790 } else {
5791 /* cannot check for Inbox folder here */
5792 res = (folder->folder_flags & (CAMEL_FOLDER_IS_TRASH | CAMEL_FOLDER_IS_JUNK)) == 0 && (
5793 (CAMEL_IS_VEE_FOLDER (folder)) || (
5794 !em_utils_folder_is_sent (registry, folder) &&
5795 !em_utils_folder_is_outbox (registry, folder) &&
5796 !em_utils_folder_is_drafts (registry, folder)));
5797 }
5798
5799 return res;
5800 }
5801
5802 void
5803 itip_view_init_view (ItipView *view)
5804 {
5805 EShell *shell;
5806 EShellSettings *shell_settings;
5807 ESourceRegistry *registry;
5808 ECalComponentText text;
5809 ECalComponentOrganizer organizer;
5810 ECalComponentDateTime datetime;
5811 icaltimezone *from_zone, *to_zone;
5812 GString *gstring = NULL;
5813 GSList *list, *l;
5814 icalcomponent *icalcomp;
5815 const gchar *string, *org;
5816 gint i;
5817 gboolean response_enabled;
5818 gboolean have_alarms = FALSE;
5819 EMailPartItip *info;
5820
5821 info = view->priv->itip_part;
5822 g_return_if_fail (info != NULL);
5823
5824 shell = e_shell_get_default ();
5825 registry = e_shell_get_registry (shell);
5826 shell_settings = e_shell_get_shell_settings (shell);
5827
5828 info->registry = g_object_ref (registry);
5829
5830 /* Reset current client before initializing view */
5831 info->current_client = NULL;
5832
5833 /* Initialize the ecal hashes */
5834 for (i = 0; i < E_CAL_CLIENT_SOURCE_TYPE_LAST; i++)
5835 info->clients[i] = g_hash_table_new_full (
5836 (GHashFunc) g_str_hash,
5837 (GEqualFunc) g_str_equal,
5838 (GDestroyNotify) g_free,
5839 (GDestroyNotify) g_object_unref);
5840
5841 /* FIXME Handle multiple VEVENTS with the same UID, ie detached instances */
5842 if (!extract_itip_data (info, view, &have_alarms))
5843 return;
5844
5845 response_enabled = in_proper_folder (registry, info->folder);
5846
5847 if (!response_enabled) {
5848 itip_view_set_mode (view, ITIP_VIEW_MODE_HIDE_ALL);
5849 } else {
5850 itip_view_set_show_inherit_alarm_check (
5851 view,
5852 have_alarms && (info->method == ICAL_METHOD_PUBLISH || info->method == ICAL_METHOD_REQUEST));
5853
5854 switch (info->method) {
5855 case ICAL_METHOD_PUBLISH:
5856 case ICAL_METHOD_REQUEST:
5857 /*
5858 * Treat meeting request (sent by organizer directly) and
5859 * published evend (forwarded by organizer or attendee) alike:
5860 * if the event has an organizer, then it can be replied to and
5861 * we show the "accept/tentative/decline" choice.
5862 * Otherwise only show "accept".
5863 */
5864 itip_view_set_mode (
5865 view,
5866 info->has_organizer ?
5867 ITIP_VIEW_MODE_REQUEST :
5868 ITIP_VIEW_MODE_PUBLISH);
5869 break;
5870 case ICAL_METHOD_REPLY:
5871 itip_view_set_mode (view, ITIP_VIEW_MODE_REPLY);
5872 break;
5873 case ICAL_METHOD_ADD:
5874 itip_view_set_mode (view, ITIP_VIEW_MODE_ADD);
5875 break;
5876 case ICAL_METHOD_CANCEL:
5877 itip_view_set_mode (view, ITIP_VIEW_MODE_CANCEL);
5878 break;
5879 case ICAL_METHOD_REFRESH:
5880 itip_view_set_mode (view, ITIP_VIEW_MODE_REFRESH);
5881 break;
5882 case ICAL_METHOD_COUNTER:
5883 itip_view_set_mode (view, ITIP_VIEW_MODE_COUNTER);
5884 break;
5885 case ICAL_METHOD_DECLINECOUNTER:
5886 itip_view_set_mode (view, ITIP_VIEW_MODE_DECLINECOUNTER);
5887 break;
5888 case ICAL_METHOD_X :
5889 /* Handle appointment requests from Microsoft Live. This is
5890 * a best-at-hand-now handling. Must be revisited when we have
5891 * better access to the source of such meetings */
5892 info->method = ICAL_METHOD_REQUEST;
5893 itip_view_set_mode (view, ITIP_VIEW_MODE_REQUEST);
5894 break;
5895 default:
5896 return;
5897 }
5898 }
5899
5900 itip_view_set_item_type (view, info->type);
5901
5902 if (response_enabled) {
5903 switch (info->method) {
5904 case ICAL_METHOD_REQUEST:
5905 /* FIXME What about the name? */
5906 itip_view_set_delegator (view, info->delegator_name ? info->delegator_name : info->delegator_address);
5907 case ICAL_METHOD_PUBLISH:
5908 case ICAL_METHOD_ADD:
5909 case ICAL_METHOD_CANCEL:
5910 case ICAL_METHOD_DECLINECOUNTER:
5911 itip_view_set_show_update_check (view, FALSE);
5912
5913 /* An organizer sent this */
5914 e_cal_component_get_organizer (info->comp, &organizer);
5915 org = organizer.cn ? organizer.cn : itip_strip_mailto (organizer.value);
5916
5917 itip_view_set_organizer (view, org);
5918 if (organizer.sentby)
5919 itip_view_set_organizer_sentby (
5920 view, itip_strip_mailto (organizer.sentby));
5921
5922 if (info->my_address) {
5923 if (!(organizer.value && !g_ascii_strcasecmp (itip_strip_mailto (organizer.value), info->my_address))
5924 && !(organizer.sentby && !g_ascii_strcasecmp (itip_strip_mailto (organizer.sentby), info->my_address))
5925 && (info->to_address && g_ascii_strcasecmp (info->to_address, info->my_address)))
5926 itip_view_set_proxy (view, info->to_name ? info->to_name : info->to_address);
5927 }
5928 break;
5929 case ICAL_METHOD_REPLY:
5930 case ICAL_METHOD_REFRESH:
5931 case ICAL_METHOD_COUNTER:
5932 itip_view_set_show_update_check (view, TRUE);
5933
5934 /* An attendee sent this */
5935 e_cal_component_get_attendee_list (info->comp, &list);
5936 if (list != NULL) {
5937 ECalComponentAttendee *attendee;
5938
5939 attendee = list->data;
5940
5941 itip_view_set_attendee (view, attendee->cn ? attendee->cn : itip_strip_mailto (attendee->value));
5942
5943 if (attendee->sentby)
5944 itip_view_set_attendee_sentby (view, itip_strip_mailto (attendee->sentby));
5945
5946 if (info->my_address) {
5947 if (!(attendee->value && !g_ascii_strcasecmp (itip_strip_mailto (attendee->value), info->my_address))
5948 && !(attendee->sentby && !g_ascii_strcasecmp (itip_strip_mailto (attendee->sentby), info->my_address))
5949 && (info->from_address && g_ascii_strcasecmp (info->from_address, info->my_address)))
5950 itip_view_set_proxy (view, info->from_name ? info->from_name : info->from_address);
5951 }
5952
5953 e_cal_component_free_attendee_list (list);
5954 }
5955 break;
5956 default:
5957 g_assert_not_reached ();
5958 break;
5959 }
5960 }
5961
5962 e_cal_component_get_summary (info->comp, &text);
5963 itip_view_set_summary (view, text.value ? text.value : C_("cal-itip", "None"));
5964
5965 e_cal_component_get_location (info->comp, &string);
5966 itip_view_set_location (view, string);
5967
5968 /* Status really only applies for REPLY */
5969 if (response_enabled && info->method == ICAL_METHOD_REPLY) {
5970 e_cal_component_get_attendee_list (info->comp, &list);
5971 if (list != NULL) {
5972 ECalComponentAttendee *a = list->data;
5973
5974 switch (a->status) {
5975 case ICAL_PARTSTAT_ACCEPTED:
5976 itip_view_set_status (view, _("Accepted"));
5977 break;
5978 case ICAL_PARTSTAT_TENTATIVE:
5979 itip_view_set_status (view, _("Tentatively Accepted"));
5980 break;
5981 case ICAL_PARTSTAT_DECLINED:
5982 itip_view_set_status (view, _("Declined"));
5983 break;
5984 case ICAL_PARTSTAT_DELEGATED:
5985 itip_view_set_status (view, _("Delegated"));
5986 break;
5987 default:
5988 itip_view_set_status (view, _("Unknown"));
5989 }
5990 }
5991 e_cal_component_free_attendee_list (list);
5992 }
5993
5994 if (info->method == ICAL_METHOD_REPLY
5995 || info->method == ICAL_METHOD_COUNTER
5996 || info->method == ICAL_METHOD_DECLINECOUNTER) {
5997 /* FIXME Check spec to see if multiple comments are actually valid */
5998 /* Comments for iTIP are limited to one per object */
5999 e_cal_component_get_comment_list (info->comp, &list);
6000 if (list) {
6001 ECalComponentText *text = list->data;
6002
6003 if (text->value) {
6004 gchar *html;
6005
6006 html = camel_text_to_html (
6007 text->value,
6008 CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
6009 CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
6010 CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES,
6011 0);
6012
6013 itip_view_set_comment (view, html);
6014
6015 g_free (html);
6016 }
6017 }
6018 e_cal_component_free_text_list (list);
6019 }
6020
6021 e_cal_component_get_description_list (info->comp, &list);
6022 for (l = list; l; l = l->next) {
6023 ECalComponentText *text = l->data;
6024
6025 if (!gstring && text->value)
6026 gstring = g_string_new (text->value);
6027 else if (text->value)
6028 g_string_append_printf (gstring, "\n\n%s", text->value);
6029 }
6030
6031 e_cal_component_free_text_list (list);
6032
6033 if (gstring) {
6034 gchar *html;
6035
6036 html = camel_text_to_html (
6037 gstring->str,
6038 CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
6039 CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
6040 CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
6041 CAMEL_MIME_FILTER_TOHTML_MARK_CITATION |
6042 CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES,
6043 0);
6044
6045 itip_view_set_description (view, html);
6046 g_string_free (gstring, TRUE);
6047 g_free (html);
6048 }
6049
6050 to_zone = e_shell_settings_get_pointer (shell_settings, "cal-timezone");
6051
6052 e_cal_component_get_dtstart (info->comp, &datetime);
6053 info->start_time = 0;
6054 if (datetime.value) {
6055 struct tm start_tm;
6056
6057 /* If the timezone is not in the component, guess the local time */
6058 /* Should we guess if the timezone is an olsen name somehow? */
6059 if (datetime.value->is_utc)
6060 from_zone = icaltimezone_get_utc_timezone ();
6061 else if (!datetime.value->is_utc && datetime.tzid)
6062 from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid);
6063 else
6064 from_zone = NULL;
6065
6066 start_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone);
6067
6068 itip_view_set_start (view, &start_tm, datetime.value->is_date);
6069 info->start_time = icaltime_as_timet_with_zone (*datetime.value, from_zone);
6070 }
6071
6072 icalcomp = e_cal_component_get_icalcomponent (info->comp);
6073
6074 /* Set the recurrence id */
6075 if (check_is_instance (icalcomp) && datetime.value) {
6076 ECalComponentRange *recur_id;
6077 struct icaltimetype icaltime = icaltime_convert_to_zone (*datetime.value, to_zone);
6078
6079 recur_id = g_new0 (ECalComponentRange, 1);
6080 recur_id->type = E_CAL_COMPONENT_RANGE_SINGLE;
6081 recur_id->datetime.value = &icaltime;
6082 recur_id->datetime.tzid = icaltimezone_get_tzid (to_zone);
6083 e_cal_component_set_recurid (info->comp, recur_id);
6084 g_free (recur_id); /* it's ok to call g_free here */
6085 }
6086 e_cal_component_free_datetime (&datetime);
6087
6088 e_cal_component_get_dtend (info->comp, &datetime);
6089 info->end_time = 0;
6090 if (datetime.value) {
6091 struct tm end_tm;
6092
6093 /* If the timezone is not in the component, guess the local time */
6094 /* Should we guess if the timezone is an olsen name somehow? */
6095 if (datetime.value->is_utc)
6096 from_zone = icaltimezone_get_utc_timezone ();
6097 else if (!datetime.value->is_utc && datetime.tzid)
6098 from_zone = icalcomponent_get_timezone (info->top_level, datetime.tzid);
6099 else
6100 from_zone = NULL;
6101
6102 if (datetime.value->is_date) {
6103 /* RFC says the DTEND is not inclusive, thus subtract one day
6104 * if we have a date */
6105
6106 icaltime_adjust (datetime.value, -1, 0, 0, 0);
6107 }
6108
6109 end_tm = icaltimetype_to_tm_with_zone (datetime.value, from_zone, to_zone);
6110
6111 itip_view_set_end (view, &end_tm, datetime.value->is_date);
6112 info->end_time = icaltime_as_timet_with_zone (*datetime.value, from_zone);
6113 }
6114 e_cal_component_free_datetime (&datetime);
6115
6116 /* Recurrence info */
6117 /* FIXME Better recurring description */
6118 if (e_cal_component_has_recurrences (info->comp)) {
6119 /* FIXME Tell the user we don't support recurring tasks */
6120 switch (info->type) {
6121 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
6122 itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This meeting recurs"));
6123 break;
6124 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
6125 itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This task recurs"));
6126 break;
6127 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
6128 itip_view_add_upper_info_item (view, ITIP_VIEW_INFO_ITEM_TYPE_INFO, _("This memo recurs"));
6129 break;
6130 default:
6131 g_assert_not_reached ();
6132 break;
6133 }
6134 }
6135
6136 g_signal_connect (
6137 view, "response",
6138 G_CALLBACK (view_response_cb), info);
6139
6140 if (response_enabled) {
6141 itip_view_set_show_free_time_check (view, info->type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS && (info->method == ICAL_METHOD_PUBLISH || info->method == ICAL_METHOD_REQUEST));
6142
6143 if (info->calendar_uid) {
6144 start_calendar_server_by_uid (info, view, info->calendar_uid, info->type);
6145 } else {
6146 find_server (info, view, info->comp);
6147 set_buttons_sensitive (info, view);
6148 }
6149 } else if (view->priv->dom_document) {
6150 /* The Open Calendar button can be shown, thus enable it */
6151 WebKitDOMElement *el;
6152
6153 el = webkit_dom_document_get_element_by_id (
6154 view->priv->dom_document, BUTTON_OPEN_CALENDAR);
6155 webkit_dom_html_button_element_set_disabled (
6156 WEBKIT_DOM_HTML_BUTTON_ELEMENT (el), FALSE);
6157 }
6158 }