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