1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
9 * for more details.
10 *
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
13 *
14 *
15 * Authors:
16 * JP Rosevear <jpr@ximian.com>
17 *
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 #include "evolution-config.h"
23
24 #include <time.h>
25 #include <glib/gi18n-lib.h>
26
27 #define LIBICAL_GLIB_UNSTABLE_API 1
28 #include <libical-glib/libical-glib.h>
29 #undef LIBICAL_GLIB_UNSTABLE_API
30
31 #include <libsoup/soup.h>
32
33 #include <libedataserver/libedataserver.h>
34
35 #include "composer/e-msg-composer.h"
36 #include "libemail-engine/libemail-engine.h"
37
38 #include "calendar-config.h"
39 #include "comp-util.h"
40
41 #include "itip-utils.h"
42
43 #define d(x)
44
45 /**
46 * itip_get_default_name_and_address:
47 * @registry: an #ESourceRegistry
48 * @name: return location for the user's real name, or %NULL
49 * @address: return location for the user's email address, or %NULL
50 *
51 * Returns the real name and email address of the default mail identity,
52 * if available. If no default mail identity is available, @name and
53 * @address are set to %NULL and the function returns %FALSE.
54 *
55 * Returns: %TRUE if @name and/or @address were set
56 **/
57 gboolean
itip_get_default_name_and_address(ESourceRegistry * registry,gchar ** name,gchar ** address)58 itip_get_default_name_and_address (ESourceRegistry *registry,
59 gchar **name,
60 gchar **address)
61 {
62 ESource *source;
63 ESourceExtension *extension;
64 const gchar *extension_name;
65 gboolean success;
66
67 source = e_source_registry_ref_default_mail_identity (registry);
68
69 if (source != NULL) {
70 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
71 extension = e_source_get_extension (source, extension_name);
72
73 if (name != NULL)
74 *name = e_source_mail_identity_dup_name (
75 E_SOURCE_MAIL_IDENTITY (extension));
76
77 if (address != NULL)
78 *address = e_source_mail_identity_dup_address (
79 E_SOURCE_MAIL_IDENTITY (extension));
80
81 g_object_unref (source);
82
83 success = TRUE;
84
85 } else {
86 if (name != NULL)
87 *name = NULL;
88
89 if (address != NULL)
90 *address = NULL;
91
92 success = FALSE;
93 }
94
95 return success;
96 }
97
98 static gint
sort_identities_by_email_cb(gconstpointer ptr1,gconstpointer ptr2)99 sort_identities_by_email_cb (gconstpointer ptr1,
100 gconstpointer ptr2)
101 {
102 const gchar **pv1 = (const gchar **) ptr1, **pv2 = (const gchar **) ptr2;
103 const gchar *addr1, *addr2;
104 gint res;
105
106 if (!pv1 || !*pv1 || !pv2 || !*pv2) {
107 if (pv1 && *pv1)
108 return -1;
109 if (pv2 && *pv2)
110 return 1;
111 return 0;
112 }
113
114 addr1 = strchr (*pv1, '<');
115 addr2 = strchr (*pv2, '<');
116
117 if (addr1)
118 addr1++;
119 else
120 addr1 = *pv1;
121 if (addr2)
122 addr2++;
123 else
124 addr2 = *pv2;
125
126 res = g_ascii_strcasecmp (addr1, addr2);
127
128 if (!res && addr1 != *pv1 && addr2 != *pv2)
129 res = g_ascii_strcasecmp (*pv1, *pv2);
130
131 return res;
132 }
133
134 /**
135 * itip_get_user_identities:
136 * @registry: an #ESourceRegistry
137 *
138 * Returns a %NULL-terminated array of name + address strings based on
139 * registered mail identities. Free the returned array with g_strfreev().
140 *
141 * Returns: a %NULL-terminated array of mail identity strings
142 **/
143 gchar **
itip_get_user_identities(ESourceRegistry * registry)144 itip_get_user_identities (ESourceRegistry *registry)
145 {
146 GList *list, *link;
147 const gchar *extension_name;
148 GPtrArray *identities;
149
150 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
151
152 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
153
154 list = e_source_registry_list_enabled (registry, extension_name);
155
156 identities = g_ptr_array_sized_new (g_list_length (list) + 1);
157
158 for (link = list; link != NULL; link = g_list_next (link)) {
159 ESource *source = E_SOURCE (link->data);
160 ESourceMailIdentity *extension;
161 const gchar *name, *address;
162 gchar *aliases;
163
164 if (!e_util_identity_can_send (registry, source))
165 continue;
166
167 extension = e_source_get_extension (source, extension_name);
168
169 name = e_source_mail_identity_get_name (extension);
170 address = e_source_mail_identity_get_address (extension);
171
172 if (address)
173 g_ptr_array_add (identities, camel_internet_address_format_address (name, address));
174
175 aliases = e_source_mail_identity_dup_aliases (extension);
176 if (aliases && *aliases) {
177 CamelInternetAddress *inet_address;
178 gint ii, len;
179
180 inet_address = camel_internet_address_new ();
181 len = camel_address_decode (CAMEL_ADDRESS (inet_address), aliases);
182
183 for (ii = 0; ii < len; ii++) {
184 const gchar *alias_name = NULL, *alias_address = NULL;
185
186 if (camel_internet_address_get (inet_address, ii, &alias_name, &alias_address) &&
187 alias_address && *alias_address) {
188 if (!alias_name || !*alias_name)
189 alias_name = name;
190
191 g_ptr_array_add (identities, camel_internet_address_format_address (alias_name, alias_address));
192 }
193 }
194
195 g_object_unref (inet_address);
196 }
197
198 g_free (aliases);
199 }
200
201 g_list_free_full (list, (GDestroyNotify) g_object_unref);
202
203 g_ptr_array_sort (identities, sort_identities_by_email_cb);
204
205 /* NULL-terminated array */
206 g_ptr_array_add (identities, NULL);
207
208 return (gchar **) g_ptr_array_free (identities, FALSE);
209 }
210
211 /**
212 * itip_get_fallback_identity:
213 * @registry: an #ESourceRegistry
214 *
215 * Returns a name + address string taken from the default mail identity,
216 * but only if the corresponding account is enabled. If the account is
217 * disabled, the function returns %NULL. This is meant to be used as a
218 * fallback identity for organizers. Free the returned string with
219 * g_free().
220 *
221 * Returns: a fallback mail identity, or %NULL
222 **/
223 gchar *
itip_get_fallback_identity(ESourceRegistry * registry)224 itip_get_fallback_identity (ESourceRegistry *registry)
225 {
226 ESource *source;
227 ESourceMailIdentity *mail_identity;
228 const gchar *extension_name;
229 const gchar *address;
230 const gchar *name;
231 gchar *identity = NULL;
232
233 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
234
235 source = e_source_registry_ref_default_mail_identity (registry);
236
237 if (source == NULL)
238 return NULL;
239
240 if (!e_source_registry_check_enabled (registry, source)) {
241 g_object_unref (source);
242 return NULL;
243 }
244
245 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
246 mail_identity = e_source_get_extension (source, extension_name);
247
248 name = e_source_mail_identity_get_name (mail_identity);
249 address = e_source_mail_identity_get_address (mail_identity);
250
251 if (address)
252 identity = camel_internet_address_format_address (name, address);
253
254 g_object_unref (source);
255
256 return identity;
257 }
258
259 /**
260 * itip_address_is_user:
261 * @registry: an #ESourceRegistry
262 * @address: an email address
263 *
264 * Looks for a registered mail identity with a matching email address.
265 *
266 * Returns: %TRUE if a match was found, %FALSE if not
267 **/
268 gboolean
itip_address_is_user(ESourceRegistry * registry,const gchar * address)269 itip_address_is_user (ESourceRegistry *registry,
270 const gchar *address)
271 {
272 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
273 g_return_val_if_fail (address != NULL, FALSE);
274
275 return em_utils_address_is_user (registry, address, FALSE);
276 }
277
278 gboolean
itip_organizer_is_user(ESourceRegistry * registry,ECalComponent * comp,ECalClient * cal_client)279 itip_organizer_is_user (ESourceRegistry *registry,
280 ECalComponent *comp,
281 ECalClient *cal_client)
282 {
283 return itip_organizer_is_user_ex (registry, comp, cal_client, FALSE);
284 }
285
286 gboolean
itip_organizer_is_user_ex(ESourceRegistry * registry,ECalComponent * comp,ECalClient * cal_client,gboolean skip_cap_test)287 itip_organizer_is_user_ex (ESourceRegistry *registry,
288 ECalComponent *comp,
289 ECalClient *cal_client,
290 gboolean skip_cap_test)
291 {
292 ECalComponentOrganizer *organizer;
293 const gchar *strip;
294 gboolean user_org = FALSE;
295
296 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
297
298 if (!e_cal_component_has_organizer (comp) ||
299 (!skip_cap_test && e_client_check_capability (
300 E_CLIENT (cal_client), E_CAL_STATIC_CAPABILITY_NO_ORGANIZER)))
301 return FALSE;
302
303 organizer = e_cal_component_get_organizer (comp);
304 if (organizer && e_cal_component_organizer_get_value (organizer)) {
305 gchar *email = NULL;
306
307 strip = itip_strip_mailto (e_cal_component_organizer_get_value (organizer));
308
309 if (e_client_get_backend_property_sync (E_CLIENT (cal_client),
310 E_CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS,
311 &email, NULL, NULL) &&
312 email && g_ascii_strcasecmp (email, strip) == 0) {
313 e_cal_component_organizer_free (organizer);
314 g_free (email);
315
316 return TRUE;
317 }
318
319 g_free (email);
320
321 if (e_client_check_capability (E_CLIENT (cal_client), E_CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS)) {
322 e_cal_component_organizer_free (organizer);
323 return FALSE;
324 }
325
326 user_org = itip_address_is_user (registry, strip);
327 }
328
329 e_cal_component_organizer_free (organizer);
330
331 return user_org;
332 }
333
334 gboolean
itip_sentby_is_user(ESourceRegistry * registry,ECalComponent * comp,ECalClient * cal_client)335 itip_sentby_is_user (ESourceRegistry *registry,
336 ECalComponent *comp,
337 ECalClient *cal_client)
338 {
339 ECalComponentOrganizer *organizer;
340 const gchar *strip;
341 gboolean user_sentby = FALSE;
342
343 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
344
345 if (!e_cal_component_has_organizer (comp) ||
346 e_client_check_capability (
347 E_CLIENT (cal_client), E_CAL_STATIC_CAPABILITY_NO_ORGANIZER))
348 return FALSE;
349
350 organizer = e_cal_component_get_organizer (comp);
351 if (organizer && e_cal_component_organizer_get_sentby (organizer)) {
352 strip = itip_strip_mailto (e_cal_component_organizer_get_sentby (organizer));
353 user_sentby = itip_address_is_user (registry, strip);
354 }
355
356 e_cal_component_organizer_free (organizer);
357
358 return user_sentby;
359 }
360
361 gboolean
itip_has_any_attendees(ECalComponent * comp)362 itip_has_any_attendees (ECalComponent *comp)
363 {
364 ECalComponentOrganizer *organizer;
365 ECalComponentAttendee *attendee;
366 GSList *attendees = NULL;
367 gboolean res;
368
369 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
370
371 if (!e_cal_component_has_attendees (comp))
372 return FALSE;
373
374 attendees = e_cal_component_get_attendees (comp);
375
376 /* No attendee */
377 if (!attendees)
378 return FALSE;
379
380 /* More than one attendee */
381 if (attendees->next) {
382 g_slist_free_full (attendees, e_cal_component_attendee_free);
383 return TRUE;
384 }
385
386 /* Exactly one attendee, check if it's not the organizer */
387 attendee = attendees->data;
388
389 g_return_val_if_fail (attendee != NULL, FALSE);
390
391 if (!e_cal_component_has_organizer (comp)) {
392 g_slist_free_full (attendees, e_cal_component_attendee_free);
393 return FALSE;
394 }
395
396 organizer = e_cal_component_get_organizer (comp);
397
398 res = e_cal_component_attendee_get_value (attendee) && (!organizer || !e_cal_component_organizer_get_value (organizer) ||
399 g_ascii_strcasecmp (itip_strip_mailto (e_cal_component_attendee_get_value (attendee)),
400 itip_strip_mailto (e_cal_component_organizer_get_value (organizer))) != 0);
401
402 g_slist_free_full (attendees, e_cal_component_attendee_free);
403 e_cal_component_organizer_free (organizer);
404
405 return res;
406 }
407
408 static ECalComponentAttendee *
get_attendee(GSList * attendees,const gchar * address,GHashTable * aliases)409 get_attendee (GSList *attendees,
410 const gchar *address,
411 GHashTable *aliases)
412 {
413 GSList *l;
414
415 if (!address)
416 return NULL;
417
418 for (l = attendees; l; l = l->next) {
419 ECalComponentAttendee *attendee = l->data;
420 const gchar *nomailto;
421
422 nomailto = itip_strip_mailto (e_cal_component_attendee_get_value (attendee));
423 if (!nomailto || !*nomailto)
424 continue;
425
426 if ((address && g_ascii_strcasecmp (nomailto, address) == 0) ||
427 (aliases && g_hash_table_contains (aliases, nomailto))) {
428 return attendee;
429 }
430 }
431
432 return NULL;
433 }
434
435 static ECalComponentAttendee *
get_attendee_if_attendee_sentby_is_user(GSList * attendees,const gchar * address,GHashTable * aliases)436 get_attendee_if_attendee_sentby_is_user (GSList *attendees,
437 const gchar *address,
438 GHashTable *aliases)
439 {
440 GSList *l;
441
442 for (l = attendees; l; l = l->next) {
443 ECalComponentAttendee *attendee = l->data;
444 const gchar *nomailto;
445
446 nomailto = itip_strip_mailto (e_cal_component_attendee_get_sentby (attendee));
447 if (!nomailto || !*nomailto)
448 continue;
449
450 if ((address && g_ascii_strcasecmp (nomailto, address) == 0) ||
451 (aliases && g_hash_table_contains (aliases, nomailto))) {
452 return attendee;
453 }
454 }
455
456 return NULL;
457 }
458
459 static gchar *
html_new_lines_for(const gchar * string)460 html_new_lines_for (const gchar *string)
461 {
462 gchar **lines;
463 gchar *joined;
464
465 lines = g_strsplit_set (string, "\n", -1);
466 joined = g_strjoinv ("<br>", lines);
467 g_strfreev (lines);
468
469 return joined;
470 }
471
472 gboolean
itip_attendee_is_user(ESourceRegistry * registry,ECalComponent * comp,ECalClient * cal_client)473 itip_attendee_is_user (ESourceRegistry *registry,
474 ECalComponent *comp,
475 ECalClient *cal_client)
476 {
477 ESource *source;
478 GSList *attendees;
479 ECalComponentAttendee *attendee = NULL;
480 GList *list, *link;
481 const gchar *extension_name;
482 gchar *address = NULL;
483
484 attendees = e_cal_component_get_attendees (comp);
485
486 if (cal_client)
487 e_client_get_backend_property_sync (
488 E_CLIENT (cal_client),
489 E_CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS,
490 &address, NULL, NULL);
491
492 if (address && *address) {
493 attendee = get_attendee (attendees, address, NULL);
494
495 if (attendee) {
496 g_slist_free_full (attendees, e_cal_component_attendee_free);
497 g_free (address);
498
499 return TRUE;
500 }
501
502 attendee = get_attendee_if_attendee_sentby_is_user (attendees, address, NULL);
503
504 if (attendee) {
505 g_slist_free_full (attendees, e_cal_component_attendee_free);
506 g_free (address);
507
508 return TRUE;
509 }
510 }
511
512 g_free (address);
513 address = NULL;
514
515 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
516 list = e_source_registry_list_enabled (registry, extension_name);
517
518 for (link = list; link != NULL; link = g_list_next (link)) {
519 ESourceMailIdentity *extension;
520 GHashTable *aliases;
521
522 source = E_SOURCE (link->data);
523
524 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
525 extension = e_source_get_extension (source, extension_name);
526
527 address = e_source_mail_identity_dup_address (extension);
528
529 aliases = e_source_mail_identity_get_aliases_as_hash_table (extension);
530
531 attendee = get_attendee (attendees, address, aliases);
532 if (attendee != NULL) {
533 g_slist_free_full (attendees, e_cal_component_attendee_free);
534
535 if (aliases)
536 g_hash_table_destroy (aliases);
537 g_free (address);
538
539 g_list_free_full (list, g_object_unref);
540
541 return TRUE;
542 }
543
544 /* If the account was not found in the attendees list, then
545 * let's check the 'sentby' fields of the attendees if we can
546 * find the account. */
547 attendee = get_attendee_if_attendee_sentby_is_user (attendees, address, aliases);
548 if (attendee) {
549 g_slist_free_full (attendees, e_cal_component_attendee_free);
550
551 if (aliases)
552 g_hash_table_destroy (aliases);
553 g_free (address);
554
555 g_list_free_full (list, g_object_unref);
556
557 return TRUE;
558 }
559
560 if (aliases)
561 g_hash_table_destroy (aliases);
562 g_free (address);
563 }
564
565 g_slist_free_full (attendees, e_cal_component_attendee_free);
566 g_list_free_full (list, g_object_unref);
567
568 return FALSE;
569 }
570
571 gchar *
itip_get_comp_attendee(ESourceRegistry * registry,ECalComponent * comp,ECalClient * cal_client)572 itip_get_comp_attendee (ESourceRegistry *registry,
573 ECalComponent *comp,
574 ECalClient *cal_client)
575 {
576 ESource *source;
577 GSList *attendees;
578 ECalComponentAttendee *attendee = NULL;
579 GList *list, *link;
580 const gchar *extension_name;
581 gchar *address = NULL;
582
583 attendees = e_cal_component_get_attendees (comp);
584
585 if (cal_client)
586 e_client_get_backend_property_sync (
587 E_CLIENT (cal_client),
588 E_CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS,
589 &address, NULL, NULL);
590
591 if (address != NULL && *address != '\0') {
592 attendee = get_attendee (attendees, address, NULL);
593
594 if (attendee) {
595 gchar *user_email;
596
597 user_email = g_strdup (itip_strip_mailto (e_cal_component_attendee_get_value (attendee)));
598 g_slist_free_full (attendees, e_cal_component_attendee_free);
599 g_free (address);
600
601 return user_email;
602 }
603
604 attendee = get_attendee_if_attendee_sentby_is_user (attendees, address, NULL);
605
606 if (attendee != NULL) {
607 gchar *user_email;
608
609 user_email = g_strdup (itip_strip_mailto (e_cal_component_attendee_get_sentby (attendee)));
610 g_slist_free_full (attendees, e_cal_component_attendee_free);
611 g_free (address);
612
613 return user_email;
614 }
615 }
616
617 g_free (address);
618 address = NULL;
619
620 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
621 list = e_source_registry_list_enabled (registry, extension_name);
622
623 for (link = list; link != NULL; link = g_list_next (link)) {
624 ESourceMailIdentity *extension;
625 GHashTable *aliases;
626
627 source = E_SOURCE (link->data);
628
629 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
630 extension = e_source_get_extension (source, extension_name);
631
632 address = e_source_mail_identity_dup_address (extension);
633
634 aliases = e_source_mail_identity_get_aliases_as_hash_table (extension);
635
636 attendee = get_attendee (attendees, address, aliases);
637 if (attendee != NULL) {
638 gchar *user_email;
639
640 user_email = g_strdup (itip_strip_mailto (e_cal_component_attendee_get_value (attendee)));
641 g_slist_free_full (attendees, e_cal_component_attendee_free);
642
643 if (aliases)
644 g_hash_table_destroy (aliases);
645 g_free (address);
646
647 g_list_free_full (list, g_object_unref);
648
649 return user_email;
650 }
651
652 /* If the account was not found in the attendees list, then
653 * let's check the 'sentby' fields of the attendees if we can
654 * find the account. */
655 attendee = get_attendee_if_attendee_sentby_is_user (attendees, address, aliases);
656 if (attendee) {
657 gchar *user_email;
658
659 user_email = g_strdup (itip_strip_mailto (e_cal_component_attendee_get_sentby (attendee)));
660 g_slist_free_full (attendees, e_cal_component_attendee_free);
661
662 if (aliases)
663 g_hash_table_destroy (aliases);
664 g_free (address);
665
666 g_list_free_full (list, g_object_unref);
667
668 return user_email;
669 }
670
671 if (aliases)
672 g_hash_table_destroy (aliases);
673 g_free (address);
674 }
675
676 g_list_free_full (list, g_object_unref);
677
678 /* We could not find the attendee in the component, so just give
679 * the default account address if the email address is not set in
680 * the backend. */
681 /* FIXME do we have a better way ? */
682 itip_get_default_name_and_address (registry, NULL, &address);
683
684 g_slist_free_full (attendees, e_cal_component_attendee_free);
685
686 if (address == NULL)
687 address = g_strdup ("");
688
689 return address;
690 }
691
692 const gchar *
itip_strip_mailto(const gchar * address)693 itip_strip_mailto (const gchar *address)
694 {
695 if (address == NULL)
696 return NULL;
697
698 if (!g_ascii_strncasecmp (address, "mailto:", 7))
699 address += 7;
700
701 return address;
702 }
703
704 static gchar *
get_label(ICalTime * tt,gboolean use_24_hour_format)705 get_label (ICalTime *tt,
706 gboolean use_24_hour_format)
707 {
708 gchar buffer[1000];
709 struct tm tmp_tm;
710
711 tmp_tm = e_cal_util_icaltime_to_tm (tt);
712
713 e_time_format_date_and_time (
714 &tmp_tm, use_24_hour_format, FALSE, FALSE, buffer, 1000);
715
716 return g_strdup (buffer);
717 }
718
719 typedef struct {
720 GHashTable *tzids;
721 ICalComponent *icomp;
722 ECalClient *client;
723 ICalComponent *zones;
724 ICalTime *clamp_vtimezone_from;
725 ICalTime *clamp_vtimezone_to;
726 } ItipUtilTZData;
727
728 static void
foreach_tzid_callback(ICalParameter * param,gpointer data)729 foreach_tzid_callback (ICalParameter *param,
730 gpointer data)
731 {
732 ItipUtilTZData *tz_data = data;
733 ICalTimezone *zone = NULL;
734 ICalComponent *vtimezone_comp, *tzcomp = NULL;
735 const gchar *tzid, *location;
736 gchar *tzid_dup = NULL;
737
738 /* Get the TZID string from the parameter. */
739 tzid = i_cal_parameter_get_tzid (param);
740 if (!tzid)
741 return;
742
743 if (g_hash_table_contains (tz_data->tzids, tzid)) {
744 location = g_hash_table_lookup (tz_data->tzids, tzid);
745 if (location)
746 i_cal_parameter_set_tzid (param, location);
747 return;
748 }
749
750 /* Look for the timezone */
751 if (tz_data->zones != NULL)
752 zone = i_cal_component_get_timezone (tz_data->zones, tzid);
753 if (zone == NULL)
754 zone = i_cal_timezone_get_builtin_timezone_from_tzid (tzid);
755 if (zone == NULL && tz_data->client != NULL &&
756 !e_cal_client_get_timezone_sync (tz_data->client, tzid, &zone, NULL, NULL))
757 zone = NULL;
758 if (zone == NULL)
759 return;
760
761 /* Convert it to a string and add it to the hash. */
762 vtimezone_comp = i_cal_timezone_get_component (zone);
763 if (!vtimezone_comp)
764 return;
765
766 location = i_cal_timezone_get_location (zone);
767 if (location && *location) {
768 ICalProperty *prop;
769
770 tzid_dup = g_strdup (tzid);
771 tzid = tzid_dup;
772
773 /* This frees the original 'tzid' */
774 i_cal_parameter_set_tzid (param, location);
775
776 if (g_hash_table_contains (tz_data->tzids, location)) {
777 g_object_unref (vtimezone_comp);
778 g_free (tzid_dup);
779 return;
780 }
781
782 tzcomp = i_cal_component_clone (vtimezone_comp);
783 prop = i_cal_component_get_first_property (tzcomp, I_CAL_TZID_PROPERTY);
784 if (prop) {
785 i_cal_property_set_tzid (prop, location);
786 g_object_unref (prop);
787 }
788
789 g_hash_table_insert (tz_data->tzids, g_strdup (location), NULL);
790 } else {
791 location = NULL;
792 }
793
794 if (!tzcomp)
795 tzcomp = i_cal_component_clone (vtimezone_comp);
796
797 if (tz_data->clamp_vtimezone_from)
798 e_cal_util_clamp_vtimezone (tzcomp, tz_data->clamp_vtimezone_from, tz_data->clamp_vtimezone_to);
799
800 i_cal_component_take_component (tz_data->icomp, tzcomp);
801 g_hash_table_insert (tz_data->tzids, tzid_dup ? tzid_dup : g_strdup (tzid), g_strdup (location));
802 g_object_unref (vtimezone_comp);
803 }
804
805 static ICalComponent *
comp_toplevel_with_zones(ICalPropertyMethod method,const GSList * ecomps,ECalClient * cal_client,ICalComponent * zones)806 comp_toplevel_with_zones (ICalPropertyMethod method,
807 const GSList *ecomps,
808 ECalClient *cal_client,
809 ICalComponent *zones)
810 {
811 ICalComponent *top_level, *icomp;
812 ICalProperty *prop;
813 ItipUtilTZData tz_data;
814 ECalComponentDateTime *dt;
815 GSList *link;
816
817 g_return_val_if_fail (ecomps != NULL, NULL);
818
819 top_level = e_cal_util_new_top_level ();
820
821 prop = i_cal_property_new_method (method);
822 i_cal_component_take_property (top_level, prop);
823
824 tz_data.tzids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
825 tz_data.icomp = top_level;
826 tz_data.client = cal_client;
827 tz_data.zones = zones;
828 tz_data.clamp_vtimezone_from = NULL;
829 tz_data.clamp_vtimezone_to = NULL;
830
831 dt = e_cal_component_get_dtstart (ecomps->data);
832 if (dt && e_cal_component_datetime_get_value (dt))
833 tz_data.clamp_vtimezone_from = i_cal_time_clone (e_cal_component_datetime_get_value (dt));
834 e_cal_component_datetime_free (dt);
835
836 if (tz_data.clamp_vtimezone_from && !ecomps->next &&
837 !e_cal_component_has_rrules (ecomps->data)) {
838 dt = e_cal_component_get_dtend (ecomps->data);
839
840 if (dt && e_cal_component_datetime_get_value (dt))
841 tz_data.clamp_vtimezone_to = i_cal_time_clone (e_cal_component_datetime_get_value (dt));
842
843 if (!tz_data.clamp_vtimezone_to)
844 tz_data.clamp_vtimezone_to = g_object_ref (tz_data.clamp_vtimezone_from);
845
846 e_cal_component_datetime_free (dt);
847 }
848
849 for (link = (GSList *) ecomps; link; link = g_slist_next (link)) {
850 icomp = e_cal_component_get_icalcomponent (link->data);
851 icomp = i_cal_component_clone (icomp);
852
853 i_cal_component_foreach_tzid (icomp, foreach_tzid_callback, &tz_data);
854
855 i_cal_component_take_component (top_level, icomp);
856 }
857
858 g_hash_table_destroy (tz_data.tzids);
859 g_clear_object (&tz_data.clamp_vtimezone_from);
860 g_clear_object (&tz_data.clamp_vtimezone_to);
861
862 return top_level;
863 }
864
865 static gboolean
users_has_attendee(const GSList * users,const gchar * address)866 users_has_attendee (const GSList *users,
867 const gchar *address)
868 {
869 const GSList *l;
870
871 for (l = users; l != NULL; l = l->next) {
872 if (!g_ascii_strcasecmp (address, l->data))
873 return TRUE;
874 }
875
876 return FALSE;
877 }
878
879 static gchar *
comp_from(ICalPropertyMethod method,ECalComponent * comp,ESourceRegistry * registry,gchar ** from_name)880 comp_from (ICalPropertyMethod method,
881 ECalComponent *comp,
882 ESourceRegistry *registry,
883 gchar **from_name)
884 {
885 ECalComponentOrganizer *organizer;
886 ECalComponentAttendee *attendee;
887 GSList *attendees;
888 gchar *from;
889 gchar *sender = NULL;
890
891 switch (method) {
892 case I_CAL_METHOD_PUBLISH:
893 case I_CAL_METHOD_REQUEST:
894 return itip_get_comp_attendee (registry, comp, NULL);
895
896 case I_CAL_METHOD_REPLY:
897 sender = itip_get_comp_attendee (registry, comp, NULL);
898 if (sender != NULL)
899 return sender;
900 if (!e_cal_component_has_attendees (comp))
901 return NULL;
902 /* coverity[fallthrough] */
903 /* falls through */
904
905 case I_CAL_METHOD_CANCEL:
906
907 case I_CAL_METHOD_ADD:
908
909 organizer = e_cal_component_get_organizer (comp);
910 if (!organizer || !e_cal_component_organizer_get_value (organizer)) {
911 e_cal_component_organizer_free (organizer);
912 e_notice (
913 NULL, GTK_MESSAGE_ERROR,
914 _("An organizer must be set."));
915 return NULL;
916 }
917 if (from_name)
918 *from_name = g_strdup (e_cal_component_organizer_get_cn (organizer));
919 from = g_strdup (itip_strip_mailto (e_cal_component_organizer_get_value (organizer)));
920 e_cal_component_organizer_free (organizer);
921 return from;
922
923 default:
924 attendees = e_cal_component_get_attendees (comp);
925 if (!attendees)
926 return NULL;
927
928 attendee = attendees->data;
929 if (e_cal_component_attendee_get_value (attendee)) {
930 from = g_strdup (itip_strip_mailto (e_cal_component_attendee_get_value (attendee)));
931 if (from_name)
932 *from_name = g_strdup (e_cal_component_attendee_get_cn (attendee));
933 } else
934 from = NULL;
935 g_slist_free_full (attendees, e_cal_component_attendee_free);
936
937 return from;
938 }
939 }
940
941 static EDestination **
comp_to_list(ESourceRegistry * registry,ICalPropertyMethod method,ECalComponent * comp,const GSList * users,gboolean reply_all,const GSList * only_attendees)942 comp_to_list (ESourceRegistry *registry,
943 ICalPropertyMethod method,
944 ECalComponent *comp,
945 const GSList *users,
946 gboolean reply_all,
947 const GSList *only_attendees)
948 {
949 ECalComponentOrganizer *organizer;
950 GSList *attendees, *l;
951 GPtrArray *array = NULL;
952 EDestination *destination;
953 gint len;
954 gchar *sender = NULL;
955
956 union {
957 gpointer *pdata;
958 EDestination **destinations;
959 } convert;
960
961 switch (method) {
962 case I_CAL_METHOD_REQUEST:
963 case I_CAL_METHOD_CANCEL:
964 attendees = e_cal_component_get_attendees (comp);
965 len = g_slist_length (attendees);
966 if (len <= 0) {
967 g_slist_free_full (attendees, e_cal_component_attendee_free);
968 e_notice (
969 NULL, GTK_MESSAGE_ERROR,
970 _("At least one attendee is necessary"));
971 return NULL;
972 }
973
974 organizer = e_cal_component_get_organizer (comp);
975 if (!organizer || !e_cal_component_organizer_get_value (organizer)) {
976 g_slist_free_full (attendees, e_cal_component_attendee_free);
977 e_cal_component_organizer_free (organizer);
978 e_notice (
979 NULL, GTK_MESSAGE_ERROR,
980 _("An organizer must be set."));
981 return NULL;
982 }
983
984 array = g_ptr_array_new ();
985
986 sender = itip_get_comp_attendee (registry, comp, NULL);
987
988 for (l = attendees; l != NULL; l = l->next) {
989 ECalComponentAttendee *att = l->data;
990 ICalParameterCutype cutype;
991
992 if (!e_cal_component_attendee_get_value (att))
993 continue;
994
995 cutype = e_cal_component_attendee_get_cutype (att);
996
997 /* Bugfix: 688711 - Varadhan
998 * Resource is also considered as a "attendee". If the respective backend
999 * is able to successfully book resources automagically, it will appear
1000 * in the users list and thereby won't get added to the list of destinations
1001 * to send the meeting invite, otherwise, as a safety measure, a meeting
1002 * invite will be sent to the resources as well. */
1003 if (cutype != I_CAL_CUTYPE_INDIVIDUAL &&
1004 cutype != I_CAL_CUTYPE_GROUP &&
1005 cutype != I_CAL_CUTYPE_RESOURCE &&
1006 cutype != I_CAL_CUTYPE_UNKNOWN)
1007 continue;
1008 else if (users_has_attendee (users, e_cal_component_attendee_get_value (att)))
1009 continue;
1010 else if (e_cal_component_attendee_get_sentby (att) &&
1011 users_has_attendee (users, e_cal_component_attendee_get_sentby (att)))
1012 continue;
1013 else if (!g_ascii_strcasecmp (e_cal_component_attendee_get_value (att), e_cal_component_organizer_get_value (organizer)))
1014 continue;
1015 else if (e_cal_component_attendee_get_sentby (att) &&
1016 e_cal_component_organizer_get_sentby (organizer) &&
1017 !g_ascii_strcasecmp (e_cal_component_attendee_get_sentby (att), e_cal_component_organizer_get_sentby (organizer)))
1018 continue;
1019 else if (!g_ascii_strcasecmp (itip_strip_mailto (e_cal_component_attendee_get_value (att)), sender))
1020 continue;
1021 else if (e_cal_component_attendee_get_partstat (att) == I_CAL_PARTSTAT_DELEGATED &&
1022 !e_cal_component_attendee_get_rsvp (att) &&
1023 method == I_CAL_METHOD_REQUEST) {
1024 const gchar *delegatedto;
1025
1026 delegatedto = e_cal_component_attendee_get_delegatedto (att);
1027 if (delegatedto && *delegatedto)
1028 continue;
1029 } else if (only_attendees &&
1030 !cal_comp_util_have_in_new_attendees (only_attendees, itip_strip_mailto (e_cal_component_attendee_get_value (att))))
1031 continue;
1032
1033 destination = e_destination_new ();
1034 if (e_cal_component_attendee_get_cn (att))
1035 e_destination_set_name (destination, e_cal_component_attendee_get_cn (att));
1036 e_destination_set_email (destination, itip_strip_mailto (e_cal_component_attendee_get_value (att)));
1037 g_ptr_array_add (array, destination);
1038 }
1039 g_free (sender);
1040 g_slist_free_full (attendees, e_cal_component_attendee_free);
1041 e_cal_component_organizer_free (organizer);
1042 break;
1043
1044 case I_CAL_METHOD_REPLY:
1045
1046 if (reply_all) {
1047 attendees = e_cal_component_get_attendees (comp);
1048 len = g_slist_length (attendees);
1049
1050 if (len <= 0)
1051 return NULL;
1052
1053 array = g_ptr_array_new ();
1054
1055 sender = itip_get_comp_attendee (registry, comp, NULL);
1056
1057 organizer = e_cal_component_get_organizer (comp);
1058 if (organizer && e_cal_component_organizer_get_value (organizer) &&
1059 (!sender || g_ascii_strcasecmp (itip_strip_mailto (e_cal_component_organizer_get_value (organizer)), sender) != 0)) {
1060 destination = e_destination_new ();
1061 e_destination_set_email (
1062 destination,
1063 itip_strip_mailto (e_cal_component_organizer_get_value (organizer)));
1064 if (e_cal_component_organizer_get_cn (organizer))
1065 e_destination_set_name (destination, e_cal_component_organizer_get_cn (organizer));
1066 g_ptr_array_add (array, destination);
1067 }
1068
1069 for (l = attendees; l != NULL; l = l->next) {
1070 ECalComponentAttendee *att = l->data;
1071 ICalParameterCutype cutype;
1072
1073 if (!e_cal_component_attendee_get_value (att))
1074 continue;
1075
1076 cutype = e_cal_component_attendee_get_cutype (att);
1077
1078 if (cutype != I_CAL_CUTYPE_INDIVIDUAL &&
1079 cutype != I_CAL_CUTYPE_GROUP &&
1080 cutype != I_CAL_CUTYPE_UNKNOWN)
1081 continue;
1082 else if (only_attendees &&
1083 !cal_comp_util_have_in_new_attendees (only_attendees,
1084 itip_strip_mailto (e_cal_component_attendee_get_value (att))))
1085 continue;
1086 else if (e_cal_component_organizer_get_value (organizer) &&
1087 g_ascii_strcasecmp (e_cal_component_attendee_get_value (att), e_cal_component_organizer_get_value (organizer)) == 0)
1088 continue;
1089 else if (sender && g_ascii_strcasecmp (itip_strip_mailto (e_cal_component_attendee_get_value (att)), sender) == 0)
1090 continue;
1091
1092 destination = e_destination_new ();
1093 if (e_cal_component_attendee_get_cn (att))
1094 e_destination_set_name (destination, e_cal_component_attendee_get_cn (att));
1095 e_destination_set_email (
1096 destination, itip_strip_mailto (e_cal_component_attendee_get_value (att)));
1097 g_ptr_array_add (array, destination);
1098 }
1099
1100 g_free (sender);
1101 g_slist_free_full (attendees, e_cal_component_attendee_free);
1102 e_cal_component_organizer_free (organizer);
1103
1104 } else {
1105 array = g_ptr_array_new ();
1106
1107 destination = e_destination_new ();
1108 organizer = e_cal_component_get_organizer (comp);
1109 if (organizer && e_cal_component_organizer_get_cn (organizer))
1110 e_destination_set_name (destination, e_cal_component_organizer_get_cn (organizer));
1111 if (e_cal_component_organizer_get_value (organizer))
1112 e_destination_set_email (
1113 destination, itip_strip_mailto (e_cal_component_organizer_get_value (organizer)));
1114 g_ptr_array_add (array, destination);
1115
1116 e_cal_component_organizer_free (organizer);
1117 }
1118 break;
1119
1120 case I_CAL_METHOD_ADD:
1121 case I_CAL_METHOD_REFRESH:
1122 case I_CAL_METHOD_COUNTER:
1123 case I_CAL_METHOD_DECLINECOUNTER:
1124 organizer = e_cal_component_get_organizer (comp);
1125 if (!organizer || !e_cal_component_organizer_get_value (organizer)) {
1126 e_cal_component_organizer_free (organizer);
1127 e_notice (
1128 NULL, GTK_MESSAGE_ERROR,
1129 _("An organizer must be set."));
1130 return NULL;
1131 }
1132
1133 array = g_ptr_array_new ();
1134
1135 destination = e_destination_new ();
1136 if (e_cal_component_organizer_get_cn (organizer))
1137 e_destination_set_name (destination, e_cal_component_organizer_get_cn (organizer));
1138 e_destination_set_email (
1139 destination, itip_strip_mailto (e_cal_component_organizer_get_value (organizer)));
1140 g_ptr_array_add (array, destination);
1141
1142 /* send the status to delegatee to the delegate also*/
1143 attendees = e_cal_component_get_attendees (comp);
1144 sender = itip_get_comp_attendee (registry, comp, NULL);
1145
1146 for (l = attendees; l != NULL; l = l->next) {
1147 ECalComponentAttendee *att = l->data;
1148 ICalParameterCutype cutype;
1149
1150 if (!e_cal_component_attendee_get_value (att))
1151 continue;
1152
1153 cutype = e_cal_component_attendee_get_cutype (att);
1154
1155 if (cutype != I_CAL_CUTYPE_INDIVIDUAL &&
1156 cutype != I_CAL_CUTYPE_GROUP &&
1157 cutype != I_CAL_CUTYPE_UNKNOWN)
1158 continue;
1159
1160 if (sender && (
1161 !g_ascii_strcasecmp (itip_strip_mailto (e_cal_component_attendee_get_value (att)), sender) ||
1162 (e_cal_component_attendee_get_sentby (att) &&
1163 !g_ascii_strcasecmp (itip_strip_mailto (e_cal_component_attendee_get_sentby (att)), sender)))) {
1164 const gchar *delegatedfrom;
1165
1166 delegatedfrom = e_cal_component_attendee_get_delegatedfrom (att);
1167
1168 if (!delegatedfrom || !*delegatedfrom)
1169 break;
1170
1171 destination = e_destination_new ();
1172 e_destination_set_email (
1173 destination, itip_strip_mailto (delegatedfrom));
1174 g_ptr_array_add (array, destination);
1175 }
1176
1177 }
1178 g_slist_free_full (attendees, e_cal_component_attendee_free);
1179 e_cal_component_organizer_free (organizer);
1180
1181 break;
1182 case I_CAL_METHOD_PUBLISH:
1183 if (users) {
1184 const GSList *list;
1185
1186 array = g_ptr_array_new ();
1187
1188 for (list = users; list != NULL; list = list->next) {
1189 destination = e_destination_new ();
1190 e_destination_set_email (destination, list->data);
1191 g_ptr_array_add (array, destination);
1192 }
1193
1194 break;
1195 }
1196 default:
1197 break;
1198 }
1199
1200 if (array == NULL)
1201 return NULL;
1202
1203 g_ptr_array_add (array, NULL);
1204 convert.pdata = g_ptr_array_free (array, FALSE);
1205
1206 return convert.destinations;
1207 }
1208
1209 static gchar *
comp_subject(ESourceRegistry * registry,ICalPropertyMethod method,ECalComponent * comp)1210 comp_subject (ESourceRegistry *registry,
1211 ICalPropertyMethod method,
1212 ECalComponent *comp)
1213 {
1214 ECalComponentText *caltext;
1215 const gchar *description, *prefix = NULL;
1216 GSList *alist, *l;
1217 gchar *subject;
1218 gchar *sender;
1219 ECalComponentAttendee *a = NULL;
1220
1221 caltext = e_cal_component_get_summary (comp);
1222 if (caltext && e_cal_component_text_get_value (caltext)) {
1223 description = e_cal_component_text_get_value (caltext);
1224 } else {
1225 switch (e_cal_component_get_vtype (comp)) {
1226 case E_CAL_COMPONENT_EVENT:
1227 description = _("Event information");
1228 break;
1229 case E_CAL_COMPONENT_TODO:
1230 description = _("Task information");
1231 break;
1232 case E_CAL_COMPONENT_JOURNAL:
1233 description = _("Memo information");
1234 break;
1235 case E_CAL_COMPONENT_FREEBUSY:
1236 description = _("Free/Busy information");
1237 break;
1238 default:
1239 description = _("Calendar information");
1240 }
1241 }
1242
1243 switch (method) {
1244 case I_CAL_METHOD_PUBLISH:
1245 case I_CAL_METHOD_REQUEST:
1246 /* FIXME: If this is an update to a previous
1247 * PUBLISH or REQUEST, then
1248 prefix = U_("Updated");
1249 */
1250 break;
1251
1252 case I_CAL_METHOD_REPLY:
1253 alist = e_cal_component_get_attendees (comp);
1254 sender = itip_get_comp_attendee (registry, comp, NULL);
1255 if (sender) {
1256 for (l = alist; l != NULL; l = l->next) {
1257 const gchar *value, *sentby;
1258
1259 a = l->data;
1260 value = e_cal_component_attendee_get_value (a);
1261 sentby = e_cal_component_attendee_get_sentby (a);
1262
1263 if ((sender && *sender) && (
1264 (value && !g_ascii_strcasecmp (itip_strip_mailto (value), sender)) ||
1265 (sentby && !g_ascii_strcasecmp (itip_strip_mailto (sentby), sender))))
1266 break;
1267 }
1268 g_free (sender);
1269 }
1270
1271 if (a != NULL) {
1272
1273 switch (e_cal_component_attendee_get_partstat (a)) {
1274 case I_CAL_PARTSTAT_ACCEPTED:
1275 /* Translators: This is part of the subject
1276 * line of a meeting request or update email.
1277 * The full subject line would be:
1278 * "Accepted: Meeting Name". */
1279 prefix = C_("Meeting", "Accepted");
1280 break;
1281 case I_CAL_PARTSTAT_TENTATIVE:
1282 /* Translators: This is part of the subject
1283 * line of a meeting request or update email.
1284 * The full subject line would be:
1285 * "Tentatively Accepted: Meeting Name". */
1286 prefix = C_("Meeting", "Tentatively Accepted");
1287 break;
1288 case I_CAL_PARTSTAT_DECLINED:
1289 /* Translators: This is part of the subject
1290 * line of a meeting request or update email.
1291 * The full subject line would be:
1292 * "Declined: Meeting Name". */
1293 prefix = C_("Meeting", "Declined");
1294 break;
1295 case I_CAL_PARTSTAT_DELEGATED:
1296 /* Translators: This is part of the subject
1297 * line of a meeting request or update email.
1298 * The full subject line would be:
1299 * "Delegated: Meeting Name". */
1300 prefix = C_("Meeting", "Delegated");
1301 break;
1302 default:
1303 break;
1304 }
1305 g_slist_free_full (alist, e_cal_component_attendee_free);
1306 }
1307 break;
1308
1309 case I_CAL_METHOD_ADD:
1310 /* Translators: This is part of the subject line of a
1311 * meeting request or update email. The full subject
1312 * line would be: "Updated: Meeting Name". */
1313 prefix = C_("Meeting", "Updated");
1314 break;
1315
1316 case I_CAL_METHOD_CANCEL:
1317 /* Translators: This is part of the subject line of a
1318 * meeting request or update email. The full subject
1319 * line would be: "Cancel: Meeting Name". */
1320 prefix = C_("Meeting", "Cancel");
1321 break;
1322
1323 case I_CAL_METHOD_REFRESH:
1324 /* Translators: This is part of the subject line of a
1325 * meeting request or update email. The full subject
1326 * line would be: "Refresh: Meeting Name". */
1327 prefix = C_("Meeting", "Refresh");
1328 break;
1329
1330 case I_CAL_METHOD_COUNTER:
1331 /* Translators: This is part of the subject line of a
1332 * meeting request or update email. The full subject
1333 * line would be: "Counter-proposal: Meeting Name". */
1334 prefix = C_("Meeting", "Counter-proposal");
1335 break;
1336
1337 case I_CAL_METHOD_DECLINECOUNTER:
1338 /* Translators: This is part of the subject line of a
1339 * meeting request or update email. The full subject
1340 * line would be: "Declined: Meeting Name". */
1341 prefix = C_("Meeting", "Declined");
1342 break;
1343
1344 default:
1345 break;
1346 }
1347
1348 if (prefix != NULL)
1349 subject = g_strdup_printf ("%s: %s", prefix, description);
1350 else
1351 subject = g_strdup (description);
1352
1353 e_cal_component_text_free (caltext);
1354
1355 return subject;
1356 }
1357
1358 static gchar *
comp_content_type(ECalComponent * comp,ICalPropertyMethod method)1359 comp_content_type (ECalComponent *comp,
1360 ICalPropertyMethod method)
1361 {
1362 const gchar *name;
1363
1364 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_FREEBUSY)
1365 name = "freebusy.ifb";
1366 else
1367 name = "calendar.ics";
1368
1369 return g_strdup_printf (
1370 "text/calendar; name=\"%s\"; charset=utf-8; METHOD=%s",
1371 name, i_cal_property_method_to_string (method));
1372 }
1373
1374 static const gchar *
comp_filename(ECalComponent * comp)1375 comp_filename (ECalComponent *comp)
1376 {
1377 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_FREEBUSY)
1378 return "freebusy.ifb";
1379 else
1380 return "calendar.ics";
1381 }
1382
1383 static gchar *
comp_description(ECalComponent * comp,gboolean use_24_hour_format)1384 comp_description (ECalComponent *comp,
1385 gboolean use_24_hour_format)
1386 {
1387 gchar *description;
1388 ECalComponentDateTime *dt;
1389 gchar *start = NULL, *end = NULL;
1390
1391 switch (e_cal_component_get_vtype (comp)) {
1392 case E_CAL_COMPONENT_EVENT:
1393 description = g_strdup (_("Event information"));
1394 break;
1395 case E_CAL_COMPONENT_TODO:
1396 description = g_strdup (_("Task information"));
1397 break;
1398 case E_CAL_COMPONENT_JOURNAL:
1399 description = g_strdup (_("Memo information"));
1400 break;
1401 case E_CAL_COMPONENT_FREEBUSY:
1402 dt = e_cal_component_get_dtstart (comp);
1403 if (dt && e_cal_component_datetime_get_value (dt))
1404 start = get_label (e_cal_component_datetime_get_value (dt), use_24_hour_format);
1405 e_cal_component_datetime_free (dt);
1406
1407 dt = e_cal_component_get_dtend (comp);
1408 if (dt && e_cal_component_datetime_get_value (dt))
1409 end = get_label (e_cal_component_datetime_get_value (dt), use_24_hour_format);
1410 e_cal_component_datetime_free (dt);
1411
1412 if (start != NULL && end != NULL)
1413 description = g_strdup_printf (
1414 _("Free/Busy information (%s to %s)"),
1415 start, end);
1416 else
1417 description = g_strdup (_("Free/Busy information"));
1418 g_free (start);
1419 g_free (end);
1420 break;
1421 default:
1422 description = g_strdup (_("iCalendar information"));
1423 break;
1424 }
1425
1426 return description;
1427 }
1428
1429 static gboolean
comp_server_send_sync(ICalPropertyMethod method,const GSList * ecomps,ECalClient * cal_client,ICalComponent * zones,GSList ** users,GCancellable * cancellable,GError ** error)1430 comp_server_send_sync (ICalPropertyMethod method,
1431 const GSList *ecomps,
1432 ECalClient *cal_client,
1433 ICalComponent *zones,
1434 GSList **users,
1435 GCancellable *cancellable,
1436 GError **error)
1437 {
1438 ICalComponent *top_level, *returned_icomp = NULL;
1439 gboolean retval = TRUE;
1440 GError *local_error = NULL;
1441
1442 top_level = comp_toplevel_with_zones (method, ecomps, cal_client, zones);
1443 d (printf ("itip-utils.c: comp_server_send_sync: calling e_cal_send_objects... \n"));
1444
1445 e_cal_client_send_objects_sync (
1446 cal_client, top_level, E_CAL_OPERATION_FLAG_NONE, users,
1447 &returned_icomp, cancellable, &local_error);
1448
1449 if (g_error_matches (local_error, E_CAL_CLIENT_ERROR, E_CAL_CLIENT_ERROR_OBJECT_ID_ALREADY_EXISTS)) {
1450 g_propagate_error (error, g_error_new (local_error->domain, local_error->code,
1451 _("Unable to book a resource, the new event collides with some other.")));
1452 g_clear_error (&local_error);
1453 retval = FALSE;
1454
1455 } else if (local_error != NULL) {
1456 g_prefix_error (&local_error, "%s", _("Unable to book a resource, error: "));
1457 g_propagate_error (error, local_error);
1458 retval = FALSE;
1459 }
1460
1461 g_clear_object (&returned_icomp);
1462 g_clear_object (&top_level);
1463
1464 return retval;
1465 }
1466
1467 static gboolean
comp_limit_attendees(ESourceRegistry * registry,ECalComponent * comp)1468 comp_limit_attendees (ESourceRegistry *registry,
1469 ECalComponent *comp)
1470 {
1471 ICalComponent *icomp;
1472 ICalProperty *prop;
1473 gboolean found = FALSE, match = FALSE;
1474 GSList *l, *list = NULL;
1475
1476 icomp = e_cal_component_get_icalcomponent (comp);
1477
1478 for (prop = i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY);
1479 prop != NULL;
1480 g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, I_CAL_ATTENDEE_PROPERTY)) {
1481 gchar *attendee;
1482 gchar *attendee_text;
1483
1484 /* If we've already found something, just erase the rest */
1485 if (found) {
1486 list = g_slist_prepend (list, g_object_ref (prop));
1487 continue;
1488 }
1489
1490 attendee = i_cal_property_get_value_as_string (prop);
1491 if (!attendee)
1492 continue;
1493
1494 attendee_text = g_strdup (itip_strip_mailto (attendee));
1495 g_free (attendee);
1496 attendee_text = g_strstrip (attendee_text);
1497 found = match = itip_address_is_user (registry, attendee_text);
1498
1499 if (!found) {
1500 ICalParameter *param;
1501
1502 param = i_cal_property_get_first_parameter (prop, I_CAL_SENTBY_PARAMETER);
1503 if (param) {
1504 const gchar *attendee_sentby;
1505 gchar *attendee_sentby_text;
1506
1507 attendee_sentby = i_cal_parameter_get_sentby (param);
1508 attendee_sentby = itip_strip_mailto (attendee_sentby);
1509 attendee_sentby_text = g_strstrip (g_strdup (attendee_sentby));
1510 found = match = itip_address_is_user (
1511 registry, attendee_sentby_text);
1512
1513 g_free (attendee_sentby_text);
1514 g_object_unref (param);
1515 }
1516 }
1517
1518 g_free (attendee_text);
1519
1520 if (!match)
1521 list = g_slist_prepend (list, g_object_ref (prop));
1522 }
1523
1524 for (l = list; l != NULL; l = l->next) {
1525 prop = l->data;
1526
1527 i_cal_component_remove_property (icomp, prop);
1528 g_object_unref (prop);
1529 }
1530 g_slist_free (list);
1531
1532 return found;
1533 }
1534
1535 static void
comp_sentby(ECalComponent * comp,ECalClient * cal_client,ESourceRegistry * registry)1536 comp_sentby (ECalComponent *comp,
1537 ECalClient *cal_client,
1538 ESourceRegistry *registry)
1539 {
1540 ECalComponentOrganizer *organizer;
1541 GSList * attendees, *l;
1542 gchar *name = NULL;
1543 gchar *address = NULL;
1544 gchar *user;
1545
1546 itip_get_default_name_and_address (registry, &name, &address);
1547
1548 organizer = e_cal_component_get_organizer (comp);
1549 if ((!organizer || !e_cal_component_organizer_get_value (organizer)) && name != NULL && address != NULL) {
1550 gchar *tmp;
1551
1552 e_cal_component_organizer_free (organizer);
1553
1554 tmp = g_strdup_printf ("mailto:%s", address);
1555 organizer = e_cal_component_organizer_new_full (tmp, NULL, name, NULL);
1556
1557 e_cal_component_set_organizer (comp, organizer);
1558
1559 e_cal_component_organizer_free (organizer);
1560
1561 g_free (tmp);
1562 g_free (name);
1563 g_free (address);
1564 return;
1565 }
1566
1567 attendees = e_cal_component_get_attendees (comp);
1568 user = itip_get_comp_attendee (registry, comp, cal_client);
1569 for (l = attendees; l && user; l = l->next) {
1570 ECalComponentAttendee *a = l->data;
1571 const gchar *value, *sentby;
1572
1573 if (!a)
1574 continue;
1575
1576 value = e_cal_component_attendee_get_value (a);
1577 if (value)
1578 value = itip_strip_mailto (value);
1579 sentby = e_cal_component_attendee_get_sentby (a);
1580 if (sentby)
1581 sentby = itip_strip_mailto (sentby);
1582
1583 if ((value && !g_ascii_strcasecmp (value, user)) ||
1584 (sentby && !g_ascii_strcasecmp (sentby, user))) {
1585 g_slist_free_full (attendees, e_cal_component_attendee_free);
1586 e_cal_component_organizer_free (organizer);
1587 g_free (user);
1588 g_free (name);
1589 g_free (address);
1590 return;
1591 }
1592 }
1593
1594 g_slist_free_full (attendees, e_cal_component_attendee_free);
1595 g_free (user);
1596
1597 if (!itip_organizer_is_user (registry, comp, cal_client) &&
1598 !itip_sentby_is_user (registry, comp, cal_client) &&
1599 address && organizer) {
1600 ECalComponentOrganizer *sentbyorg;
1601 gchar *sentby;
1602
1603 sentby = g_strdup_printf ("mailto:%s", address);
1604 sentbyorg = e_cal_component_organizer_new_full (
1605 e_cal_component_organizer_get_value (organizer),
1606 sentby,
1607 e_cal_component_organizer_get_cn (organizer),
1608 e_cal_component_organizer_get_language (organizer));
1609
1610 e_cal_component_set_organizer (comp, sentbyorg);
1611
1612 e_cal_component_organizer_free (sentbyorg);
1613 g_free (sentby);
1614 }
1615
1616 g_free (name);
1617 g_free (address);
1618 e_cal_component_organizer_free (organizer);
1619 }
1620
1621 static ECalComponent *
comp_minimal(ESourceRegistry * registry,ECalComponent * comp,gboolean attendee)1622 comp_minimal (ESourceRegistry *registry,
1623 ECalComponent *comp,
1624 gboolean attendee)
1625 {
1626 ECalComponent *clone;
1627 ECalComponentOrganizer *organizer;
1628 ECalComponentRange *recur_id;
1629 ICalComponent *icomp, *icomp_clone;
1630 ICalProperty *prop;
1631 ICalTime *itt;
1632 const gchar *uid;
1633 GSList *comments;
1634
1635 clone = e_cal_component_new ();
1636 e_cal_component_set_new_vtype (clone, e_cal_component_get_vtype (comp));
1637
1638 if (attendee) {
1639 GSList *attendees;
1640
1641 attendees = e_cal_component_get_attendees (comp);
1642 e_cal_component_set_attendees (clone, attendees);
1643
1644 g_slist_free_full (attendees, e_cal_component_attendee_free);
1645
1646 if (!comp_limit_attendees (registry, clone)) {
1647 e_notice (
1648 NULL, GTK_MESSAGE_ERROR,
1649 _("You must be an attendee of the event."));
1650 goto error;
1651 }
1652 }
1653
1654 itt = i_cal_time_new_from_timet_with_zone (time (NULL), FALSE, i_cal_timezone_get_utc_timezone ());
1655 e_cal_component_set_dtstamp (clone, itt);
1656 g_clear_object (&itt);
1657
1658 organizer = e_cal_component_get_organizer (comp);
1659 if (!organizer || !e_cal_component_organizer_get_value (organizer)) {
1660 e_cal_component_organizer_free (organizer);
1661 goto error;
1662 }
1663 e_cal_component_set_organizer (clone, organizer);
1664 e_cal_component_organizer_free (organizer);
1665
1666 uid = e_cal_component_get_uid (comp);
1667 e_cal_component_set_uid (clone, uid);
1668
1669 comments = e_cal_component_get_comments (comp);
1670 if (g_slist_length (comments) <= 1) {
1671 e_cal_component_set_comments (clone, comments);
1672 } else {
1673 GSList *l = comments;
1674
1675 comments = g_slist_remove_link (comments, l);
1676 e_cal_component_set_comments (clone, l);
1677 g_slist_free_full (l, e_cal_component_text_free);
1678 }
1679 g_slist_free_full (comments, e_cal_component_text_free);
1680
1681 recur_id = e_cal_component_get_recurid (comp);
1682 if (recur_id)
1683 e_cal_component_set_recurid (clone, recur_id);
1684 e_cal_component_range_free (recur_id);
1685
1686 icomp = e_cal_component_get_icalcomponent (comp);
1687 icomp_clone = e_cal_component_get_icalcomponent (clone);
1688 for (prop = i_cal_component_get_first_property (icomp, I_CAL_X_PROPERTY);
1689 prop != NULL;
1690 g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, I_CAL_X_PROPERTY))
1691 {
1692 ICalProperty *p;
1693
1694 p = i_cal_property_clone (prop);
1695 i_cal_component_take_property (icomp_clone, p);
1696 }
1697
1698 return clone;
1699
1700 error:
1701 g_object_unref (clone);
1702 return NULL;
1703 }
1704
1705 static void
strip_x_microsoft_props(ECalComponent * comp)1706 strip_x_microsoft_props (ECalComponent *comp)
1707 {
1708 GSList *lst = NULL, *l;
1709 ICalComponent *icomp;
1710 ICalProperty *prop;
1711
1712 g_return_if_fail (comp != NULL);
1713
1714 icomp = e_cal_component_get_icalcomponent (comp);
1715 g_return_if_fail (icomp != NULL);
1716
1717 for (prop = i_cal_component_get_first_property (icomp, I_CAL_X_PROPERTY);
1718 prop;
1719 g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, I_CAL_X_PROPERTY)) {
1720 const gchar *x_name = i_cal_property_get_x_name (prop);
1721
1722 if (x_name && g_ascii_strncasecmp (x_name, "X-MICROSOFT-", 12) == 0)
1723 lst = g_slist_prepend (lst, g_object_ref (prop));
1724 }
1725
1726 for (l = lst; l != NULL; l = l->next) {
1727 prop = l->data;
1728
1729 i_cal_component_remove_property (icomp, prop);
1730 }
1731
1732 g_slist_free_full (lst, g_object_unref);
1733 }
1734
1735 /* https://tools.ietf.org/html/rfc6638#section-7.3 */
1736 static void
remove_schedule_status_parameters(ECalComponent * comp)1737 remove_schedule_status_parameters (ECalComponent *comp)
1738 {
1739 ICalComponent *icomp;
1740 ICalProperty *prop;
1741
1742 g_return_if_fail (E_IS_CAL_COMPONENT (comp));
1743
1744 icomp = e_cal_component_get_icalcomponent (comp);
1745
1746 if (!icomp)
1747 return;
1748
1749 for (prop = i_cal_component_get_first_property (icomp, I_CAL_ORGANIZER_PROPERTY);
1750 prop;
1751 g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, I_CAL_ORGANIZER_PROPERTY)) {
1752 i_cal_property_remove_parameter_by_kind (prop, I_CAL_SCHEDULESTATUS_PARAMETER);
1753 }
1754
1755 for (prop = i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY);
1756 prop;
1757 g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, I_CAL_ATTENDEE_PROPERTY)) {
1758 i_cal_property_remove_parameter_by_kind (prop, I_CAL_SCHEDULESTATUS_PARAMETER);
1759 }
1760 }
1761
1762 static ECalComponent *
comp_compliant_one(ESourceRegistry * registry,ICalPropertyMethod method,ECalComponent * comp,ECalClient * client,ICalComponent * zones,ICalTimezone * default_zone,gboolean strip_alarms)1763 comp_compliant_one (ESourceRegistry *registry,
1764 ICalPropertyMethod method,
1765 ECalComponent *comp,
1766 ECalClient *client,
1767 ICalComponent *zones,
1768 ICalTimezone *default_zone,
1769 gboolean strip_alarms)
1770 {
1771 ECalComponent *clone, *temp_clone;
1772 ICalTime *itt;
1773
1774 clone = e_cal_component_clone (comp);
1775 itt = i_cal_time_new_from_timet_with_zone (time (NULL), FALSE, i_cal_timezone_get_utc_timezone ());
1776 e_cal_component_set_dtstamp (clone, itt);
1777 g_clear_object (&itt);
1778
1779 /* Make UNTIL date a datetime in a simple recurrence */
1780 if (e_cal_component_has_recurrences (clone) &&
1781 e_cal_component_has_simple_recurrence (clone)) {
1782 GSList *rrule_list;
1783 ICalRecurrence *rt;
1784
1785 rrule_list = e_cal_component_get_rrules (clone);
1786 rt = rrule_list->data;
1787
1788 itt = i_cal_recurrence_get_until (rt);
1789 if (itt && !i_cal_time_is_null_time (itt) && i_cal_time_is_date (itt)) {
1790 ECalComponentDateTime *dt;
1791 ICalTime *dtvalue;
1792 ICalTimezone *from_zone = NULL, *to_zone;
1793
1794 dt = e_cal_component_get_dtstart (clone);
1795 dtvalue = dt ? e_cal_component_datetime_get_value (dt) : NULL;
1796
1797 if (!dtvalue || i_cal_time_is_date (dtvalue)) {
1798 from_zone = default_zone;
1799 } else if (!e_cal_component_datetime_get_tzid (dt)) {
1800 from_zone = i_cal_timezone_get_utc_timezone ();
1801 } else {
1802 if (zones != NULL)
1803 from_zone = i_cal_component_get_timezone (zones, e_cal_component_datetime_get_tzid (dt));
1804 if (from_zone == NULL)
1805 from_zone = i_cal_timezone_get_builtin_timezone_from_tzid (e_cal_component_datetime_get_tzid (dt));
1806 if (from_zone == NULL && client != NULL)
1807 /* FIXME Error checking */
1808 if (!e_cal_client_get_timezone_sync (client, e_cal_component_datetime_get_tzid (dt), &from_zone, NULL, NULL))
1809 from_zone = NULL;
1810 }
1811
1812 to_zone = i_cal_timezone_get_utc_timezone ();
1813
1814 i_cal_time_set_time (itt,
1815 i_cal_time_get_hour (dtvalue),
1816 i_cal_time_get_minute (dtvalue),
1817 i_cal_time_get_second (dtvalue));
1818 i_cal_time_set_is_date (itt, FALSE);
1819
1820 i_cal_time_convert_timezone (itt, from_zone, to_zone);
1821 i_cal_time_set_timezone (itt, to_zone);
1822
1823 i_cal_recurrence_set_until (rt, itt);
1824
1825 e_cal_component_datetime_free (dt);
1826 e_cal_component_set_rrules (clone, rrule_list);
1827 e_cal_component_abort_sequence (clone);
1828 }
1829
1830 g_slist_free_full (rrule_list, g_object_unref);
1831 g_clear_object (&itt);
1832 }
1833
1834 /* We delete incoming alarms if requested, even this helps with outlook */
1835 if (strip_alarms) {
1836 e_cal_component_remove_all_alarms (clone);
1837 } else {
1838 /* Always strip procedure alarms, because of security */
1839 GSList *uids, *link;
1840
1841 uids = e_cal_component_get_alarm_uids (clone);
1842
1843 for (link = uids; link; link = g_slist_next (link)) {
1844 ECalComponentAlarm *alarm;
1845
1846 alarm = e_cal_component_get_alarm (clone, link->data);
1847 if (alarm) {
1848 ECalComponentAlarmAction action;
1849
1850 action = e_cal_component_alarm_get_action (alarm);
1851 e_cal_component_alarm_free (alarm);
1852
1853 if (action == E_CAL_COMPONENT_ALARM_PROCEDURE)
1854 e_cal_component_remove_alarm (clone, link->data);
1855 }
1856 }
1857
1858 g_slist_free_full (uids, g_free);
1859 }
1860
1861 strip_x_microsoft_props (clone);
1862
1863 /* Strip X-LIC-ERROR stuff */
1864 e_cal_component_strip_errors (clone);
1865
1866 /* Comply with itip spec */
1867 switch (method) {
1868 case I_CAL_METHOD_PUBLISH:
1869 comp_sentby (clone, client, registry);
1870 e_cal_component_set_attendees (clone, NULL);
1871 break;
1872 case I_CAL_METHOD_REQUEST:
1873 comp_sentby (clone, client, registry);
1874 break;
1875 case I_CAL_METHOD_CANCEL:
1876 comp_sentby (clone, client, registry);
1877 break;
1878 case I_CAL_METHOD_REPLY:
1879 break;
1880 case I_CAL_METHOD_ADD:
1881 break;
1882 case I_CAL_METHOD_REFRESH:
1883 /* Need to remove almost everything */
1884 temp_clone = comp_minimal (registry, clone, TRUE);
1885 g_object_unref (clone);
1886 clone = temp_clone;
1887 break;
1888 case I_CAL_METHOD_COUNTER:
1889 break;
1890 case I_CAL_METHOD_DECLINECOUNTER:
1891 /* Need to remove almost everything */
1892 temp_clone = comp_minimal (registry, clone, FALSE);
1893 g_object_unref (clone);
1894 clone = temp_clone;
1895 break;
1896 default:
1897 break;
1898 }
1899
1900 remove_schedule_status_parameters (clone);
1901
1902 return clone;
1903 }
1904
1905 static gboolean
comp_compliant(ESourceRegistry * registry,ICalPropertyMethod method,GSList * ecomps,gboolean unref_orig_ecomp,ECalClient * client,ICalComponent * zones,ICalTimezone * default_zone,gboolean strip_alarms)1906 comp_compliant (ESourceRegistry *registry,
1907 ICalPropertyMethod method,
1908 GSList *ecomps,
1909 gboolean unref_orig_ecomp,
1910 ECalClient *client,
1911 ICalComponent *zones,
1912 ICalTimezone *default_zone,
1913 gboolean strip_alarms)
1914 {
1915 GSList *link;
1916
1917 if (!ecomps)
1918 return FALSE;
1919
1920 for (link = ecomps; link; link = g_slist_next (link)) {
1921 ECalComponent *original_comp = link->data, *new_comp;
1922
1923 new_comp = comp_compliant_one (registry, method, original_comp, client, zones, default_zone, strip_alarms);
1924 if (new_comp) {
1925 cal_comp_util_copy_new_attendees (new_comp, original_comp);
1926
1927 if (unref_orig_ecomp)
1928 g_object_unref (original_comp);
1929
1930 link->data = new_comp;
1931 } else {
1932 return FALSE;
1933 }
1934 }
1935
1936 return TRUE;
1937 }
1938
1939 void
itip_cal_mime_attach_free(gpointer ptr)1940 itip_cal_mime_attach_free (gpointer ptr)
1941 {
1942 struct CalMimeAttach *mime_attach = ptr;
1943
1944 if (mime_attach) {
1945 g_free (mime_attach->filename);
1946 g_free (mime_attach->content_type);
1947 g_free (mime_attach->content_id);
1948 g_free (mime_attach->description);
1949 g_free (mime_attach->encoded_data);
1950 g_free (mime_attach);
1951 }
1952 }
1953
1954 static void
append_cal_attachments(EMsgComposer * composer,GSList * attach_list)1955 append_cal_attachments (EMsgComposer *composer,
1956 GSList *attach_list)
1957 {
1958 struct CalMimeAttach *mime_attach;
1959 GSList *l;
1960
1961 for (l = attach_list; l; l = l->next) {
1962 CamelMimePart *attachment;
1963
1964 mime_attach = (struct CalMimeAttach *) l->data;
1965
1966 attachment = camel_mime_part_new ();
1967 camel_mime_part_set_content (
1968 attachment, mime_attach->encoded_data,
1969 mime_attach->length, mime_attach->content_type);
1970 if (mime_attach->content_id)
1971 camel_mime_part_set_content_id (
1972 attachment, mime_attach->content_id);
1973 if (mime_attach->filename != NULL)
1974 camel_mime_part_set_filename (
1975 attachment, mime_attach->filename);
1976 if (mime_attach->description != NULL)
1977 camel_mime_part_set_description (
1978 attachment, mime_attach->description);
1979 if (mime_attach->disposition)
1980 camel_mime_part_set_disposition (
1981 attachment, "inline");
1982 else
1983 camel_mime_part_set_disposition (
1984 attachment, "attachment");
1985 e_msg_composer_attach (composer, attachment);
1986 g_object_unref (attachment);
1987 }
1988
1989 g_slist_free_full (attach_list, itip_cal_mime_attach_free);
1990 }
1991
1992 static ESource *
find_enabled_identity(ESourceRegistry * registry,const gchar * id_address)1993 find_enabled_identity (ESourceRegistry *registry,
1994 const gchar *id_address)
1995 {
1996 GList *list, *link;
1997 ESource *mail_identity = NULL;
1998 const gchar *extension_name;
1999
2000 if (id_address == NULL)
2001 return NULL;
2002
2003 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
2004 list = e_source_registry_list_enabled (registry, extension_name);
2005
2006 for (link = list; link != NULL; link = g_list_next (link)) {
2007 ESource *source = E_SOURCE (link->data);
2008 ESourceMailIdentity *extension;
2009 GHashTable *aliases;
2010 const gchar *address;
2011
2012 extension = e_source_get_extension (source, extension_name);
2013 address = e_source_mail_identity_get_address (extension);
2014
2015 if (address && g_ascii_strcasecmp (address, id_address) == 0) {
2016 mail_identity = g_object_ref (source);
2017 break;
2018 }
2019
2020 aliases = e_source_mail_identity_get_aliases_as_hash_table (extension);
2021 if (aliases) {
2022 if (g_hash_table_contains (aliases, id_address))
2023 mail_identity = g_object_ref (source);
2024 g_hash_table_destroy (aliases);
2025
2026 if (mail_identity)
2027 break;
2028 }
2029 }
2030
2031 g_list_free_full (list, (GDestroyNotify) g_object_unref);
2032
2033 return mail_identity;
2034 }
2035
2036 static gchar *
get_identity_uid_for_from(EShell * shell,ICalPropertyMethod method,ECalComponent * comp,ECalClient * cal_client,gchar ** identity_name,gchar ** identity_address)2037 get_identity_uid_for_from (EShell *shell,
2038 ICalPropertyMethod method,
2039 ECalComponent *comp,
2040 ECalClient *cal_client,
2041 gchar **identity_name,
2042 gchar **identity_address)
2043 {
2044 EClientCache *client_cache;
2045 ESourceRegistry *registry;
2046 ESource *source = NULL;
2047 gchar *identity_uid = NULL;
2048
2049 client_cache = e_shell_get_client_cache (shell);
2050 registry = e_client_cache_ref_registry (client_cache);
2051
2052 /* always use organizer's email when user is an organizer */
2053 if (itip_organizer_is_user (registry, comp, cal_client)) {
2054 ECalComponentOrganizer *organizer;
2055
2056 organizer = e_cal_component_get_organizer (comp);
2057 if (organizer && e_cal_component_organizer_get_value (organizer)) {
2058 source = find_enabled_identity (
2059 registry,
2060 itip_strip_mailto (e_cal_component_organizer_get_value (organizer)));
2061
2062 if (source) {
2063 if (identity_name)
2064 *identity_name = g_strdup (e_cal_component_organizer_get_cn (organizer));
2065 if (identity_address)
2066 *identity_address = g_strdup (itip_strip_mailto (e_cal_component_organizer_get_value (organizer)));
2067 }
2068 }
2069
2070 e_cal_component_organizer_free (organizer);
2071 }
2072
2073 if (source == NULL) {
2074 gchar *from = comp_from (method, comp, registry, identity_name);
2075
2076 if (from != NULL) {
2077 source = find_enabled_identity (registry, from);
2078 if (source) {
2079 if (identity_address)
2080 *identity_address = g_strdup (from);
2081 }
2082 }
2083
2084 g_free (from);
2085 }
2086
2087 if (source != NULL) {
2088 identity_uid = g_strdup (e_source_get_uid (source));
2089
2090 g_object_unref (source);
2091 }
2092
2093 g_object_unref (registry);
2094
2095 return identity_uid;
2096 }
2097
2098 static gint
master_first_cmp(gconstpointer ptr1,gconstpointer ptr2)2099 master_first_cmp (gconstpointer ptr1,
2100 gconstpointer ptr2)
2101 {
2102 ECalComponent *comp1 = (ECalComponent *) ptr1;
2103 ECalComponent *comp2 = (ECalComponent *) ptr2;
2104 ICalComponent *icomp1 = comp1 ? e_cal_component_get_icalcomponent (comp1) : NULL;
2105 ICalComponent *icomp2 = comp2 ? e_cal_component_get_icalcomponent (comp2) : NULL;
2106 gboolean has_rid1, has_rid2;
2107
2108 has_rid1 = (icomp1 && e_cal_util_component_has_property (icomp1, I_CAL_RECURRENCEID_PROPERTY)) ? 1 : 0;
2109 has_rid2 = (icomp2 && e_cal_util_component_has_property (icomp2, I_CAL_RECURRENCEID_PROPERTY)) ? 1 : 0;
2110
2111 if (has_rid1 == has_rid2)
2112 return g_strcmp0 (icomp1 ? i_cal_component_get_uid (icomp1) : NULL,
2113 icomp2 ? i_cal_component_get_uid (icomp2) : NULL);
2114
2115 if (has_rid1)
2116 return 1;
2117
2118 return -1;
2119 }
2120
2121 typedef struct {
2122 ESourceRegistry *registry;
2123 ICalPropertyMethod method;
2124 GSList *send_comps; /* ECalComponent * */
2125 ECalClient *cal_client;
2126 ICalComponent *zones;
2127 GSList *attachments_list;
2128 GSList *users;
2129 EItipSendComponentFlags flags;
2130
2131 gboolean completed;
2132 gboolean success;
2133
2134 GError *async_error;
2135 } ItipSendComponentData;
2136
2137 static void
itip_send_component_data_free(gpointer ptr)2138 itip_send_component_data_free (gpointer ptr)
2139 {
2140 ItipSendComponentData *isc = ptr;
2141
2142 if (isc) {
2143 g_clear_object (&isc->registry);
2144 g_slist_free_full (isc->send_comps, g_object_unref);
2145 g_clear_object (&isc->cal_client);
2146 g_clear_object (&isc->zones);
2147 g_clear_error (&isc->async_error);
2148 g_slist_free_full (isc->attachments_list, itip_cal_mime_attach_free); /* CamelMimePart */
2149 g_slist_free_full (isc->users, g_free);
2150 g_slice_free (ItipSendComponentData, isc);
2151 }
2152 }
2153
2154 static void
itip_send_component_begin(ItipSendComponentData * isc,GCancellable * cancellable,GError ** error)2155 itip_send_component_begin (ItipSendComponentData *isc,
2156 GCancellable *cancellable,
2157 GError **error)
2158 {
2159 GSList *link;
2160
2161 g_return_if_fail (isc != NULL);
2162
2163 isc->completed = FALSE;
2164
2165 if ((isc->flags & (E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_ACCEPTED |
2166 E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_DECLINED |
2167 E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_TENTATIVE)) != 0 &&
2168 isc->send_comps && !isc->send_comps->next) {
2169 ICalComponent *toplevel, *icomp;
2170 ICalParameterPartstat partstat;
2171 GSList *link;
2172 gchar *attendee;
2173
2174 if ((isc->flags & E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_ACCEPTED) != 0)
2175 partstat = I_CAL_PARTSTAT_ACCEPTED;
2176 else if ((isc->flags & E_ITIP_SEND_COMPONENT_FLAG_SAVE_RESPONSE_DECLINED) != 0)
2177 partstat = I_CAL_PARTSTAT_DECLINED;
2178 else
2179 partstat = I_CAL_PARTSTAT_TENTATIVE;
2180
2181 if ((isc->flags & E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT) != 0 && isc->send_comps) {
2182 /* Ensure we send the master object with its detached instances, not the instance only */
2183 GSList *ecalcomps = NULL;
2184 const gchar *uid;
2185
2186 uid = e_cal_component_get_uid (isc->send_comps->data);
2187
2188 if (e_cal_client_get_objects_for_uid_sync (isc->cal_client, uid, &ecalcomps, cancellable, NULL) && ecalcomps) {
2189 GSList *old_send_comps = isc->send_comps;
2190
2191 isc->send_comps = g_slist_sort (ecalcomps, master_first_cmp);
2192
2193 cal_comp_util_copy_new_attendees (isc->send_comps->data, old_send_comps->data);
2194
2195 g_slist_free_full (old_send_comps, g_object_unref);
2196 }
2197
2198 isc->flags &= ~E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT;
2199 }
2200
2201 attendee = itip_get_comp_attendee (isc->registry, isc->send_comps->data, isc->cal_client);
2202
2203 icomp = e_cal_component_get_icalcomponent (isc->send_comps->data);
2204 itip_utils_prepare_attendee_response (isc->registry, icomp, attendee, partstat);
2205 itip_utils_update_cdo_replytime (icomp);
2206
2207 toplevel = i_cal_component_new_vcalendar ();
2208 i_cal_component_set_method (toplevel, isc->method);
2209
2210 for (link = isc->send_comps; link; link = g_slist_next (link)) {
2211 ECalComponent *comp = link->data;
2212
2213 icomp = e_cal_component_get_icalcomponent (comp);
2214
2215 if (icomp)
2216 i_cal_component_take_component (toplevel, i_cal_component_clone (icomp));
2217 }
2218
2219 isc->success = e_cal_client_receive_objects_sync (isc->cal_client, toplevel, E_CAL_OPERATION_FLAG_NONE, cancellable, error);
2220
2221 g_object_unref (toplevel);
2222
2223 if (!isc->success) {
2224 isc->completed = TRUE;
2225 g_free (attendee);
2226 return;
2227 }
2228
2229 /* Include only the 'attendee' and use the first component as well in the reply */
2230 if (isc->method == I_CAL_METHOD_REPLY) {
2231 while (g_slist_next (isc->send_comps)) {
2232 ECalComponent *comp = isc->send_comps->next->data;
2233
2234 isc->send_comps = g_slist_remove (isc->send_comps, comp);
2235
2236 g_clear_object (&comp);
2237 }
2238
2239 itip_utils_remove_all_but_attendee (e_cal_component_get_icalcomponent (isc->send_comps->data), attendee);
2240 }
2241
2242 g_free (attendee);
2243 }
2244
2245 if (isc->method != I_CAL_METHOD_PUBLISH && e_cal_client_check_save_schedules (isc->cal_client)) {
2246 isc->success = TRUE;
2247 isc->completed = TRUE;
2248 return;
2249 }
2250
2251 if ((isc->flags & E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT) != 0 && isc->send_comps) {
2252 /* Ensure we send the master object with its detached instances, not the instance only */
2253 GSList *ecalcomps = NULL;
2254 const gchar *uid;
2255
2256 uid = e_cal_component_get_uid (isc->send_comps->data);
2257
2258 if (e_cal_client_get_objects_for_uid_sync (isc->cal_client, uid, &ecalcomps, cancellable, NULL) && ecalcomps) {
2259 GSList *old_send_comps = isc->send_comps;
2260
2261 isc->send_comps = g_slist_sort (ecalcomps, master_first_cmp);
2262
2263 cal_comp_util_copy_new_attendees (isc->send_comps->data, old_send_comps->data);
2264
2265 g_slist_free_full (old_send_comps, g_object_unref);
2266 }
2267 }
2268
2269 for (link = isc->send_comps; link; link = g_slist_next (link)) {
2270 ECalComponent *comp = link->data;
2271
2272 if (comp && e_cal_component_has_attachments (comp)) {
2273 ECalComponent *clone;
2274
2275 clone = e_cal_component_clone (comp);
2276 g_object_unref (comp);
2277
2278 link->data = clone;
2279
2280 if (!e_cal_util_inline_local_attachments_sync (e_cal_component_get_icalcomponent (clone), cancellable, error)) {
2281 isc->success = FALSE;
2282 return;
2283 }
2284 }
2285 }
2286
2287 /* Give the server a chance to manipulate the comp */
2288 if (isc->method != I_CAL_METHOD_PUBLISH) {
2289 d (printf ("itip-utils.c: itip_send_component_begin: calling comp_server_send_sync... \n"));
2290 if (!comp_server_send_sync (isc->method, isc->send_comps, isc->cal_client, isc->zones, &isc->users, cancellable, error)) {
2291 isc->success = FALSE;
2292 isc->completed = TRUE;
2293 return;
2294 }
2295 }
2296
2297 /* check whether backend could handle sending requests/updates */
2298 if (isc->method != I_CAL_METHOD_PUBLISH &&
2299 e_client_check_capability (E_CLIENT (isc->cal_client), E_CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) {
2300 isc->success = TRUE;
2301 isc->completed = TRUE;
2302 }
2303 }
2304
2305 typedef struct _CreateComposerData {
2306 gchar *identity_uid;
2307 gchar *identity_name;
2308 gchar *identity_address;
2309 EDestination **destinations;
2310 gchar *subject;
2311 gchar *ical_string;
2312 gchar *content_type;
2313 gchar *event_body_text;
2314 GSList *attachments_list;
2315 GSList *send_comps; /* ECalComponent * */
2316 gboolean show_only;
2317 } CreateComposerData;
2318
2319 static void
itip_send_component_composer_created_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)2320 itip_send_component_composer_created_cb (GObject *source_object,
2321 GAsyncResult *result,
2322 gpointer user_data)
2323 {
2324 CreateComposerData *ccd = user_data;
2325 EComposerHeaderTable *table;
2326 EMsgComposer *composer;
2327 GSettings *settings;
2328 gboolean use_24hour_format;
2329 GError *error = NULL;
2330
2331 g_return_if_fail (ccd != NULL);
2332
2333 composer = e_msg_composer_new_finish (result, &error);
2334 if (error) {
2335 g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
2336 g_clear_error (&error);
2337 goto free_ccd;
2338 }
2339
2340 settings = e_util_ref_settings ("org.gnome.evolution.calendar");
2341 use_24hour_format = g_settings_get_boolean (settings, "use-24hour-format");
2342 g_object_unref (settings);
2343
2344 table = e_msg_composer_get_header_table (composer);
2345
2346 if (ccd->identity_uid)
2347 e_composer_header_table_set_identity_uid (table, ccd->identity_uid, ccd->identity_name, ccd->identity_address);
2348
2349 e_composer_header_table_set_subject (table, ccd->subject);
2350 e_composer_header_table_set_destinations_to (table, ccd->destinations);
2351
2352 if (e_cal_component_get_vtype (ccd->send_comps->data) == E_CAL_COMPONENT_EVENT) {
2353 if (ccd->event_body_text)
2354 e_msg_composer_set_body_text (composer, ccd->event_body_text, TRUE);
2355 else
2356 e_msg_composer_set_body (composer, ccd->ical_string, ccd->content_type);
2357 } else {
2358 CamelMimePart *attachment;
2359 const gchar *filename;
2360 gchar *description;
2361 gchar *body;
2362
2363 filename = comp_filename (ccd->send_comps->data);
2364 description = comp_description (ccd->send_comps->data, use_24hour_format);
2365
2366 body = camel_text_to_html (description, CAMEL_MIME_FILTER_TOHTML_PRE, 0);
2367 e_msg_composer_set_body_text (composer, body, TRUE);
2368 g_free (body);
2369
2370 attachment = camel_mime_part_new ();
2371 camel_mime_part_set_content (
2372 attachment, ccd->ical_string,
2373 strlen (ccd->ical_string), ccd->content_type);
2374 if (filename != NULL && *filename != '\0')
2375 camel_mime_part_set_filename (attachment, filename);
2376 if (description != NULL && *description != '\0')
2377 camel_mime_part_set_description (attachment, description);
2378 camel_mime_part_set_disposition (attachment, "inline");
2379 e_msg_composer_attach (composer, attachment);
2380 g_object_unref (attachment);
2381
2382 g_free (description);
2383 }
2384
2385 append_cal_attachments (composer, ccd->attachments_list);
2386 ccd->attachments_list = NULL;
2387
2388 if (ccd->show_only)
2389 gtk_widget_show (GTK_WIDGET (composer));
2390 else
2391 e_msg_composer_send (composer);
2392
2393 free_ccd:
2394 e_destination_freev (ccd->destinations);
2395 g_slist_free_full (ccd->send_comps, g_object_unref);
2396 g_free (ccd->identity_uid);
2397 g_free (ccd->identity_name);
2398 g_free (ccd->identity_address);
2399 g_free (ccd->subject);
2400 g_free (ccd->ical_string);
2401 g_free (ccd->content_type);
2402 g_free (ccd->event_body_text);
2403 g_slice_free (CreateComposerData, ccd);
2404 }
2405
2406 static void
itip_send_component_complete(ItipSendComponentData * isc)2407 itip_send_component_complete (ItipSendComponentData *isc)
2408 {
2409 CreateComposerData *ccd;
2410 EDestination **destinations;
2411 EShell *shell;
2412 ICalComponent *top_level = NULL;
2413 ICalTimezone *default_zone;
2414 gchar *identity_uid, *identity_name = NULL, *identity_address = NULL;
2415
2416 g_return_if_fail (isc != NULL);
2417
2418 if (isc->completed)
2419 return;
2420
2421 isc->success = FALSE;
2422
2423 default_zone = calendar_config_get_icaltimezone ();
2424
2425 shell = e_shell_get_default ();
2426
2427 identity_uid = get_identity_uid_for_from (shell, isc->method, isc->send_comps->data, isc->cal_client, &identity_name, &identity_address);
2428
2429 /* Tidy up the comp */
2430 if (!comp_compliant (isc->registry, isc->method, isc->send_comps, TRUE, isc->cal_client, isc->zones, default_zone,
2431 (isc->flags & E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS) != 0)) {
2432 g_free (identity_uid);
2433 g_free (identity_name);
2434 g_free (identity_address);
2435 goto cleanup;
2436 }
2437
2438 /* Recipients */
2439 destinations = comp_to_list (
2440 isc->registry, isc->method, isc->send_comps->data, isc->users, FALSE,
2441 (isc->flags & E_ITIP_SEND_COMPONENT_FLAG_ONLY_NEW_ATTENDEES) != 0 ? g_object_get_data (
2442 G_OBJECT (isc->send_comps->data), "new-attendees") : NULL);
2443 if (isc->method != I_CAL_METHOD_PUBLISH) {
2444 if (destinations == NULL) {
2445 /* We sent them all via the server */
2446 isc->success = TRUE;
2447 g_free (identity_uid);
2448 g_free (identity_name);
2449 g_free (identity_address);
2450 goto cleanup;
2451 }
2452 }
2453
2454 top_level = comp_toplevel_with_zones (isc->method, isc->send_comps, isc->cal_client, isc->zones);
2455
2456 ccd = g_slice_new0 (CreateComposerData);
2457 ccd->identity_uid = identity_uid;
2458 ccd->identity_name = identity_name;
2459 ccd->identity_address = identity_address;
2460 ccd->destinations = destinations;
2461 ccd->subject = comp_subject (isc->registry, isc->method, isc->send_comps->data);
2462 ccd->ical_string = i_cal_component_as_ical_string (top_level);
2463 ccd->content_type = comp_content_type (isc->send_comps->data, isc->method);
2464 ccd->event_body_text = NULL;
2465 ccd->attachments_list = isc->attachments_list;
2466 ccd->send_comps = isc->send_comps;
2467 ccd->show_only = isc->method == I_CAL_METHOD_PUBLISH && !isc->users;
2468
2469 isc->attachments_list = NULL;
2470 isc->send_comps = NULL;
2471
2472 e_msg_composer_new (shell, itip_send_component_composer_created_cb, ccd);
2473
2474 isc->success = TRUE;
2475
2476 cleanup:
2477 g_clear_object (&top_level);
2478 }
2479
2480 static void
itip_send_component_complete_and_free(gpointer ptr)2481 itip_send_component_complete_and_free (gpointer ptr)
2482 {
2483 ItipSendComponentData *isc = ptr;
2484
2485 if (isc) {
2486 itip_send_component_complete (isc);
2487 itip_send_component_data_free (isc);
2488 }
2489 }
2490
2491 static void
itip_send_component_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)2492 itip_send_component_thread (EAlertSinkThreadJobData *job_data,
2493 gpointer user_data,
2494 GCancellable *cancellable,
2495 GError **error)
2496 {
2497 ItipSendComponentData *isc = user_data;
2498
2499 g_return_if_fail (isc != NULL);
2500
2501 itip_send_component_begin (isc, cancellable, error);
2502 }
2503
2504 void
itip_send_component_with_model(ECalModel * model,ICalPropertyMethod method,ECalComponent * send_comp,ECalClient * cal_client,ICalComponent * zones,GSList * attachments_list,GSList * users,EItipSendComponentFlags flags)2505 itip_send_component_with_model (ECalModel *model,
2506 ICalPropertyMethod method,
2507 ECalComponent *send_comp,
2508 ECalClient *cal_client,
2509 ICalComponent *zones,
2510 GSList *attachments_list,
2511 GSList *users,
2512 EItipSendComponentFlags flags)
2513 {
2514 ESourceRegistry *registry;
2515 ECalDataModel *data_model;
2516 ESource *source;
2517 const gchar *alert_ident = NULL;
2518 const gchar *description = NULL;
2519 gchar *display_name;
2520 GCancellable *cancellable;
2521 ItipSendComponentData *isc;
2522
2523 g_return_if_fail (E_IS_CAL_MODEL (model));
2524 g_return_if_fail (E_IS_CAL_CLIENT (cal_client));
2525
2526 switch (e_cal_client_get_source_type (cal_client)) {
2527 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
2528 description = _("Sending an event");
2529 alert_ident = "calendar:failed-send-event";
2530 break;
2531 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
2532 description = _("Sending a memo");
2533 alert_ident = "calendar:failed-send-memo";
2534 break;
2535 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
2536 description = _("Sending a task");
2537 alert_ident = "calendar:failed-send-task";
2538 break;
2539 default:
2540 g_warn_if_reached ();
2541 break;
2542 }
2543
2544 registry = e_cal_model_get_registry (model);
2545 data_model = e_cal_model_get_data_model (model);
2546 source = e_client_get_source (E_CLIENT (cal_client));
2547
2548 isc = g_slice_new0 (ItipSendComponentData);
2549 isc->registry = g_object_ref (registry);
2550 isc->method = method;
2551 isc->send_comps = g_slist_prepend (NULL, g_object_ref (send_comp));
2552 isc->cal_client = g_object_ref (cal_client);
2553 if (zones) {
2554 isc->zones = i_cal_component_clone (zones);
2555 }
2556 isc->attachments_list = attachments_list;
2557 if (users) {
2558 GSList *link;
2559
2560 isc->users = g_slist_copy (users);
2561 for (link = isc->users; link; link = g_slist_next (link)) {
2562 link->data = g_strdup (link->data);
2563 }
2564 }
2565 isc->flags = flags;
2566 isc->success = FALSE;
2567 isc->completed = FALSE;
2568
2569 display_name = e_util_get_source_full_name (registry, source);
2570 cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident,
2571 display_name, itip_send_component_thread,
2572 isc, itip_send_component_complete_and_free);
2573
2574 g_clear_object (&cancellable);
2575 g_free (display_name);
2576 }
2577
2578 gboolean
itip_send_comp_sync(ESourceRegistry * registry,ICalPropertyMethod method,ECalComponent * send_comp,ECalClient * cal_client,ICalComponent * zones,GSList * attachments_list,GSList * users,gboolean strip_alarms,gboolean only_new_attendees,GCancellable * cancellable,GError ** error)2579 itip_send_comp_sync (ESourceRegistry *registry,
2580 ICalPropertyMethod method,
2581 ECalComponent *send_comp,
2582 ECalClient *cal_client,
2583 ICalComponent *zones,
2584 GSList *attachments_list,
2585 GSList *users,
2586 gboolean strip_alarms,
2587 gboolean only_new_attendees,
2588 GCancellable *cancellable,
2589 GError **error)
2590 {
2591 ItipSendComponentData isc;
2592
2593 memset (&isc, 0, sizeof (ItipSendComponentData));
2594
2595 isc.registry = registry;
2596 isc.method = method;
2597 isc.send_comps = g_slist_prepend (NULL, g_object_ref (send_comp));
2598 isc.cal_client = cal_client;
2599 isc.zones = zones;
2600 isc.attachments_list = attachments_list;
2601 isc.users = users;
2602 isc.flags = (strip_alarms ? E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS : 0) |
2603 (only_new_attendees ? E_ITIP_SEND_COMPONENT_FLAG_ONLY_NEW_ATTENDEES : 0);
2604
2605 isc.completed = FALSE;
2606 isc.success = FALSE;
2607
2608 itip_send_component_begin (&isc, cancellable, error);
2609 itip_send_component_complete (&isc);
2610
2611 g_slist_free_full (isc.send_comps, g_object_unref);
2612 g_slist_free_full (isc.users, g_free);
2613
2614 return isc.success;
2615 }
2616
2617 static void
itip_send_component_task_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)2618 itip_send_component_task_thread (GTask *task,
2619 gpointer source_object,
2620 gpointer task_data,
2621 GCancellable *cancellable)
2622 {
2623 ItipSendComponentData *isc = task_data;
2624
2625 g_return_if_fail (isc != NULL);
2626
2627 itip_send_component_begin (isc, cancellable, &isc->async_error);
2628 }
2629
2630 void
itip_send_component(ESourceRegistry * registry,ICalPropertyMethod method,ECalComponent * send_comp,ECalClient * cal_client,ICalComponent * zones,GSList * attachments_list,GSList * users,EItipSendComponentFlags flags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2631 itip_send_component (ESourceRegistry *registry,
2632 ICalPropertyMethod method,
2633 ECalComponent *send_comp,
2634 ECalClient *cal_client,
2635 ICalComponent *zones,
2636 GSList *attachments_list,
2637 GSList *users,
2638 EItipSendComponentFlags flags,
2639 GCancellable *cancellable,
2640 GAsyncReadyCallback callback,
2641 gpointer user_data)
2642 {
2643 GTask *task;
2644 ItipSendComponentData *isc;
2645
2646 isc = g_slice_new0 (ItipSendComponentData);
2647 isc->registry = g_object_ref (registry);
2648 isc->method = method;
2649 isc->send_comps = g_slist_prepend (NULL, g_object_ref (send_comp));
2650 isc->cal_client = g_object_ref (cal_client);
2651 if (zones)
2652 isc->zones = i_cal_component_clone (zones);
2653 isc->attachments_list = attachments_list;
2654 if (users) {
2655 GSList *link;
2656
2657 isc->users = g_slist_copy (users);
2658 for (link = isc->users; link; link = g_slist_next (link)) {
2659 link->data = g_strdup (link->data);
2660 }
2661 }
2662 isc->flags = flags;
2663 isc->completed = FALSE;
2664 isc->success = FALSE;
2665
2666 task = g_task_new (NULL, cancellable, callback, user_data);
2667 g_task_set_task_data (task, isc, itip_send_component_data_free);
2668 g_task_set_source_tag (task, itip_send_component);
2669 g_task_run_in_thread (task, itip_send_component_task_thread);
2670 g_object_unref (task);
2671 }
2672
2673 gboolean
itip_send_component_finish(GAsyncResult * result,GError ** error)2674 itip_send_component_finish (GAsyncResult *result,
2675 GError **error)
2676 {
2677 ItipSendComponentData *isc;
2678
2679 isc = g_task_get_task_data (G_TASK (result));
2680
2681 g_return_val_if_fail (isc != NULL, FALSE);
2682 g_return_val_if_fail (g_async_result_is_tagged (result, itip_send_component), FALSE);
2683
2684 itip_send_component_complete (isc);
2685
2686 if (isc->async_error) {
2687 g_propagate_error (error, isc->async_error);
2688 isc->async_error = NULL;
2689 }
2690
2691 return isc->success;
2692 }
2693
2694 gboolean
reply_to_calendar_comp(ESourceRegistry * registry,ICalPropertyMethod method,ECalComponent * send_comp,ECalClient * cal_client,gboolean reply_all,ICalComponent * zones,GSList * attachments_list)2695 reply_to_calendar_comp (ESourceRegistry *registry,
2696 ICalPropertyMethod method,
2697 ECalComponent *send_comp,
2698 ECalClient *cal_client,
2699 gboolean reply_all,
2700 ICalComponent *zones,
2701 GSList *attachments_list)
2702 {
2703 EShell *shell;
2704 ICalComponent *top_level = NULL;
2705 ICalTimezone *default_zone;
2706 gboolean retval = FALSE;
2707 gchar *identity_uid, *identity_name = NULL, *identity_address = NULL;
2708 GSList *ecomps;
2709 CreateComposerData *ccd;
2710
2711 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
2712
2713 /* FIXME Pass this in. */
2714 shell = e_shell_get_default ();
2715
2716 default_zone = calendar_config_get_icaltimezone ();
2717
2718 ecomps = g_slist_prepend (NULL, send_comp);
2719
2720 identity_uid = get_identity_uid_for_from (shell, method, send_comp, cal_client, &identity_name, &identity_address);
2721
2722 /* Tidy up the comp */
2723 if (!comp_compliant (registry, method, ecomps, FALSE, cal_client, zones, default_zone, TRUE)) {
2724 g_free (identity_uid);
2725 g_free (identity_name);
2726 g_free (identity_address);
2727 goto cleanup;
2728 }
2729
2730 top_level = comp_toplevel_with_zones (method, ecomps, cal_client, zones);
2731
2732 ccd = g_slice_new0 (CreateComposerData);
2733 ccd->identity_uid = identity_uid;
2734 ccd->identity_name = identity_name;
2735 ccd->identity_address = identity_address;
2736 ccd->destinations = comp_to_list (registry, method, ecomps->data, NULL, reply_all, NULL);
2737 ccd->subject = comp_subject (registry, method, ecomps->data);
2738 ccd->ical_string = i_cal_component_as_ical_string (top_level);
2739 ccd->send_comps = ecomps;
2740 ccd->show_only = TRUE;
2741
2742 if (e_cal_component_get_vtype (ecomps->data) == E_CAL_COMPONENT_EVENT) {
2743 ECalComponent *comp = ecomps->data;
2744 GString *body;
2745 gchar *orig_from = NULL;
2746 gchar *description = NULL;
2747 gchar *subject = NULL;
2748 gchar *location;
2749 gchar *time = NULL;
2750 gchar *html_description = NULL;
2751 GSList *text_list;
2752 ECalComponentOrganizer *organizer;
2753 ECalComponentText *text;
2754 ECalComponentDateTime *dtstart;
2755 ICalTimezone *start_zone = NULL;
2756 time_t start;
2757
2758 text_list = e_cal_component_get_descriptions (comp);
2759
2760 if (text_list) {
2761 text = text_list->data;
2762 if (text && e_cal_component_text_get_value (text))
2763 description = g_strdup (e_cal_component_text_get_value (text));
2764 }
2765
2766 g_slist_free_full (text_list, e_cal_component_text_free);
2767
2768 text = e_cal_component_get_summary (comp);
2769 if (text && e_cal_component_text_get_value (text))
2770 subject = g_strdup (e_cal_component_text_get_value (text));
2771 e_cal_component_text_free (text);
2772
2773 organizer = e_cal_component_get_organizer (comp);
2774 if (organizer && e_cal_component_organizer_get_value (organizer))
2775 orig_from = g_strdup (itip_strip_mailto (e_cal_component_organizer_get_value (organizer)));
2776 e_cal_component_organizer_free (organizer);
2777
2778 location = e_cal_component_get_location (comp);
2779 if (!location) {
2780 /* Translator: This is used as a placeholder when an event doesn't have set a location */
2781 location = g_strdup (C_("Location", "Unspecified"));
2782 }
2783
2784 dtstart = e_cal_component_get_dtstart (comp);
2785 if (dtstart && e_cal_component_datetime_get_value (dtstart)) {
2786 ICalTime *itt;
2787
2788 itt = e_cal_component_datetime_get_value (dtstart);
2789
2790 start_zone = e_cal_component_datetime_get_tzid (dtstart) ?
2791 i_cal_timezone_get_builtin_timezone_from_tzid (e_cal_component_datetime_get_tzid (dtstart)) : NULL;
2792 if (!start_zone && e_cal_component_datetime_get_tzid (dtstart)) {
2793 GError *error = NULL;
2794
2795 if (!e_cal_client_get_timezone_sync (
2796 cal_client, e_cal_component_datetime_get_tzid (dtstart),
2797 &start_zone, NULL, &error))
2798 start_zone = NULL;
2799
2800 if (error != NULL) {
2801 g_warning (
2802 "%s: Couldn't get timezone '%s' from server: %s",
2803 G_STRFUNC,
2804 e_cal_component_datetime_get_tzid (dtstart) ?
2805 e_cal_component_datetime_get_tzid (dtstart) : "",
2806 error->message);
2807 g_error_free (error);
2808 }
2809 }
2810
2811 if (!start_zone || i_cal_time_is_date (itt))
2812 start_zone = default_zone;
2813
2814 start = i_cal_time_as_timet_with_zone (itt, start_zone);
2815 time = g_strdup (ctime (&start));
2816 }
2817 e_cal_component_datetime_free (dtstart);
2818
2819 body = g_string_new (
2820 "<br><br><hr><br><b>"
2821 "______ Original Appointment ______ "
2822 "</b><br><br><table>");
2823
2824 if (orig_from && *orig_from)
2825 g_string_append_printf (
2826 body,
2827 "<tr><td><b>From</b></td>"
2828 "<td>:</td><td>%s</td></tr>", orig_from);
2829 g_free (orig_from);
2830
2831 if (subject)
2832 g_string_append_printf (
2833 body,
2834 "<tr><td><b>Subject</b></td>"
2835 "<td>:</td><td>%s</td></tr>", subject);
2836 g_free (subject);
2837
2838 g_string_append_printf (
2839 body,
2840 "<tr><td><b>Location</b></td>"
2841 "<td>:</td><td>%s</td></tr>", location);
2842 g_free (location);
2843
2844 if (time)
2845 g_string_append_printf (
2846 body,
2847 "<tr><td><b>Time</b></td>"
2848 "<td>:</td><td>%s</td></tr>", time);
2849 g_free (time);
2850
2851 g_string_append_printf (body, "</table><br>");
2852
2853 html_description = html_new_lines_for (description ? description : "");
2854 g_string_append (body, html_description);
2855 g_free (html_description);
2856 g_free (description);
2857
2858 ccd->event_body_text = g_string_free (body, FALSE);
2859 }
2860
2861 e_msg_composer_new (shell, itip_send_component_composer_created_cb, ccd);
2862
2863 retval = TRUE;
2864
2865 cleanup:
2866
2867 g_clear_object (&top_level);
2868
2869 return retval;
2870 }
2871
2872 gboolean
itip_publish_begin(ECalComponent * pub_comp,ECalClient * cal_client,gboolean cloned,ECalComponent ** clone)2873 itip_publish_begin (ECalComponent *pub_comp,
2874 ECalClient *cal_client,
2875 gboolean cloned,
2876 ECalComponent **clone)
2877 {
2878 ICalComponent *icomp = NULL, *icomp_clone = NULL;
2879 ICalProperty *prop;
2880
2881 if (e_cal_component_get_vtype (pub_comp) == E_CAL_COMPONENT_FREEBUSY) {
2882
2883 if (!cloned) {
2884 *clone = e_cal_component_clone (pub_comp);
2885 } else {
2886 icomp = e_cal_component_get_icalcomponent (pub_comp);
2887 icomp_clone = e_cal_component_get_icalcomponent (*clone);
2888 for (prop = i_cal_component_get_first_property (icomp, I_CAL_FREEBUSY_PROPERTY);
2889 prop;
2890 g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, I_CAL_FREEBUSY_PROPERTY)) {
2891 ICalProperty *p;
2892
2893 p = i_cal_property_clone (prop);
2894 i_cal_component_take_property (icomp_clone, p);
2895 }
2896 }
2897 }
2898
2899 return TRUE;
2900 }
2901
2902 static gboolean
check_time(ICalTime * tmval,gboolean can_null_time)2903 check_time (ICalTime *tmval,
2904 gboolean can_null_time)
2905 {
2906 gboolean valid;
2907
2908 if (!tmval || i_cal_time_is_null_time (tmval)) {
2909 g_clear_object (&tmval);
2910 return can_null_time;
2911 }
2912
2913 valid = i_cal_time_is_valid_time (tmval) &&
2914 i_cal_time_get_month (tmval) >= 1 && i_cal_time_get_month (tmval) <= 12 &&
2915 i_cal_time_get_day (tmval) >= 1 && i_cal_time_get_day (tmval) <= 31 &&
2916 i_cal_time_get_hour (tmval) >= 0 && i_cal_time_get_hour (tmval) < 24 &&
2917 i_cal_time_get_minute (tmval) >= 0 && i_cal_time_get_minute (tmval) < 60 &&
2918 i_cal_time_get_second (tmval) >= 0 && i_cal_time_get_second (tmval) < 60;
2919
2920 g_clear_object (&tmval);
2921
2922 return valid;
2923 }
2924
2925 /* Returns whether the passed-in ICalComponent is valid or not.
2926 * It does some sanity checks on values too. */
2927 gboolean
itip_is_component_valid(ICalComponent * icomp)2928 itip_is_component_valid (ICalComponent *icomp)
2929 {
2930 if (!icomp || !i_cal_component_is_valid (icomp))
2931 return FALSE;
2932
2933 switch (i_cal_component_isa (icomp)) {
2934 case I_CAL_VEVENT_COMPONENT:
2935 return check_time (i_cal_component_get_dtstart (icomp), FALSE) &&
2936 check_time (i_cal_component_get_dtend (icomp), TRUE);
2937 case I_CAL_VTODO_COMPONENT:
2938 return check_time (i_cal_component_get_dtstart (icomp), TRUE) &&
2939 check_time (i_cal_component_get_due (icomp), TRUE);
2940 case I_CAL_VJOURNAL_COMPONENT:
2941 return check_time (i_cal_component_get_dtstart (icomp), TRUE) &&
2942 check_time (i_cal_component_get_dtend (icomp), TRUE);
2943 default:
2944 break;
2945 }
2946
2947 return TRUE;
2948 }
2949
2950 gboolean
itip_component_has_recipients(ECalComponent * comp)2951 itip_component_has_recipients (ECalComponent *comp)
2952 {
2953 GSList *attendees, *link;
2954 ECalComponentAttendee *attendee;
2955 ECalComponentOrganizer *organizer;
2956 gboolean res = FALSE;
2957
2958 g_return_val_if_fail (comp != NULL, FALSE);
2959
2960 organizer = e_cal_component_get_organizer (comp);
2961 attendees = e_cal_component_get_attendees (comp);
2962
2963 if (!attendees) {
2964 if (organizer && e_cal_component_organizer_get_value (organizer) &&
2965 e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_JOURNAL) {
2966 /* memos store recipients in an extra property */
2967 ICalComponent *icomp;
2968 ICalProperty *prop;
2969
2970 icomp = e_cal_component_get_icalcomponent (comp);
2971
2972 for (prop = i_cal_component_get_first_property (icomp, I_CAL_X_PROPERTY);
2973 prop;
2974 g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, I_CAL_X_PROPERTY)) {
2975 const gchar *x_name;
2976
2977 x_name = i_cal_property_get_x_name (prop);
2978
2979 if (g_str_equal (x_name, "X-EVOLUTION-RECIPIENTS")) {
2980 const gchar *str_recipients = i_cal_property_get_x (prop);
2981
2982 res = str_recipients && g_ascii_strcasecmp (e_cal_component_organizer_get_value (organizer), str_recipients) != 0;
2983 g_object_unref (prop);
2984 break;
2985 }
2986 }
2987 }
2988
2989 e_cal_component_organizer_free (organizer);
2990
2991 return res;
2992 }
2993
2994 if (g_slist_length (attendees) > 1 || !e_cal_component_has_organizer (comp)) {
2995 g_slist_free_full (attendees, e_cal_component_attendee_free);
2996 e_cal_component_organizer_free (organizer);
2997 return TRUE;
2998 }
2999
3000 for (link = attendees; link && !res; link = g_slist_next (link)) {
3001 attendee = link->data;
3002
3003 res = organizer && e_cal_component_organizer_get_value (organizer) &&
3004 attendee && e_cal_component_attendee_get_value (attendee) &&
3005 g_ascii_strcasecmp (e_cal_component_organizer_get_value (organizer), e_cal_component_attendee_get_value (attendee)) != 0;
3006 }
3007
3008 g_slist_free_full (attendees, e_cal_component_attendee_free);
3009 e_cal_component_organizer_free (organizer);
3010
3011 return res;
3012 }
3013
3014 void
itip_utils_update_cdo_replytime(ICalComponent * icomp)3015 itip_utils_update_cdo_replytime (ICalComponent *icomp)
3016 {
3017 ICalTime *stamp;
3018 gchar *str;
3019
3020 g_return_if_fail (I_CAL_IS_COMPONENT (icomp));
3021
3022 while (e_cal_util_component_remove_x_property (icomp, "X-MICROSOFT-CDO-REPLYTIME")) {
3023 /* Delete all existing X-MICROSOFT-CDO-REPLYTIME properties first */
3024 }
3025
3026 /* Set X-MICROSOFT-CDO-REPLYTIME to record the time at which
3027 * the user accepted/declined the request. (Outlook ignores
3028 * SEQUENCE in REPLY reponses and instead requires that each
3029 * updated response have a later REPLYTIME than the previous
3030 * one.) This also ends up getting saved in our own copy of
3031 * the meeting, though there's currently no way to see that
3032 * information (unless it's being saved to an Exchange folder
3033 * and you then look at it in Outlook).
3034 */
3035 stamp = i_cal_time_new_current_with_zone (i_cal_timezone_get_utc_timezone ());
3036 str = i_cal_time_as_ical_string (stamp);
3037 e_cal_util_component_set_x_property (icomp, "X-MICROSOFT-CDO-REPLYTIME", str);
3038 g_clear_object (&stamp);
3039 g_free (str);
3040 }
3041
3042 ICalProperty *
itip_utils_find_attendee_property(ICalComponent * icomp,const gchar * address)3043 itip_utils_find_attendee_property (ICalComponent *icomp,
3044 const gchar *address)
3045 {
3046 ICalProperty *prop;
3047
3048 if (!address || !*address)
3049 return NULL;
3050
3051 for (prop = i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY);
3052 prop;
3053 g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, I_CAL_ATTENDEE_PROPERTY)) {
3054 gchar *attendee;
3055 gchar *text;
3056
3057 attendee = i_cal_property_get_value_as_string (prop);
3058
3059 if (!attendee)
3060 continue;
3061
3062 text = g_strdup (itip_strip_mailto (attendee));
3063 text = g_strstrip (text);
3064 if (text && !g_ascii_strcasecmp (address, text)) {
3065 g_free (text);
3066 g_free (attendee);
3067 break;
3068 }
3069 g_free (text);
3070 g_free (attendee);
3071 }
3072
3073 return prop;
3074 }
3075
3076 void
itip_utils_prepare_attendee_response(ESourceRegistry * registry,ICalComponent * icomp,const gchar * address,ICalParameterPartstat partstat)3077 itip_utils_prepare_attendee_response (ESourceRegistry *registry,
3078 ICalComponent *icomp,
3079 const gchar *address,
3080 ICalParameterPartstat partstat)
3081 {
3082 ICalProperty *prop;
3083
3084 prop = itip_utils_find_attendee_property (icomp, address);
3085 if (prop) {
3086 ICalParameter *param;
3087
3088 i_cal_property_remove_parameter_by_kind (prop, I_CAL_PARTSTAT_PARAMETER);
3089 param = i_cal_parameter_new_partstat (partstat);
3090 if (param)
3091 i_cal_property_take_parameter (prop, param);
3092
3093 g_object_unref (prop);
3094 } else {
3095 ICalParameter *param;
3096
3097 if (address && *address) {
3098 gchar *mailto_uri;
3099
3100 mailto_uri = g_strconcat ("mailto:", itip_strip_mailto (address), NULL);
3101 prop = i_cal_property_new_attendee (mailto_uri);
3102 g_free (mailto_uri);
3103
3104 param = i_cal_parameter_new_role (I_CAL_ROLE_OPTPARTICIPANT);
3105 i_cal_property_take_parameter (prop, param);
3106
3107 param = i_cal_parameter_new_partstat (partstat);
3108 if (param)
3109 i_cal_property_take_parameter (prop, param);
3110
3111 i_cal_component_take_property (icomp, prop);
3112 } else {
3113 gchar *default_name = NULL;
3114 gchar *default_address = NULL;
3115 gchar *mailto_uri;
3116
3117 itip_get_default_name_and_address (
3118 registry, &default_name, &default_address);
3119
3120 mailto_uri = g_strconcat ("mailto:", itip_strip_mailto (default_address), NULL);
3121 prop = i_cal_property_new_attendee (mailto_uri);
3122 g_free (mailto_uri);
3123
3124 if (default_name && *default_name && g_strcmp0 (default_name, default_address) != 0) {
3125 param = i_cal_parameter_new_cn (default_name);
3126 i_cal_property_take_parameter (prop, param);
3127 }
3128
3129 param = i_cal_parameter_new_role (I_CAL_ROLE_REQPARTICIPANT);
3130 i_cal_property_take_parameter (prop, param);
3131
3132 param = i_cal_parameter_new_partstat (partstat);
3133 if (param)
3134 i_cal_property_take_parameter (prop, param);
3135
3136 i_cal_component_take_property (icomp, prop);
3137
3138 g_free (default_name);
3139 g_free (default_address);
3140 }
3141 }
3142 }
3143
3144 gboolean
itip_utils_remove_all_but_attendee(ICalComponent * icomp,const gchar * attendee)3145 itip_utils_remove_all_but_attendee (ICalComponent *icomp,
3146 const gchar *attendee)
3147 {
3148 ICalProperty *prop;
3149 GSList *remove = NULL, *link;
3150 gboolean found = FALSE;
3151
3152 g_return_val_if_fail (I_CAL_IS_COMPONENT (icomp), FALSE);
3153 g_return_val_if_fail (attendee != NULL, FALSE);
3154
3155 for (prop = i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY);
3156 prop;
3157 prop = i_cal_component_get_next_property (icomp, I_CAL_ATTENDEE_PROPERTY)) {
3158 const gchar *address = i_cal_property_get_attendee (prop);
3159
3160 if (found || g_ascii_strcasecmp (itip_strip_mailto (address), attendee) != 0) {
3161 remove = g_slist_prepend (remove, prop);
3162 } else {
3163 found = TRUE;
3164 g_object_unref (prop);
3165 }
3166 }
3167
3168 for (link = remove; link; link = g_slist_next (link)) {
3169 prop = link->data;
3170
3171 i_cal_component_remove_property (icomp, prop);
3172 }
3173
3174 g_slist_free_full (remove, g_object_unref);
3175
3176 return found;
3177 }
3178