Bug 793125 - Crash due to popup menus left attached too long
[evolution.git] / src / calendar / gui / itip-utils.c
blob64a5436b9fc7e5118ed233dbef1b6f282eaf3d90
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.
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.
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/>.
15 * Authors:
16 * JP Rosevear <jpr@ximian.com>
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 #include "evolution-config.h"
24 #include <time.h>
25 #include <glib/gi18n-lib.h>
26 #include <libical/ical.h>
27 #include <libsoup/soup.h>
29 #include <composer/e-msg-composer.h>
30 #include <libedataserver/libedataserver.h>
32 #include "calendar-config.h"
33 #include "comp-util.h"
35 #include "itip-utils.h"
37 #define d(x)
39 static const gchar *itip_methods[] = {
40 "PUBLISH",
41 "REQUEST",
42 "REPLY",
43 "ADD",
44 "CANCEL",
45 "RERESH",
46 "COUNTER",
47 "DECLINECOUNTER"
50 static icalproperty_method itip_methods_enum[] = {
51 ICAL_METHOD_PUBLISH,
52 ICAL_METHOD_REQUEST,
53 ICAL_METHOD_REPLY,
54 ICAL_METHOD_ADD,
55 ICAL_METHOD_CANCEL,
56 ICAL_METHOD_REFRESH,
57 ICAL_METHOD_COUNTER,
58 ICAL_METHOD_DECLINECOUNTER,
61 /**
62 * itip_get_default_name_and_address:
63 * @registry: an #ESourceRegistry
64 * @name: return location for the user's real name, or %NULL
65 * @address: return location for the user's email address, or %NULL
67 * Returns the real name and email address of the default mail identity,
68 * if available. If no default mail identity is available, @name and
69 * @address are set to %NULL and the function returns %FALSE.
71 * Returns: %TRUE if @name and/or @address were set
72 **/
73 gboolean
74 itip_get_default_name_and_address (ESourceRegistry *registry,
75 gchar **name,
76 gchar **address)
78 ESource *source;
79 ESourceExtension *extension;
80 const gchar *extension_name;
81 gboolean success;
83 source = e_source_registry_ref_default_mail_identity (registry);
85 if (source != NULL) {
86 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
87 extension = e_source_get_extension (source, extension_name);
89 if (name != NULL)
90 *name = e_source_mail_identity_dup_name (
91 E_SOURCE_MAIL_IDENTITY (extension));
93 if (address != NULL)
94 *address = e_source_mail_identity_dup_address (
95 E_SOURCE_MAIL_IDENTITY (extension));
97 g_object_unref (source);
99 success = TRUE;
101 } else {
102 if (name != NULL)
103 *name = NULL;
105 if (address != NULL)
106 *address = NULL;
108 success = FALSE;
111 return success;
114 static gint
115 sort_identities_by_email_cb (gconstpointer ptr1,
116 gconstpointer ptr2)
118 const gchar **pv1 = (const gchar **) ptr1, **pv2 = (const gchar **) ptr2;
119 const gchar *addr1, *addr2;
120 gint res;
122 if (!pv1 || !*pv1 || !pv2 || !*pv2) {
123 if (pv1 && *pv1)
124 return -1;
125 if (pv2 && *pv2)
126 return 1;
127 return 0;
130 addr1 = strchr (*pv1, '<');
131 addr2 = strchr (*pv2, '<');
133 if (addr1)
134 addr1++;
135 else
136 addr1 = *pv1;
137 if (addr2)
138 addr2++;
139 else
140 addr2 = *pv2;
142 res = g_ascii_strcasecmp (addr1, addr2);
144 if (!res && addr1 != *pv1 && addr2 != *pv2)
145 res = g_ascii_strcasecmp (*pv1, *pv2);
147 return res;
151 * itip_get_user_identities:
152 * @registry: an #ESourceRegistry
154 * Returns a %NULL-terminated array of name + address strings based on
155 * registered mail identities. Free the returned array with g_strfreev().
157 * Returns: an %NULL-terminated array of mail identity strings
159 gchar **
160 itip_get_user_identities (ESourceRegistry *registry)
162 GList *list, *link;
163 const gchar *extension_name;
164 GPtrArray *identities;
166 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
168 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
170 list = e_source_registry_list_enabled (registry, extension_name);
172 identities = g_ptr_array_sized_new (g_list_length (list) + 1);
174 for (link = list; link != NULL; link = g_list_next (link)) {
175 ESource *source = E_SOURCE (link->data);
176 ESourceMailIdentity *extension;
177 const gchar *name, *address;
178 gchar *aliases;
180 if (!e_util_identity_can_send (registry, source))
181 continue;
183 extension = e_source_get_extension (source, extension_name);
185 name = e_source_mail_identity_get_name (extension);
186 address = e_source_mail_identity_get_address (extension);
188 if (address)
189 g_ptr_array_add (identities, camel_internet_address_format_address (name, address));
191 aliases = e_source_mail_identity_dup_aliases (extension);
192 if (aliases && *aliases) {
193 CamelInternetAddress *inet_address;
194 gint ii, len;
196 inet_address = camel_internet_address_new ();
197 len = camel_address_decode (CAMEL_ADDRESS (inet_address), aliases);
199 for (ii = 0; ii < len; ii++) {
200 const gchar *alias_name = NULL, *alias_address = NULL;
202 if (camel_internet_address_get (inet_address, ii, &alias_name, &alias_address) &&
203 alias_address && *alias_address) {
204 if (!alias_name || !*alias_name)
205 alias_name = name;
207 g_ptr_array_add (identities, camel_internet_address_format_address (alias_name, alias_address));
212 g_free (aliases);
215 g_list_free_full (list, (GDestroyNotify) g_object_unref);
217 g_ptr_array_sort (identities, sort_identities_by_email_cb);
219 /* NULL-terminated array */
220 g_ptr_array_add (identities, NULL);
222 return (gchar **) g_ptr_array_free (identities, FALSE);
226 * itip_get_fallback_identity:
227 * @registry: an #ESourceRegistry
229 * Returns a name + address string taken from the default mail identity,
230 * but only if the corresponding account is enabled. If the account is
231 * disabled, the function returns %NULL. This is meant to be used as a
232 * fallback identity for organizers. Free the returned string with
233 * g_free().
235 * Returns: a fallback mail identity, or %NULL
237 gchar *
238 itip_get_fallback_identity (ESourceRegistry *registry)
240 ESource *source;
241 ESourceMailIdentity *mail_identity;
242 const gchar *extension_name;
243 const gchar *address;
244 const gchar *name;
245 gchar *identity = NULL;
247 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
249 source = e_source_registry_ref_default_mail_identity (registry);
251 if (source == NULL)
252 return NULL;
254 if (!e_source_registry_check_enabled (registry, source)) {
255 g_object_unref (source);
256 return NULL;
259 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
260 mail_identity = e_source_get_extension (source, extension_name);
262 name = e_source_mail_identity_get_name (mail_identity);
263 address = e_source_mail_identity_get_address (mail_identity);
265 if (address)
266 identity = camel_internet_address_format_address (name, address);
268 g_object_unref (source);
270 return identity;
274 * itip_address_is_user:
275 * @registry: an #ESourceRegistry
276 * @address: an email address
278 * Looks for a registered mail identity with a matching email address.
280 * Returns: %TRUE if a match was found, %FALSE if not
282 gboolean
283 itip_address_is_user (ESourceRegistry *registry,
284 const gchar *address)
286 GList *list, *iter;
287 const gchar *extension_name;
288 gboolean match = FALSE;
290 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
291 g_return_val_if_fail (address != NULL, FALSE);
293 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
295 list = e_source_registry_list_sources (registry, extension_name);
297 for (iter = list; iter && !match; iter = g_list_next (iter)) {
298 ESource *source = E_SOURCE (iter->data);
299 ESourceMailIdentity *extension;
300 GHashTable *aliases;
301 const gchar *id_address;
303 extension = e_source_get_extension (source, extension_name);
304 id_address = e_source_mail_identity_get_address (extension);
306 if (id_address && g_ascii_strcasecmp (address, id_address) == 0) {
307 match = TRUE;
308 break;
311 aliases = e_source_mail_identity_get_aliases_as_hash_table (extension);
312 if (aliases) {
313 match = g_hash_table_contains (aliases, address);
314 g_hash_table_destroy (aliases);
318 g_list_free_full (list, (GDestroyNotify) g_object_unref);
320 return match;
323 gboolean
324 itip_organizer_is_user (ESourceRegistry *registry,
325 ECalComponent *comp,
326 ECalClient *cal_client)
328 return itip_organizer_is_user_ex (registry, comp, cal_client, FALSE);
331 gboolean
332 itip_organizer_is_user_ex (ESourceRegistry *registry,
333 ECalComponent *comp,
334 ECalClient *cal_client,
335 gboolean skip_cap_test)
337 ECalComponentOrganizer organizer;
338 const gchar *strip;
339 gboolean user_org = FALSE;
341 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
343 if (!e_cal_component_has_organizer (comp) ||
344 (!skip_cap_test && e_client_check_capability (
345 E_CLIENT (cal_client), CAL_STATIC_CAPABILITY_NO_ORGANIZER)))
346 return FALSE;
348 e_cal_component_get_organizer (comp, &organizer);
349 if (organizer.value != NULL) {
350 gchar *email = NULL;
352 strip = itip_strip_mailto (organizer.value);
354 if (e_client_get_backend_property_sync (E_CLIENT (cal_client),
355 CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS,
356 &email, NULL, NULL) &&
357 email && g_ascii_strcasecmp (email, strip) == 0) {
358 g_free (email);
360 return TRUE;
363 g_free (email);
365 if (e_client_check_capability (E_CLIENT (cal_client), CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS)) {
366 return FALSE;
369 user_org = itip_address_is_user (registry, strip);
372 return user_org;
375 gboolean
376 itip_sentby_is_user (ESourceRegistry *registry,
377 ECalComponent *comp,
378 ECalClient *cal_client)
380 ECalComponentOrganizer organizer;
381 const gchar *strip;
382 gboolean user_sentby = FALSE;
384 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
386 if (!e_cal_component_has_organizer (comp) ||
387 e_client_check_capability (
388 E_CLIENT (cal_client), CAL_STATIC_CAPABILITY_NO_ORGANIZER))
389 return FALSE;
391 e_cal_component_get_organizer (comp, &organizer);
392 if (organizer.sentby != NULL) {
393 strip = itip_strip_mailto (organizer.sentby);
394 user_sentby = itip_address_is_user (registry, strip);
397 return user_sentby;
400 gboolean
401 itip_has_any_attendees (ECalComponent *comp)
403 ECalComponentOrganizer organizer;
404 ECalComponentAttendee *attendee;
405 GSList *attendees = NULL;
406 gboolean res;
408 g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
410 if (!e_cal_component_has_attendees (comp))
411 return FALSE;
413 e_cal_component_get_attendee_list (comp, &attendees);
415 /* No attendee */
416 if (!attendees)
417 return FALSE;
419 /* More than one attendee */
420 if (attendees->next) {
421 e_cal_component_free_attendee_list (attendees);
422 return TRUE;
425 /* Exactly one attendee, check if it's not the organizer */
426 attendee = attendees->data;
428 g_return_val_if_fail (attendee != NULL, FALSE);
430 if (!e_cal_component_has_organizer (comp)) {
431 e_cal_component_free_attendee_list (attendees);
432 return FALSE;
435 e_cal_component_get_organizer (comp, &organizer);
437 res = attendee->value && (!organizer.value ||
438 g_ascii_strcasecmp (itip_strip_mailto (attendee->value), itip_strip_mailto (organizer.value)) != 0);
440 e_cal_component_free_attendee_list (attendees);
442 return res;
445 static ECalComponentAttendee *
446 get_attendee (GSList *attendees,
447 const gchar *address,
448 GHashTable *aliases)
450 GSList *l;
452 if (!address)
453 return NULL;
455 for (l = attendees; l; l = l->next) {
456 ECalComponentAttendee *attendee = l->data;
457 const gchar *nomailto;
459 nomailto = itip_strip_mailto (attendee->value);
460 if (!nomailto || !*nomailto)
461 continue;
463 if ((address && g_ascii_strcasecmp (nomailto, address) == 0) ||
464 (aliases && g_hash_table_contains (aliases, nomailto))) {
465 return attendee;
469 return NULL;
472 static ECalComponentAttendee *
473 get_attendee_if_attendee_sentby_is_user (GSList *attendees,
474 const gchar *address,
475 GHashTable *aliases)
477 GSList *l;
479 for (l = attendees; l; l = l->next) {
480 ECalComponentAttendee *attendee = l->data;
481 const gchar *nomailto;
483 nomailto = itip_strip_mailto (attendee->sentby);
484 if (!nomailto || !*nomailto)
485 continue;
487 if ((address && g_ascii_strcasecmp (nomailto, address) == 0) ||
488 (aliases && g_hash_table_contains (aliases, nomailto))) {
489 return attendee;
493 return NULL;
496 static gchar *
497 html_new_lines_for (const gchar *string)
499 gchar **lines;
500 gchar *joined;
502 lines = g_strsplit_set (string, "\n", -1);
503 joined = g_strjoinv ("<br>", lines);
504 g_strfreev (lines);
506 return joined;
509 gchar *
510 itip_get_comp_attendee (ESourceRegistry *registry,
511 ECalComponent *comp,
512 ECalClient *cal_client)
514 ESource *source;
515 GSList *attendees;
516 ECalComponentAttendee *attendee = NULL;
517 GList *list, *link;
518 const gchar *extension_name;
519 gchar *address = NULL;
521 e_cal_component_get_attendee_list (comp, &attendees);
523 if (cal_client)
524 e_client_get_backend_property_sync (
525 E_CLIENT (cal_client),
526 CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS,
527 &address, NULL, NULL);
529 if (address != NULL && *address != '\0') {
530 attendee = get_attendee (attendees, address, NULL);
532 if (attendee) {
533 gchar *user_email;
535 user_email = g_strdup (
536 itip_strip_mailto (attendee->value));
537 e_cal_component_free_attendee_list (attendees);
538 g_free (address);
540 return user_email;
543 attendee = get_attendee_if_attendee_sentby_is_user (attendees, address, NULL);
545 if (attendee != NULL) {
546 gchar *user_email;
548 user_email = g_strdup (
549 itip_strip_mailto (attendee->sentby));
550 e_cal_component_free_attendee_list (attendees);
551 g_free (address);
553 return user_email;
557 g_free (address);
558 address = NULL;
560 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
561 list = e_source_registry_list_enabled (registry, extension_name);
563 for (link = list; link != NULL; link = g_list_next (link)) {
564 ESourceMailIdentity *extension;
565 GHashTable *aliases;
567 source = E_SOURCE (link->data);
569 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
570 extension = e_source_get_extension (source, extension_name);
572 address = e_source_mail_identity_dup_address (extension);
574 aliases = e_source_mail_identity_get_aliases_as_hash_table (extension);
576 attendee = get_attendee (attendees, address, aliases);
577 if (attendee != NULL) {
578 gchar *user_email;
580 user_email = g_strdup (itip_strip_mailto (attendee->value));
581 e_cal_component_free_attendee_list (attendees);
583 if (aliases)
584 g_hash_table_destroy (aliases);
585 g_free (address);
587 g_list_free_full (list, g_object_unref);
589 return user_email;
592 /* If the account was not found in the attendees list, then
593 * let's check the 'sentby' fields of the attendees if we can
594 * find the account. */
595 attendee = get_attendee_if_attendee_sentby_is_user (attendees, address, aliases);
596 if (attendee) {
597 gchar *user_email;
599 user_email = g_strdup (itip_strip_mailto (attendee->sentby));
600 e_cal_component_free_attendee_list (attendees);
602 if (aliases)
603 g_hash_table_destroy (aliases);
604 g_free (address);
606 g_list_free_full (list, g_object_unref);
608 return user_email;
611 if (aliases)
612 g_hash_table_destroy (aliases);
613 g_free (address);
616 g_list_free_full (list, g_object_unref);
618 /* We could not find the attendee in the component, so just give
619 * the default account address if the email address is not set in
620 * the backend. */
621 /* FIXME do we have a better way ? */
622 itip_get_default_name_and_address (registry, NULL, &address);
624 e_cal_component_free_attendee_list (attendees);
626 if (address == NULL)
627 address = g_strdup ("");
629 return address;
632 const gchar *
633 itip_strip_mailto (const gchar *address)
635 if (address == NULL)
636 return NULL;
638 if (!g_ascii_strncasecmp (address, "mailto:", 7))
639 address += 7;
641 return address;
644 static gchar *
645 get_label (struct icaltimetype *tt,
646 gboolean use_24_hour_format)
648 gchar buffer[1000];
649 struct tm tmp_tm;
651 tmp_tm = icaltimetype_to_tm (tt);
653 e_time_format_date_and_time (
654 &tmp_tm, use_24_hour_format, FALSE, FALSE, buffer, 1000);
656 return g_strdup (buffer);
659 typedef struct {
660 GHashTable *tzids;
661 icalcomponent *icomp;
662 ECalClient *client;
663 icalcomponent *zones;
664 } ItipUtilTZData;
666 static void
667 foreach_tzid_callback (icalparameter *param,
668 gpointer data)
670 ItipUtilTZData *tz_data = data;
671 const gchar *tzid;
672 icaltimezone *zone = NULL;
673 icalcomponent *vtimezone_comp;
675 /* Get the TZID string from the parameter. */
676 tzid = icalparameter_get_tzid (param);
677 if (!tzid || g_hash_table_lookup (tz_data->tzids, tzid))
678 return;
680 /* Look for the timezone */
681 if (tz_data->zones != NULL)
682 zone = icalcomponent_get_timezone (tz_data->zones, tzid);
683 if (zone == NULL)
684 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
685 if (zone == NULL && tz_data->client != NULL)
686 e_cal_client_get_timezone_sync (tz_data->client, tzid, &zone, NULL, NULL);
687 if (zone == NULL)
688 return;
690 /* Convert it to a string and add it to the hash. */
691 vtimezone_comp = icaltimezone_get_component (zone);
692 if (!vtimezone_comp)
693 return;
695 icalcomponent_add_component (
696 tz_data->icomp, icalcomponent_new_clone (vtimezone_comp));
697 g_hash_table_insert (tz_data->tzids, (gchar *) tzid, (gchar *) tzid);
700 static icalcomponent *
701 comp_toplevel_with_zones (ECalComponentItipMethod method,
702 const GSList *ecomps,
703 ECalClient *cal_client,
704 icalcomponent *zones)
706 icalcomponent *top_level, *icomp;
707 icalproperty *prop;
708 icalvalue *value;
709 ItipUtilTZData tz_data;
710 GSList *link;
712 top_level = e_cal_util_new_top_level ();
714 prop = icalproperty_new (ICAL_METHOD_PROPERTY);
715 value = icalvalue_new_method (itip_methods_enum[method]);
716 icalproperty_set_value (prop, value);
717 icalcomponent_add_property (top_level, prop);
719 tz_data.tzids = g_hash_table_new (g_str_hash, g_str_equal);
720 tz_data.icomp = top_level;
721 tz_data.client = cal_client;
722 tz_data.zones = zones;
724 for (link = (GSList *) ecomps; link; link = g_slist_next (link)) {
725 icomp = e_cal_component_get_icalcomponent (link->data);
726 icomp = icalcomponent_new_clone (icomp);
728 icalcomponent_foreach_tzid (icomp, foreach_tzid_callback, &tz_data);
730 icalcomponent_add_component (top_level, icomp);
733 g_hash_table_destroy (tz_data.tzids);
735 return top_level;
738 static gboolean
739 users_has_attendee (const GSList *users,
740 const gchar *address)
742 const GSList *l;
744 for (l = users; l != NULL; l = l->next) {
745 if (!g_ascii_strcasecmp (address, l->data))
746 return TRUE;
749 return FALSE;
752 static gchar *
753 comp_from (ECalComponentItipMethod method,
754 ECalComponent *comp,
755 ESourceRegistry *registry,
756 gchar **from_name)
758 ECalComponentOrganizer organizer;
759 ECalComponentAttendee *attendee;
760 GSList *attendees;
761 gchar *from;
762 gchar *sender = NULL;
764 switch (method) {
765 case E_CAL_COMPONENT_METHOD_PUBLISH:
766 case E_CAL_COMPONENT_METHOD_REQUEST:
767 return itip_get_comp_attendee (registry, comp, NULL);
769 case E_CAL_COMPONENT_METHOD_REPLY:
770 sender = itip_get_comp_attendee (registry, comp, NULL);
771 if (sender != NULL)
772 return sender;
773 if (!e_cal_component_has_attendees (comp))
774 return NULL;
775 /* coverity[fallthrough] */
777 case E_CAL_COMPONENT_METHOD_CANCEL:
779 case E_CAL_COMPONENT_METHOD_ADD:
781 e_cal_component_get_organizer (comp, &organizer);
782 if (organizer.value == NULL) {
783 e_notice (
784 NULL, GTK_MESSAGE_ERROR,
785 _("An organizer must be set."));
786 return NULL;
788 if (from_name)
789 *from_name = g_strdup (organizer.cn);
790 return g_strdup (itip_strip_mailto (organizer.value));
792 default:
793 if (!e_cal_component_has_attendees (comp))
794 return NULL;
796 e_cal_component_get_attendee_list (comp, &attendees);
797 attendee = attendees->data;
798 if (attendee->value != NULL) {
799 from = g_strdup (itip_strip_mailto (attendee->value));
800 if (from_name)
801 *from_name = g_strdup (attendee->cn);
802 } else
803 from = NULL;
804 e_cal_component_free_attendee_list (attendees);
806 return from;
810 static EDestination **
811 comp_to_list (ESourceRegistry *registry,
812 ECalComponentItipMethod method,
813 ECalComponent *comp,
814 const GSList *users,
815 gboolean reply_all,
816 const GSList *only_attendees)
818 ECalComponentOrganizer organizer;
819 GSList *attendees, *l;
820 GPtrArray *array = NULL;
821 EDestination *destination;
822 gint len;
823 gchar *sender = NULL;
825 union {
826 gpointer *pdata;
827 EDestination **destinations;
828 } convert;
830 switch (method) {
831 case E_CAL_COMPONENT_METHOD_REQUEST:
832 case E_CAL_COMPONENT_METHOD_CANCEL:
833 e_cal_component_get_attendee_list (comp, &attendees);
834 len = g_slist_length (attendees);
835 if (len <= 0) {
836 e_notice (
837 NULL, GTK_MESSAGE_ERROR,
838 _("At least one attendee is necessary"));
839 e_cal_component_free_attendee_list (attendees);
840 return NULL;
843 e_cal_component_get_organizer (comp, &organizer);
844 if (organizer.value == NULL) {
845 e_notice (
846 NULL, GTK_MESSAGE_ERROR,
847 _("An organizer must be set."));
848 return NULL;
851 array = g_ptr_array_new ();
853 sender = itip_get_comp_attendee (registry, comp, NULL);
855 for (l = attendees; l != NULL; l = l->next) {
856 ECalComponentAttendee *att = l->data;
858 /* Bugfix: 688711 - Varadhan
859 * Resource is also considered as a "attendee". If the respective backend
860 * is able to successfully book resources automagically, it will appear
861 * in the users list and thereby won't get added to the list of destinations
862 * to send the meeting invite, otherwise, as a safety measure, a meeting
863 * invite will be sent to the resources as well. */
864 if (att->cutype != ICAL_CUTYPE_INDIVIDUAL &&
865 att->cutype != ICAL_CUTYPE_GROUP &&
866 att->cutype != ICAL_CUTYPE_RESOURCE &&
867 att->cutype != ICAL_CUTYPE_UNKNOWN)
868 continue;
869 else if (users_has_attendee (users, att->value))
870 continue;
871 else if (att->sentby &&
872 users_has_attendee (users, att->sentby))
873 continue;
874 else if (!g_ascii_strcasecmp (
875 att->value, organizer.value))
876 continue;
877 else if (att->sentby && !g_ascii_strcasecmp (
878 att->sentby, organizer.sentby))
879 continue;
880 else if (!g_ascii_strcasecmp (
881 itip_strip_mailto (att->value), sender))
882 continue;
883 else if (att->status == ICAL_PARTSTAT_DELEGATED &&
884 (att->delto && *att->delto) && !(att->rsvp) &&
885 method == E_CAL_COMPONENT_METHOD_REQUEST)
886 continue;
887 else if (only_attendees &&
888 !cal_comp_util_have_in_new_attendees (
889 only_attendees, itip_strip_mailto (att->value)))
890 continue;
892 destination = e_destination_new ();
893 if (att->cn != NULL)
894 e_destination_set_name (destination, att->cn);
895 e_destination_set_email (
896 destination, itip_strip_mailto (att->value));
897 g_ptr_array_add (array, destination);
899 g_free (sender);
900 e_cal_component_free_attendee_list (attendees);
901 break;
903 case E_CAL_COMPONENT_METHOD_REPLY:
905 if (reply_all) {
906 e_cal_component_get_attendee_list (comp, &attendees);
907 len = g_slist_length (attendees);
909 if (len <= 0)
910 return NULL;
912 array = g_ptr_array_new ();
914 sender = itip_get_comp_attendee (registry, comp, NULL);
916 e_cal_component_get_organizer (comp, &organizer);
917 if (organizer.value && (!sender || g_ascii_strcasecmp (
918 itip_strip_mailto (organizer.value), sender) != 0)) {
919 destination = e_destination_new ();
920 e_destination_set_email (
921 destination,
922 itip_strip_mailto (organizer.value));
923 if (organizer.cn)
924 e_destination_set_name (destination, organizer.cn);
925 g_ptr_array_add (array, destination);
928 for (l = attendees; l != NULL; l = l->next) {
929 ECalComponentAttendee *att = l->data;
931 if (!att->value)
932 continue;
933 else if (att->cutype != ICAL_CUTYPE_INDIVIDUAL &&
934 att->cutype != ICAL_CUTYPE_GROUP &&
935 att->cutype != ICAL_CUTYPE_UNKNOWN)
936 continue;
937 else if (only_attendees &&
938 !cal_comp_util_have_in_new_attendees (
939 only_attendees, itip_strip_mailto (att->value)))
940 continue;
941 else if (organizer.value &&
942 g_ascii_strcasecmp (att->value, organizer.value) == 0)
943 continue;
944 else if (sender && g_ascii_strcasecmp (
945 itip_strip_mailto (att->value), sender) == 0)
946 continue;
948 destination = e_destination_new ();
949 if (att->cn != NULL)
950 e_destination_set_name (destination, att->cn);
951 e_destination_set_email (
952 destination, itip_strip_mailto (att->value));
953 g_ptr_array_add (array, destination);
956 g_free (sender);
957 e_cal_component_free_attendee_list (attendees);
959 } else {
960 array = g_ptr_array_new ();
962 destination = e_destination_new ();
963 e_cal_component_get_organizer (comp, &organizer);
964 if (organizer.cn)
965 e_destination_set_name (destination, organizer.cn);
966 if (organizer.value)
967 e_destination_set_email (
968 destination, itip_strip_mailto (organizer.value));
969 g_ptr_array_add (array, destination);
971 break;
973 case E_CAL_COMPONENT_METHOD_ADD:
974 case E_CAL_COMPONENT_METHOD_REFRESH:
975 case E_CAL_COMPONENT_METHOD_COUNTER:
976 case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
977 e_cal_component_get_organizer (comp, &organizer);
978 if (organizer.value == NULL) {
979 e_notice (
980 NULL, GTK_MESSAGE_ERROR,
981 _("An organizer must be set."));
982 return NULL;
985 array = g_ptr_array_new ();
987 destination = e_destination_new ();
988 if (organizer.cn != NULL)
989 e_destination_set_name (destination, organizer.cn);
990 e_destination_set_email (
991 destination, itip_strip_mailto (organizer.value));
992 g_ptr_array_add (array, destination);
994 /* send the status to delegatee to the delegate also*/
995 e_cal_component_get_attendee_list (comp, &attendees);
996 sender = itip_get_comp_attendee (registry, comp, NULL);
998 for (l = attendees; l != NULL; l = l->next) {
999 ECalComponentAttendee *att = l->data;
1001 if (att->cutype != ICAL_CUTYPE_INDIVIDUAL &&
1002 att->cutype != ICAL_CUTYPE_GROUP &&
1003 att->cutype != ICAL_CUTYPE_UNKNOWN)
1004 continue;
1006 if (!g_ascii_strcasecmp (
1007 itip_strip_mailto (att->value), sender) ||
1008 (att->sentby && !g_ascii_strcasecmp (
1009 itip_strip_mailto (att->sentby), sender))) {
1011 if (!(att->delfrom && *att->delfrom))
1012 break;
1014 destination = e_destination_new ();
1015 e_destination_set_email (
1016 destination, itip_strip_mailto (att->delfrom));
1017 g_ptr_array_add (array, destination);
1021 e_cal_component_free_attendee_list (attendees);
1023 break;
1024 case E_CAL_COMPONENT_METHOD_PUBLISH:
1025 if (users) {
1026 const GSList *list;
1028 array = g_ptr_array_new ();
1030 for (list = users; list != NULL; list = list->next) {
1031 destination = e_destination_new ();
1032 e_destination_set_email (destination, list->data);
1033 g_ptr_array_add (array, destination);
1036 break;
1038 default:
1039 break;
1042 if (array == NULL)
1043 return NULL;
1045 g_ptr_array_add (array, NULL);
1046 convert.pdata = g_ptr_array_free (array, FALSE);
1048 return convert.destinations;
1051 static gchar *
1052 comp_subject (ESourceRegistry *registry,
1053 ECalComponentItipMethod method,
1054 ECalComponent *comp)
1056 ECalComponentText caltext;
1057 const gchar *description, *prefix = NULL;
1058 GSList *alist, *l;
1059 gchar *subject;
1060 gchar *sender;
1061 ECalComponentAttendee *a = NULL;
1063 e_cal_component_get_summary (comp, &caltext);
1064 if (caltext.value != NULL)
1065 description = caltext.value;
1066 else {
1067 switch (e_cal_component_get_vtype (comp)) {
1068 case E_CAL_COMPONENT_EVENT:
1069 description = _("Event information");
1070 break;
1071 case E_CAL_COMPONENT_TODO:
1072 description = _("Task information");
1073 break;
1074 case E_CAL_COMPONENT_JOURNAL:
1075 description = _("Memo information");
1076 break;
1077 case E_CAL_COMPONENT_FREEBUSY:
1078 description = _("Free/Busy information");
1079 break;
1080 default:
1081 description = _("Calendar information");
1085 switch (method) {
1086 case E_CAL_COMPONENT_METHOD_PUBLISH:
1087 case E_CAL_COMPONENT_METHOD_REQUEST:
1088 /* FIXME: If this is an update to a previous
1089 * PUBLISH or REQUEST, then
1090 prefix = U_("Updated");
1092 break;
1094 case E_CAL_COMPONENT_METHOD_REPLY:
1095 e_cal_component_get_attendee_list (comp, &alist);
1096 sender = itip_get_comp_attendee (registry, comp, NULL);
1097 if (sender) {
1099 for (l = alist; l != NULL; l = l->next) {
1100 a = l->data;
1101 if ((sender && *sender) && (g_ascii_strcasecmp (
1102 itip_strip_mailto (a->value), sender) ||
1103 (a->sentby && g_ascii_strcasecmp (
1104 itip_strip_mailto (a->sentby), sender))))
1105 break;
1107 g_free (sender);
1110 if (a != NULL) {
1112 switch (a->status) {
1113 case ICAL_PARTSTAT_ACCEPTED:
1114 /* Translators: This is part of the subject
1115 * line of a meeting request or update email.
1116 * The full subject line would be:
1117 * "Accepted: Meeting Name". */
1118 prefix = C_("Meeting", "Accepted");
1119 break;
1120 case ICAL_PARTSTAT_TENTATIVE:
1121 /* Translators: This is part of the subject
1122 * line of a meeting request or update email.
1123 * The full subject line would be:
1124 * "Tentatively Accepted: Meeting Name". */
1125 prefix = C_("Meeting", "Tentatively Accepted");
1126 break;
1127 case ICAL_PARTSTAT_DECLINED:
1128 /* Translators: This is part of the subject
1129 * line of a meeting request or update email.
1130 * The full subject line would be:
1131 * "Declined: Meeting Name". */
1132 prefix = C_("Meeting", "Declined");
1133 break;
1134 case ICAL_PARTSTAT_DELEGATED:
1135 /* Translators: This is part of the subject
1136 * line of a meeting request or update email.
1137 * The full subject line would be:
1138 * "Delegated: Meeting Name". */
1139 prefix = C_("Meeting", "Delegated");
1140 break;
1141 default:
1142 break;
1144 e_cal_component_free_attendee_list (alist);
1146 break;
1148 case E_CAL_COMPONENT_METHOD_ADD:
1149 /* Translators: This is part of the subject line of a
1150 * meeting request or update email. The full subject
1151 * line would be: "Updated: Meeting Name". */
1152 prefix = C_("Meeting", "Updated");
1153 break;
1155 case E_CAL_COMPONENT_METHOD_CANCEL:
1156 /* Translators: This is part of the subject line of a
1157 * meeting request or update email. The full subject
1158 * line would be: "Cancel: Meeting Name". */
1159 prefix = C_("Meeting", "Cancel");
1160 break;
1162 case E_CAL_COMPONENT_METHOD_REFRESH:
1163 /* Translators: This is part of the subject line of a
1164 * meeting request or update email. The full subject
1165 * line would be: "Refresh: Meeting Name". */
1166 prefix = C_("Meeting", "Refresh");
1167 break;
1169 case E_CAL_COMPONENT_METHOD_COUNTER:
1170 /* Translators: This is part of the subject line of a
1171 * meeting request or update email. The full subject
1172 * line would be: "Counter-proposal: Meeting Name". */
1173 prefix = C_("Meeting", "Counter-proposal");
1174 break;
1176 case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
1177 /* Translators: This is part of the subject line of a
1178 * meeting request or update email. The full subject
1179 * line would be: "Declined: Meeting Name". */
1180 prefix = C_("Meeting", "Declined");
1181 break;
1183 default:
1184 break;
1187 if (prefix != NULL)
1188 subject = g_strdup_printf ("%s: %s", prefix, description);
1189 else
1190 subject = g_strdup (description);
1192 return subject;
1195 static gchar *
1196 comp_content_type (ECalComponent *comp,
1197 ECalComponentItipMethod method)
1199 const gchar *name;
1201 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_FREEBUSY)
1202 name = "freebusy.ifb";
1203 else
1204 name = "calendar.ics";
1206 return g_strdup_printf (
1207 "text/calendar; name=\"%s\"; charset=utf-8; METHOD=%s",
1208 name, itip_methods[method]);
1211 static const gchar *
1212 comp_filename (ECalComponent *comp)
1214 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_FREEBUSY)
1215 return "freebusy.ifb";
1216 else
1217 return "calendar.ics";
1220 static gchar *
1221 comp_description (ECalComponent *comp,
1222 gboolean use_24_hour_format)
1224 gchar *description;
1225 ECalComponentDateTime dt;
1226 gchar *start = NULL, *end = NULL;
1228 switch (e_cal_component_get_vtype (comp)) {
1229 case E_CAL_COMPONENT_EVENT:
1230 description = g_strdup (_("Event information"));
1231 break;
1232 case E_CAL_COMPONENT_TODO:
1233 description = g_strdup (_("Task information"));
1234 break;
1235 case E_CAL_COMPONENT_JOURNAL:
1236 description = g_strdup (_("Memo information"));
1237 break;
1238 case E_CAL_COMPONENT_FREEBUSY:
1239 e_cal_component_get_dtstart (comp, &dt);
1240 if (dt.value)
1241 start = get_label (dt.value, use_24_hour_format);
1242 e_cal_component_free_datetime (&dt);
1244 e_cal_component_get_dtend (comp, &dt);
1245 if (dt.value)
1246 end = get_label (dt.value, use_24_hour_format);
1247 e_cal_component_free_datetime (&dt);
1249 if (start != NULL && end != NULL)
1250 description = g_strdup_printf (
1251 _("Free/Busy information (%s to %s)"),
1252 start, end);
1253 else
1254 description = g_strdup (_("Free/Busy information"));
1255 g_free (start);
1256 g_free (end);
1257 break;
1258 default:
1259 description = g_strdup (_("iCalendar information"));
1260 break;
1263 return description;
1266 static gboolean
1267 comp_server_send_sync (ECalComponentItipMethod method,
1268 const GSList *ecomps,
1269 ECalClient *cal_client,
1270 icalcomponent *zones,
1271 GSList **users,
1272 GCancellable *cancellable,
1273 GError **error)
1275 icalcomponent *top_level, *returned_icalcomp = NULL;
1276 gboolean retval = TRUE;
1277 GError *local_error = NULL;
1279 top_level = comp_toplevel_with_zones (method, ecomps, cal_client, zones);
1280 d (printf ("itip-utils.c: comp_server_send_sync: calling e_cal_send_objects... \n"));
1282 e_cal_client_send_objects_sync (
1283 cal_client, top_level, users,
1284 &returned_icalcomp, cancellable, &local_error);
1286 if (g_error_matches (local_error, E_CAL_CLIENT_ERROR, E_CAL_CLIENT_ERROR_OBJECT_ID_ALREADY_EXISTS)) {
1287 g_propagate_error (error, g_error_new (local_error->domain, local_error->code,
1288 _("Unable to book a resource, the new event collides with some other.")));
1289 g_clear_error (&local_error);
1290 retval = FALSE;
1292 } else if (local_error != NULL) {
1293 g_prefix_error (&local_error, "%s", _("Unable to book a resource, error: "));
1294 g_propagate_error (error, local_error);
1295 retval = FALSE;
1298 if (returned_icalcomp != NULL)
1299 icalcomponent_free (returned_icalcomp);
1300 icalcomponent_free (top_level);
1302 return retval;
1305 static gboolean
1306 comp_limit_attendees (ESourceRegistry *registry,
1307 ECalComponent *comp)
1309 icalcomponent *icomp;
1310 icalproperty *prop;
1311 gboolean found = FALSE, match = FALSE;
1312 GSList *l, *list = NULL;
1314 icomp = e_cal_component_get_icalcomponent (comp);
1316 for (prop = icalcomponent_get_first_property (icomp, ICAL_ATTENDEE_PROPERTY);
1317 prop != NULL;
1318 prop = icalcomponent_get_next_property (icomp, ICAL_ATTENDEE_PROPERTY))
1320 gchar *attendee;
1321 gchar *attendee_text;
1322 icalparameter *param;
1323 const gchar *attendee_sentby;
1324 gchar *attendee_sentby_text = NULL;
1326 /* If we've already found something, just erase the rest */
1327 if (found) {
1328 list = g_slist_prepend (list, prop);
1329 continue;
1332 attendee = icalproperty_get_value_as_string_r (prop);
1333 if (!attendee)
1334 continue;
1336 attendee_text = g_strdup (itip_strip_mailto (attendee));
1337 g_free (attendee);
1338 attendee_text = g_strstrip (attendee_text);
1339 found = match = itip_address_is_user (registry, attendee_text);
1341 if (!found) {
1342 param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
1343 if (param) {
1344 attendee_sentby =
1345 icalparameter_get_sentby (param);
1346 attendee_sentby =
1347 itip_strip_mailto (attendee_sentby);
1348 attendee_sentby_text =
1349 g_strstrip (g_strdup (attendee_sentby));
1350 found = match = itip_address_is_user (
1351 registry, attendee_sentby_text);
1355 g_free (attendee_text);
1356 g_free (attendee_sentby_text);
1358 if (!match)
1359 list = g_slist_prepend (list, prop);
1362 for (l = list; l != NULL; l = l->next) {
1363 prop = l->data;
1365 icalcomponent_remove_property (icomp, prop);
1366 icalproperty_free (prop);
1368 g_slist_free (list);
1370 return found;
1373 static void
1374 comp_sentby (ECalComponent *comp,
1375 ECalClient *cal_client,
1376 ESourceRegistry *registry)
1378 ECalComponentOrganizer organizer;
1379 GSList * attendees, *l;
1380 gchar *name;
1381 gchar *address;
1382 gchar *user = NULL;
1384 itip_get_default_name_and_address (registry, &name, &address);
1386 e_cal_component_get_organizer (comp, &organizer);
1387 if (!organizer.value && name != NULL && address != NULL) {
1388 organizer.value = g_strdup_printf ("MAILTO:%s", address);
1389 organizer.sentby = NULL;
1390 organizer.cn = name;
1391 organizer.language = NULL;
1393 e_cal_component_set_organizer (comp, &organizer);
1394 g_free ((gchar *) organizer.value);
1396 g_free (name);
1397 g_free (address);
1398 return;
1401 e_cal_component_get_attendee_list (comp, &attendees);
1402 user = itip_get_comp_attendee (registry, comp, cal_client);
1403 for (l = attendees; l; l = l->next) {
1404 ECalComponentAttendee *a = l->data;
1406 if (!g_ascii_strcasecmp (
1407 itip_strip_mailto (a->value), user) ||
1408 (a->sentby && !g_ascii_strcasecmp (
1409 itip_strip_mailto (a->sentby), user))) {
1410 g_free (user);
1412 g_free (name);
1413 g_free (address);
1414 return;
1418 if (!itip_organizer_is_user (registry, comp, cal_client) &&
1419 !itip_sentby_is_user (registry, comp, cal_client) &&
1420 address != NULL) {
1421 organizer.value = g_strdup (organizer.value);
1422 organizer.sentby = g_strdup_printf ("MAILTO:%s", address);
1423 organizer.cn = g_strdup (organizer.cn);
1424 organizer.language = g_strdup (organizer.language);
1426 e_cal_component_set_organizer (comp, &organizer);
1428 g_free ((gchar *) organizer.value);
1429 g_free ((gchar *) organizer.sentby);
1430 g_free ((gchar *) organizer.cn);
1431 g_free ((gchar *) organizer.language);
1434 g_free (name);
1435 g_free (address);
1438 static ECalComponent *
1439 comp_minimal (ESourceRegistry *registry,
1440 ECalComponent *comp,
1441 gboolean attendee)
1443 ECalComponent *clone;
1444 icalcomponent *icomp, *icomp_clone;
1445 icalproperty *prop;
1446 ECalComponentOrganizer organizer;
1447 const gchar *uid;
1448 GSList *comments;
1449 struct icaltimetype itt;
1450 ECalComponentRange recur_id;
1452 clone = e_cal_component_new ();
1453 e_cal_component_set_new_vtype (clone, e_cal_component_get_vtype (comp));
1455 if (attendee) {
1456 GSList *attendees;
1458 e_cal_component_get_attendee_list (comp, &attendees);
1459 e_cal_component_set_attendee_list (clone, attendees);
1461 if (!comp_limit_attendees (registry, clone)) {
1462 e_notice (
1463 NULL, GTK_MESSAGE_ERROR,
1464 _("You must be an attendee of the event."));
1465 goto error;
1469 itt = icaltime_from_timet_with_zone (
1470 time (NULL), FALSE,
1471 icaltimezone_get_utc_timezone ());
1472 e_cal_component_set_dtstamp (clone, &itt);
1474 e_cal_component_get_organizer (comp, &organizer);
1475 if (organizer.value == NULL)
1476 goto error;
1477 e_cal_component_set_organizer (clone, &organizer);
1479 e_cal_component_get_uid (comp, &uid);
1480 e_cal_component_set_uid (clone, uid);
1482 e_cal_component_get_comment_list (comp, &comments);
1483 if (g_slist_length (comments) <= 1) {
1484 e_cal_component_set_comment_list (clone, comments);
1485 } else {
1486 GSList *l = comments;
1488 comments = g_slist_remove_link (comments, l);
1489 e_cal_component_set_comment_list (clone, l);
1490 e_cal_component_free_text_list (l);
1492 e_cal_component_free_text_list (comments);
1494 e_cal_component_get_recurid (comp, &recur_id);
1495 if (recur_id.datetime.value != NULL)
1496 e_cal_component_set_recurid (clone, &recur_id);
1498 icomp = e_cal_component_get_icalcomponent (comp);
1499 icomp_clone = e_cal_component_get_icalcomponent (clone);
1500 for (prop = icalcomponent_get_first_property (icomp, ICAL_X_PROPERTY);
1501 prop != NULL;
1502 prop = icalcomponent_get_next_property (icomp, ICAL_X_PROPERTY))
1504 icalproperty *p;
1506 p = icalproperty_new_clone (prop);
1507 icalcomponent_add_property (icomp_clone, p);
1510 e_cal_component_rescan (clone);
1512 return clone;
1514 error:
1515 g_object_unref (clone);
1516 return NULL;
1519 static void
1520 strip_x_microsoft_props (ECalComponent *comp)
1522 GSList *lst = NULL, *l;
1523 icalcomponent *icalcomp;
1524 icalproperty *icalprop;
1526 g_return_if_fail (comp != NULL);
1528 icalcomp = e_cal_component_get_icalcomponent (comp);
1529 g_return_if_fail (icalcomp != NULL);
1531 for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
1532 icalprop;
1533 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) {
1534 const gchar *x_name = icalproperty_get_x_name (icalprop);
1536 if (x_name && g_ascii_strncasecmp (x_name, "X-MICROSOFT-", 12) == 0)
1537 lst = g_slist_prepend (lst, icalprop);
1540 for (l = lst; l != NULL; l = l->next) {
1541 icalprop = l->data;
1542 icalcomponent_remove_property (icalcomp, icalprop);
1543 icalproperty_free (icalprop);
1546 g_slist_free (lst);
1549 static ECalComponent *
1550 comp_compliant_one (ESourceRegistry *registry,
1551 ECalComponentItipMethod method,
1552 ECalComponent *comp,
1553 ECalClient *client,
1554 icalcomponent *zones,
1555 icaltimezone *default_zone,
1556 gboolean strip_alarms)
1558 ECalComponent *clone, *temp_clone;
1559 struct icaltimetype itt;
1561 clone = e_cal_component_clone (comp);
1562 itt = icaltime_from_timet_with_zone (
1563 time (NULL), FALSE,
1564 icaltimezone_get_utc_timezone ());
1565 e_cal_component_set_dtstamp (clone, &itt);
1567 /* Make UNTIL date a datetime in a simple recurrence */
1568 if (e_cal_component_has_recurrences (clone)
1569 && e_cal_component_has_simple_recurrence (clone)) {
1570 GSList *rrule_list;
1571 struct icalrecurrencetype *r;
1573 e_cal_component_get_rrule_list (clone, &rrule_list);
1574 r = rrule_list->data;
1576 if (!icaltime_is_null_time (r->until) && r->until.is_date) {
1577 ECalComponentDateTime dt;
1578 icaltimezone *from_zone = NULL, *to_zone;
1580 e_cal_component_get_dtstart (clone, &dt);
1582 if (dt.value->is_date) {
1583 from_zone = default_zone;
1584 } else if (dt.tzid == NULL) {
1585 from_zone = icaltimezone_get_utc_timezone ();
1586 } else {
1587 if (zones != NULL)
1588 from_zone = icalcomponent_get_timezone (zones, dt.tzid);
1589 if (from_zone == NULL)
1590 from_zone = icaltimezone_get_builtin_timezone_from_tzid (dt.tzid);
1591 if (from_zone == NULL && client != NULL)
1592 /* FIXME Error checking */
1593 e_cal_client_get_timezone_sync (
1594 client, dt.tzid,
1595 &from_zone, NULL, NULL);
1598 to_zone = icaltimezone_get_utc_timezone ();
1600 r->until.hour = dt.value->hour;
1601 r->until.minute = dt.value->minute;
1602 r->until.second = dt.value->second;
1603 r->until.is_date = FALSE;
1605 icaltimezone_convert_time (&r->until, from_zone, to_zone);
1606 r->until.zone = to_zone;
1608 e_cal_component_free_datetime (&dt);
1609 e_cal_component_set_rrule_list (clone, rrule_list);
1610 e_cal_component_abort_sequence (clone);
1613 e_cal_component_free_recur_list (rrule_list);
1616 /* We delete incoming alarms if requested, even this helps with outlook */
1617 if (strip_alarms) {
1618 e_cal_component_remove_all_alarms (clone);
1619 } else {
1620 /* Always strip procedure alarms, because of security */
1621 GList *uids, *l;
1623 uids = e_cal_component_get_alarm_uids (clone);
1625 for (l = uids; l; l = l->next) {
1626 ECalComponentAlarm *alarm;
1627 ECalComponentAlarmAction action = E_CAL_COMPONENT_ALARM_UNKNOWN;
1629 alarm = e_cal_component_get_alarm (clone, (const gchar *) l->data);
1630 if (alarm) {
1631 e_cal_component_alarm_get_action (alarm, &action);
1632 e_cal_component_alarm_free (alarm);
1634 if (action == E_CAL_COMPONENT_ALARM_PROCEDURE)
1635 e_cal_component_remove_alarm (clone, (const gchar *) l->data);
1639 cal_obj_uid_list_free (uids);
1642 strip_x_microsoft_props (clone);
1644 /* Strip X-LIC-ERROR stuff */
1645 e_cal_component_strip_errors (clone);
1647 /* Comply with itip spec */
1648 switch (method) {
1649 case E_CAL_COMPONENT_METHOD_PUBLISH:
1650 comp_sentby (clone, client, registry);
1651 e_cal_component_set_attendee_list (clone, NULL);
1652 break;
1653 case E_CAL_COMPONENT_METHOD_REQUEST:
1654 comp_sentby (clone, client, registry);
1655 break;
1656 case E_CAL_COMPONENT_METHOD_CANCEL:
1657 comp_sentby (clone, client, registry);
1658 break;
1659 case E_CAL_COMPONENT_METHOD_REPLY:
1660 break;
1661 case E_CAL_COMPONENT_METHOD_ADD:
1662 break;
1663 case E_CAL_COMPONENT_METHOD_REFRESH:
1664 /* Need to remove almost everything */
1665 temp_clone = comp_minimal (registry, clone, TRUE);
1666 g_object_unref (clone);
1667 clone = temp_clone;
1668 break;
1669 case E_CAL_COMPONENT_METHOD_COUNTER:
1670 break;
1671 case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
1672 /* Need to remove almost everything */
1673 temp_clone = comp_minimal (registry, clone, FALSE);
1674 g_object_unref (clone);
1675 clone = temp_clone;
1676 break;
1677 default:
1678 break;
1681 return clone;
1684 static gboolean
1685 comp_compliant (ESourceRegistry *registry,
1686 ECalComponentItipMethod method,
1687 GSList *ecomps,
1688 gboolean unref_orig_ecomp,
1689 ECalClient *client,
1690 icalcomponent *zones,
1691 icaltimezone *default_zone,
1692 gboolean strip_alarms)
1694 GSList *link;
1696 if (!ecomps)
1697 return FALSE;
1699 for (link = ecomps; link; link = g_slist_next (link)) {
1700 ECalComponent *original_comp = link->data, *new_comp;
1702 new_comp = comp_compliant_one (registry, method, original_comp, client, zones, default_zone, strip_alarms);
1703 if (new_comp) {
1704 cal_comp_util_copy_new_attendees (new_comp, original_comp);
1706 if (unref_orig_ecomp)
1707 g_object_unref (original_comp);
1709 link->data = new_comp;
1710 } else {
1711 return FALSE;
1715 return TRUE;
1718 void
1719 itip_cal_mime_attach_free (gpointer ptr)
1721 struct CalMimeAttach *mime_attach = ptr;
1723 if (mime_attach) {
1724 g_free (mime_attach->filename);
1725 g_free (mime_attach->content_type);
1726 g_free (mime_attach->content_id);
1727 g_free (mime_attach->description);
1728 g_free (mime_attach->encoded_data);
1729 g_free (mime_attach);
1733 static void
1734 append_cal_attachments (EMsgComposer *composer,
1735 GSList *attach_list)
1737 struct CalMimeAttach *mime_attach;
1738 GSList *l;
1740 for (l = attach_list; l; l = l->next) {
1741 CamelMimePart *attachment;
1743 mime_attach = (struct CalMimeAttach *) l->data;
1745 attachment = camel_mime_part_new ();
1746 camel_mime_part_set_content (
1747 attachment, mime_attach->encoded_data,
1748 mime_attach->length, mime_attach->content_type);
1749 if (mime_attach->content_id)
1750 camel_mime_part_set_content_id (
1751 attachment, mime_attach->content_id);
1752 if (mime_attach->filename != NULL)
1753 camel_mime_part_set_filename (
1754 attachment, mime_attach->filename);
1755 if (mime_attach->description != NULL)
1756 camel_mime_part_set_description (
1757 attachment, mime_attach->description);
1758 if (mime_attach->disposition)
1759 camel_mime_part_set_disposition (
1760 attachment, "inline");
1761 else
1762 camel_mime_part_set_disposition (
1763 attachment, "attachment");
1764 e_msg_composer_attach (composer, attachment);
1765 g_object_unref (attachment);
1768 g_slist_free_full (attach_list, itip_cal_mime_attach_free);
1771 static ESource *
1772 find_enabled_identity (ESourceRegistry *registry,
1773 const gchar *id_address)
1775 GList *list, *link;
1776 ESource *mail_identity = NULL;
1777 const gchar *extension_name;
1779 if (id_address == NULL)
1780 return NULL;
1782 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1783 list = e_source_registry_list_enabled (registry, extension_name);
1785 for (link = list; link != NULL; link = g_list_next (link)) {
1786 ESource *source = E_SOURCE (link->data);
1787 ESourceMailIdentity *extension;
1788 GHashTable *aliases;
1789 const gchar *address;
1791 extension = e_source_get_extension (source, extension_name);
1792 address = e_source_mail_identity_get_address (extension);
1794 if (address && g_ascii_strcasecmp (address, id_address) == 0) {
1795 mail_identity = g_object_ref (source);
1796 break;
1799 aliases = e_source_mail_identity_get_aliases_as_hash_table (extension);
1800 if (aliases) {
1801 if (g_hash_table_contains (aliases, id_address))
1802 mail_identity = g_object_ref (source);
1803 g_hash_table_destroy (aliases);
1805 if (mail_identity)
1806 break;
1810 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1812 return mail_identity;
1815 static gchar *
1816 get_identity_uid_for_from (EShell *shell,
1817 ECalComponentItipMethod method,
1818 ECalComponent *comp,
1819 ECalClient *cal_client,
1820 gchar **identity_name,
1821 gchar **identity_address)
1823 EClientCache *client_cache;
1824 ESourceRegistry *registry;
1825 ESource *source = NULL;
1826 gchar *identity_uid = NULL;
1828 client_cache = e_shell_get_client_cache (shell);
1829 registry = e_client_cache_ref_registry (client_cache);
1831 /* always use organizer's email when user is an organizer */
1832 if (itip_organizer_is_user (registry, comp, cal_client)) {
1833 ECalComponentOrganizer organizer = {0};
1835 e_cal_component_get_organizer (comp, &organizer);
1836 if (organizer.value != NULL) {
1837 source = find_enabled_identity (
1838 registry,
1839 itip_strip_mailto (organizer.value));
1841 if (source) {
1842 if (identity_name)
1843 *identity_name = g_strdup (organizer.cn);
1844 if (identity_address)
1845 *identity_address = g_strdup (itip_strip_mailto (organizer.value));
1850 if (source == NULL) {
1851 gchar *from = comp_from (method, comp, registry, identity_name);
1853 if (from != NULL) {
1854 source = find_enabled_identity (registry, from);
1855 if (source) {
1856 if (identity_address)
1857 *identity_address = g_strdup (from);
1861 g_free (from);
1864 if (source != NULL) {
1865 identity_uid = g_strdup (e_source_get_uid (source));
1867 g_object_unref (source);
1870 g_object_unref (registry);
1872 return identity_uid;
1875 static gint
1876 master_first_cmp (gconstpointer ptr1,
1877 gconstpointer ptr2)
1879 ECalComponent *comp1 = (ECalComponent *) ptr1;
1880 ECalComponent *comp2 = (ECalComponent *) ptr2;
1881 icalcomponent *icomp1 = comp1 ? e_cal_component_get_icalcomponent (comp1) : NULL;
1882 icalcomponent *icomp2 = comp2 ? e_cal_component_get_icalcomponent (comp2) : NULL;
1883 gboolean has_rid1, has_rid2;
1885 has_rid1 = (icomp1 && icalcomponent_get_first_property (icomp1, ICAL_RECURRENCEID_PROPERTY)) ? 1 : 0;
1886 has_rid2 = (icomp2 && icalcomponent_get_first_property (icomp2, ICAL_RECURRENCEID_PROPERTY)) ? 1 : 0;
1888 if (has_rid1 == has_rid2)
1889 return g_strcmp0 (icomp1 ? icalcomponent_get_uid (icomp1) : NULL,
1890 icomp2 ? icalcomponent_get_uid (icomp2) : NULL);
1892 if (has_rid1)
1893 return 1;
1895 return -1;
1898 typedef struct {
1899 ESourceRegistry *registry;
1900 ECalComponentItipMethod method;
1901 GSList *send_comps; /* ECalComponent * */
1902 ECalClient *cal_client;
1903 icalcomponent *zones;
1904 GSList *attachments_list;
1905 GSList *users;
1906 gboolean strip_alarms;
1907 gboolean only_new_attendees;
1908 gboolean ensure_master_object;
1910 gboolean completed;
1911 gboolean success;
1913 GError *async_error;
1914 } ItipSendComponentData;
1916 static void
1917 itip_send_component_data_free (gpointer ptr)
1919 ItipSendComponentData *isc = ptr;
1921 if (isc) {
1922 g_clear_object (&isc->registry);
1923 g_slist_free_full (isc->send_comps, g_object_unref);
1924 g_clear_object (&isc->cal_client);
1925 g_clear_error (&isc->async_error);
1926 if (isc->zones)
1927 icalcomponent_free (isc->zones);
1928 g_slist_free_full (isc->attachments_list, itip_cal_mime_attach_free); /* CamelMimePart */
1929 g_slist_free_full (isc->users, g_free);
1930 g_free (isc);
1934 static void
1935 itip_send_component_begin (ItipSendComponentData *isc,
1936 GCancellable *cancellable,
1937 GError **error)
1939 g_return_if_fail (isc != NULL);
1941 isc->completed = FALSE;
1943 if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH && e_cal_client_check_save_schedules (isc->cal_client)) {
1944 isc->success = TRUE;
1945 isc->completed = TRUE;
1946 return;
1949 if (isc->ensure_master_object && isc->send_comps) {
1950 /* Ensure we send the master object with its detached instances, not the instance only */
1951 GSList *ecalcomps = NULL;
1952 const gchar *uid = NULL;
1954 e_cal_component_get_uid (isc->send_comps->data, &uid);
1956 if (e_cal_client_get_objects_for_uid_sync (isc->cal_client, uid, &ecalcomps, cancellable, NULL) && ecalcomps) {
1957 GSList *old_send_comps = isc->send_comps;
1959 isc->send_comps = g_slist_sort (ecalcomps, master_first_cmp);
1961 cal_comp_util_copy_new_attendees (isc->send_comps->data, old_send_comps->data);
1963 g_slist_free_full (old_send_comps, g_object_unref);
1967 /* Give the server a chance to manipulate the comp */
1968 if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH) {
1969 d (printf ("itip-utils.c: itip_send_component_begin: calling comp_server_send_sync... \n"));
1970 if (!comp_server_send_sync (isc->method, isc->send_comps, isc->cal_client, isc->zones, &isc->users, cancellable, error)) {
1971 isc->success = FALSE;
1972 isc->completed = TRUE;
1973 return;
1977 /* check whether backend could handle sending requests/updates */
1978 if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH &&
1979 e_client_check_capability (E_CLIENT (isc->cal_client), CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) {
1980 isc->success = TRUE;
1981 isc->completed = TRUE;
1985 typedef struct _CreateComposerData {
1986 gchar *identity_uid;
1987 gchar *identity_name;
1988 gchar *identity_address;
1989 EDestination **destinations;
1990 gchar *subject;
1991 gchar *ical_string;
1992 gchar *content_type;
1993 gchar *event_body_text;
1994 GSList *attachments_list;
1995 GSList *send_comps; /* ECalComponent * */
1996 gboolean show_only;
1997 } CreateComposerData;
1999 static void
2000 itip_send_component_composer_created_cb (GObject *source_object,
2001 GAsyncResult *result,
2002 gpointer user_data)
2004 CreateComposerData *ccd = user_data;
2005 EComposerHeaderTable *table;
2006 EMsgComposer *composer;
2007 GSettings *settings;
2008 gboolean use_24hour_format;
2009 GError *error = NULL;
2011 g_return_if_fail (ccd != NULL);
2013 composer = e_msg_composer_new_finish (result, &error);
2014 if (error) {
2015 g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC, error->message);
2016 g_clear_error (&error);
2017 return;
2020 settings = e_util_ref_settings ("org.gnome.evolution.calendar");
2021 use_24hour_format = g_settings_get_boolean (settings, "use-24hour-format");
2022 g_object_unref (settings);
2024 table = e_msg_composer_get_header_table (composer);
2026 if (ccd->identity_uid)
2027 e_composer_header_table_set_identity_uid (table, ccd->identity_uid, ccd->identity_name, ccd->identity_address);
2029 e_composer_header_table_set_subject (table, ccd->subject);
2030 e_composer_header_table_set_destinations_to (table, ccd->destinations);
2032 if (e_cal_component_get_vtype (ccd->send_comps->data) == E_CAL_COMPONENT_EVENT) {
2033 if (ccd->event_body_text)
2034 e_msg_composer_set_body_text (composer, ccd->event_body_text, TRUE);
2035 else
2036 e_msg_composer_set_body (composer, ccd->ical_string, ccd->content_type);
2037 } else {
2038 CamelMimePart *attachment;
2039 const gchar *filename;
2040 gchar *description;
2041 gchar *body;
2043 filename = comp_filename (ccd->send_comps->data);
2044 description = comp_description (ccd->send_comps->data, use_24hour_format);
2046 body = camel_text_to_html (description, CAMEL_MIME_FILTER_TOHTML_PRE, 0);
2047 e_msg_composer_set_body_text (composer, body, TRUE);
2048 g_free (body);
2050 attachment = camel_mime_part_new ();
2051 camel_mime_part_set_content (
2052 attachment, ccd->ical_string,
2053 strlen (ccd->ical_string), ccd->content_type);
2054 if (filename != NULL && *filename != '\0')
2055 camel_mime_part_set_filename (attachment, filename);
2056 if (description != NULL && *description != '\0')
2057 camel_mime_part_set_description (attachment, description);
2058 camel_mime_part_set_disposition (attachment, "inline");
2059 e_msg_composer_attach (composer, attachment);
2060 g_object_unref (attachment);
2062 g_free (description);
2065 append_cal_attachments (composer, ccd->attachments_list);
2066 ccd->attachments_list = NULL;
2068 if (ccd->show_only)
2069 gtk_widget_show (GTK_WIDGET (composer));
2070 else
2071 e_msg_composer_send (composer);
2073 e_destination_freev (ccd->destinations);
2074 g_slist_free_full (ccd->send_comps, g_object_unref);
2075 g_free (ccd->identity_uid);
2076 g_free (ccd->identity_name);
2077 g_free (ccd->identity_address);
2078 g_free (ccd->subject);
2079 g_free (ccd->ical_string);
2080 g_free (ccd->content_type);
2081 g_free (ccd->event_body_text);
2082 g_free (ccd);
2085 static void
2086 itip_send_component_complete (ItipSendComponentData *isc)
2088 CreateComposerData *ccd;
2089 EDestination **destinations;
2090 EShell *shell;
2091 icalcomponent *top_level = NULL;
2092 icaltimezone *default_zone;
2093 gchar *identity_uid, *identity_name = NULL, *identity_address = NULL;
2095 g_return_if_fail (isc != NULL);
2097 if (isc->completed)
2098 return;
2100 isc->success = FALSE;
2102 default_zone = calendar_config_get_icaltimezone ();
2104 shell = e_shell_get_default ();
2106 identity_uid = get_identity_uid_for_from (shell, isc->method, isc->send_comps->data, isc->cal_client, &identity_name, &identity_address);
2108 /* Tidy up the comp */
2109 if (!comp_compliant (isc->registry, isc->method, isc->send_comps, TRUE, isc->cal_client, isc->zones, default_zone, isc->strip_alarms)) {
2110 g_free (identity_uid);
2111 g_free (identity_name);
2112 g_free (identity_address);
2113 goto cleanup;
2116 /* Recipients */
2117 destinations = comp_to_list (
2118 isc->registry, isc->method, isc->send_comps->data, isc->users, FALSE,
2119 isc->only_new_attendees ? g_object_get_data (
2120 G_OBJECT (isc->send_comps->data), "new-attendees") : NULL);
2121 if (isc->method != E_CAL_COMPONENT_METHOD_PUBLISH) {
2122 if (destinations == NULL) {
2123 /* We sent them all via the server */
2124 isc->success = TRUE;
2125 g_free (identity_uid);
2126 g_free (identity_name);
2127 g_free (identity_address);
2128 goto cleanup;
2132 top_level = comp_toplevel_with_zones (isc->method, isc->send_comps, isc->cal_client, isc->zones);
2134 ccd = g_new0 (CreateComposerData, 1);
2135 ccd->identity_uid = identity_uid;
2136 ccd->identity_name = identity_name;
2137 ccd->identity_address = identity_address;
2138 ccd->destinations = destinations;
2139 ccd->subject = comp_subject (isc->registry, isc->method, isc->send_comps->data);
2140 ccd->ical_string = icalcomponent_as_ical_string_r (top_level);
2141 ccd->content_type = comp_content_type (isc->send_comps->data, isc->method);
2142 ccd->event_body_text = NULL;
2143 ccd->attachments_list = isc->attachments_list;
2144 ccd->send_comps = isc->send_comps;
2145 ccd->show_only = isc->method == E_CAL_COMPONENT_METHOD_PUBLISH && !isc->users;
2147 isc->attachments_list = NULL;
2148 isc->send_comps = NULL;
2150 e_msg_composer_new (shell, itip_send_component_composer_created_cb, ccd);
2152 isc->success = TRUE;
2154 cleanup:
2155 if (top_level != NULL)
2156 icalcomponent_free (top_level);
2159 static void
2160 itip_send_component_complete_and_free (gpointer ptr)
2162 ItipSendComponentData *isc = ptr;
2164 if (isc) {
2165 itip_send_component_complete (isc);
2166 itip_send_component_data_free (isc);
2170 static void
2171 itip_send_component_thread (EAlertSinkThreadJobData *job_data,
2172 gpointer user_data,
2173 GCancellable *cancellable,
2174 GError **error)
2176 ItipSendComponentData *isc = user_data;
2178 g_return_if_fail (isc != NULL);
2180 itip_send_component_begin (isc, cancellable, error);
2183 void
2184 itip_send_component_with_model (ECalModel *model,
2185 ECalComponentItipMethod method,
2186 ECalComponent *send_comp,
2187 ECalClient *cal_client,
2188 icalcomponent *zones,
2189 GSList *attachments_list,
2190 GSList *users,
2191 gboolean strip_alarms,
2192 gboolean only_new_attendees,
2193 gboolean ensure_master_object)
2195 ESourceRegistry *registry;
2196 ECalDataModel *data_model;
2197 ESource *source;
2198 const gchar *alert_ident = NULL;
2199 const gchar *description = NULL;
2200 gchar *display_name;
2201 GCancellable *cancellable;
2202 ItipSendComponentData *isc;
2204 g_return_if_fail (E_IS_CAL_MODEL (model));
2205 g_return_if_fail (E_IS_CAL_CLIENT (cal_client));
2207 switch (e_cal_client_get_source_type (cal_client)) {
2208 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
2209 description = _("Sending an event");
2210 alert_ident = "calendar:failed-send-event";
2211 break;
2212 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
2213 description = _("Sending a memo");
2214 alert_ident = "calendar:failed-send-memo";
2215 break;
2216 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
2217 description = _("Sending a task");
2218 alert_ident = "calendar:failed-send-task";
2219 break;
2220 default:
2221 g_warn_if_reached ();
2222 break;
2225 registry = e_cal_model_get_registry (model);
2226 data_model = e_cal_model_get_data_model (model);
2227 source = e_client_get_source (E_CLIENT (cal_client));
2229 isc = g_new0 (ItipSendComponentData, 1);
2230 isc->registry = g_object_ref (registry);
2231 isc->method = method;
2232 isc->send_comps = g_slist_prepend (NULL, g_object_ref (send_comp));
2233 isc->cal_client = g_object_ref (cal_client);
2234 if (zones) {
2235 isc->zones = icalcomponent_new_clone (zones);
2237 isc->attachments_list = attachments_list;
2238 if (users) {
2239 GSList *link;
2241 isc->users = g_slist_copy (users);
2242 for (link = isc->users; link; link = g_slist_next (link)) {
2243 link->data = g_strdup (link->data);
2246 isc->strip_alarms = strip_alarms;
2247 isc->only_new_attendees = only_new_attendees;
2248 isc->ensure_master_object = ensure_master_object;
2249 isc->success = FALSE;
2250 isc->completed = FALSE;
2252 display_name = e_util_get_source_full_name (registry, source);
2253 cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident,
2254 display_name, itip_send_component_thread,
2255 isc, itip_send_component_complete_and_free);
2257 g_clear_object (&cancellable);
2258 g_free (display_name);
2261 gboolean
2262 itip_send_comp_sync (ESourceRegistry *registry,
2263 ECalComponentItipMethod method,
2264 ECalComponent *send_comp,
2265 ECalClient *cal_client,
2266 icalcomponent *zones,
2267 GSList *attachments_list,
2268 GSList *users,
2269 gboolean strip_alarms,
2270 gboolean only_new_attendees,
2271 GCancellable *cancellable,
2272 GError **error)
2274 ItipSendComponentData isc;
2276 memset (&isc, 0, sizeof (ItipSendComponentData));
2278 isc.registry = registry;
2279 isc.method = method;
2280 isc.send_comps = g_slist_prepend (NULL, g_object_ref (send_comp));
2281 isc.cal_client = cal_client;
2282 isc.zones = zones;
2283 isc.attachments_list = attachments_list;
2284 isc.users = users;
2285 isc.strip_alarms = strip_alarms;
2286 isc.only_new_attendees = only_new_attendees;
2288 isc.completed = FALSE;
2289 isc.success = FALSE;
2291 itip_send_component_begin (&isc, cancellable, error);
2292 itip_send_component_complete (&isc);
2294 g_slist_free_full (isc.send_comps, g_object_unref);
2295 g_slist_free_full (isc.users, g_free);
2297 return isc.success;
2300 static void
2301 itip_send_component_task_thread (GTask *task,
2302 gpointer source_object,
2303 gpointer task_data,
2304 GCancellable *cancellable)
2306 ItipSendComponentData *isc = task_data;
2308 g_return_if_fail (isc != NULL);
2310 itip_send_component_begin (isc, cancellable, &isc->async_error);
2313 void
2314 itip_send_component (ESourceRegistry *registry,
2315 ECalComponentItipMethod method,
2316 ECalComponent *send_comp,
2317 ECalClient *cal_client,
2318 icalcomponent *zones,
2319 GSList *attachments_list,
2320 GSList *users,
2321 gboolean strip_alarms,
2322 gboolean only_new_attendees,
2323 gboolean ensure_master_object,
2324 GCancellable *cancellable,
2325 GAsyncReadyCallback callback,
2326 gpointer user_data)
2328 GTask *task;
2329 ItipSendComponentData *isc;
2331 isc = g_new0 (ItipSendComponentData, 1);
2332 isc->registry = g_object_ref (registry);
2333 isc->method = method;
2334 isc->send_comps = g_slist_prepend (NULL, g_object_ref (send_comp));
2335 isc->cal_client = g_object_ref (cal_client);
2336 if (zones)
2337 isc->zones = icalcomponent_new_clone (zones);
2338 isc->attachments_list = attachments_list;
2339 if (users) {
2340 GSList *link;
2342 isc->users = g_slist_copy (users);
2343 for (link = isc->users; link; link = g_slist_next (link)) {
2344 link->data = g_strdup (link->data);
2347 isc->strip_alarms = strip_alarms;
2348 isc->only_new_attendees = only_new_attendees;
2349 isc->ensure_master_object = ensure_master_object;
2351 isc->completed = FALSE;
2352 isc->success = FALSE;
2354 task = g_task_new (NULL, cancellable, callback, user_data);
2355 g_task_set_task_data (task, isc, itip_send_component_data_free);
2356 g_task_set_source_tag (task, itip_send_component);
2357 g_task_run_in_thread (task, itip_send_component_task_thread);
2358 g_object_unref (task);
2361 gboolean
2362 itip_send_component_finish (GAsyncResult *result,
2363 GError **error)
2365 ItipSendComponentData *isc;
2367 isc = g_task_get_task_data (G_TASK (result));
2369 g_return_val_if_fail (isc != NULL, FALSE);
2370 g_return_val_if_fail (g_async_result_is_tagged (result, itip_send_component), FALSE);
2372 itip_send_component_complete (isc);
2374 if (isc->async_error) {
2375 g_propagate_error (error, isc->async_error);
2376 isc->async_error = NULL;
2379 return isc->success;
2382 gboolean
2383 reply_to_calendar_comp (ESourceRegistry *registry,
2384 ECalComponentItipMethod method,
2385 ECalComponent *send_comp,
2386 ECalClient *cal_client,
2387 gboolean reply_all,
2388 icalcomponent *zones,
2389 GSList *attachments_list)
2391 EShell *shell;
2392 icalcomponent *top_level = NULL;
2393 icaltimezone *default_zone;
2394 gboolean retval = FALSE;
2395 gchar *identity_uid, *identity_name = NULL, *identity_address = NULL;
2396 GSList *ecomps;
2397 CreateComposerData *ccd;
2399 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
2401 /* FIXME Pass this in. */
2402 shell = e_shell_get_default ();
2404 default_zone = calendar_config_get_icaltimezone ();
2406 ecomps = g_slist_prepend (NULL, send_comp);
2408 identity_uid = get_identity_uid_for_from (shell, method, send_comp, cal_client, &identity_name, &identity_address);
2410 /* Tidy up the comp */
2411 if (!comp_compliant (registry, method, ecomps, FALSE, cal_client, zones, default_zone, TRUE)) {
2412 g_free (identity_uid);
2413 g_free (identity_name);
2414 g_free (identity_address);
2415 goto cleanup;
2418 top_level = comp_toplevel_with_zones (method, ecomps, cal_client, zones);
2420 ccd = g_new0 (CreateComposerData, 1);
2421 ccd->identity_uid = identity_uid;
2422 ccd->identity_name = identity_name;
2423 ccd->identity_address = identity_address;
2424 ccd->destinations = comp_to_list (registry, method, ecomps->data, NULL, reply_all, NULL);
2425 ccd->subject = comp_subject (registry, method, ecomps->data);
2426 ccd->ical_string = icalcomponent_as_ical_string_r (top_level);
2427 ccd->send_comps = ecomps;
2428 ccd->show_only = TRUE;
2430 if (e_cal_component_get_vtype (ecomps->data) == E_CAL_COMPONENT_EVENT) {
2431 ECalComponent *comp = ecomps->data;
2432 GString *body;
2433 gchar *orig_from = NULL;
2434 const gchar *description = NULL;
2435 gchar *subject = NULL;
2436 const gchar *location = NULL;
2437 gchar *time = NULL;
2438 gchar *html_description = NULL;
2439 GSList *text_list = NULL;
2440 ECalComponentOrganizer organizer;
2441 ECalComponentText text;
2442 ECalComponentDateTime dtstart;
2443 icaltimezone *start_zone = NULL;
2444 time_t start;
2446 e_cal_component_get_description_list (comp, &text_list);
2448 if (text_list) {
2449 ECalComponentText text = *((ECalComponentText *) text_list->data);
2450 if (text.value)
2451 description = text.value;
2452 else
2453 description = "";
2454 } else {
2455 description = "";
2458 e_cal_component_free_text_list (text_list);
2460 e_cal_component_get_summary (comp, &text);
2461 if (text.value)
2462 subject = g_strdup (text.value);
2464 e_cal_component_get_organizer (comp, &organizer);
2465 if (organizer.value)
2466 orig_from = g_strdup (itip_strip_mailto (organizer.value));
2468 e_cal_component_get_location (comp, &location);
2469 if (!location)
2470 location = "Unspecified";
2472 e_cal_component_get_dtstart (comp, &dtstart);
2473 if (dtstart.value) {
2474 start_zone = icaltimezone_get_builtin_timezone_from_tzid (dtstart.tzid);
2475 if (!start_zone && dtstart.tzid) {
2476 GError *error = NULL;
2478 e_cal_client_get_timezone_sync (
2479 cal_client, dtstart.tzid,
2480 &start_zone, NULL, &error);
2482 if (error != NULL) {
2483 g_warning (
2484 "%s: Couldn't get timezone '%s' from server: %s",
2485 G_STRFUNC,
2486 dtstart.tzid ? dtstart.tzid : "",
2487 error->message);
2488 g_error_free (error);
2492 if (!start_zone || dtstart.value->is_date)
2493 start_zone = default_zone;
2495 start = icaltime_as_timet_with_zone (*dtstart.value, start_zone);
2496 time = g_strdup (ctime (&start));
2499 body = g_string_new (
2500 "<br><br><hr><br><b>"
2501 "______ Original Appointment ______ "
2502 "</b><br><br><table>");
2504 if (orig_from && *orig_from)
2505 g_string_append_printf (
2506 body,
2507 "<tr><td><b>From</b></td>"
2508 "<td>:</td><td>%s</td></tr>", orig_from);
2509 g_free (orig_from);
2511 if (subject)
2512 g_string_append_printf (
2513 body,
2514 "<tr><td><b>Subject</b></td>"
2515 "<td>:</td><td>%s</td></tr>", subject);
2516 g_free (subject);
2518 g_string_append_printf (
2519 body,
2520 "<tr><td><b>Location</b></td>"
2521 "<td>:</td><td>%s</td></tr>", location);
2523 if (time)
2524 g_string_append_printf (
2525 body,
2526 "<tr><td><b>Time</b></td>"
2527 "<td>:</td><td>%s</td></tr>", time);
2528 g_free (time);
2530 g_string_append_printf (body, "</table><br>");
2532 html_description = html_new_lines_for (description);
2533 g_string_append (body, html_description);
2534 g_free (html_description);
2536 ccd->event_body_text = g_string_free (body, FALSE);
2539 e_msg_composer_new (shell, itip_send_component_composer_created_cb, ccd);
2541 retval = TRUE;
2543 cleanup:
2545 if (top_level != NULL)
2546 icalcomponent_free (top_level);
2548 return retval;
2551 gboolean
2552 itip_publish_begin (ECalComponent *pub_comp,
2553 ECalClient *cal_client,
2554 gboolean cloned,
2555 ECalComponent **clone)
2557 icalcomponent *icomp = NULL, *icomp_clone = NULL;
2558 icalproperty *prop;
2560 if (e_cal_component_get_vtype (pub_comp) == E_CAL_COMPONENT_FREEBUSY) {
2562 if (!cloned)
2563 *clone = e_cal_component_clone (pub_comp);
2564 else {
2566 icomp = e_cal_component_get_icalcomponent (pub_comp);
2567 icomp_clone = e_cal_component_get_icalcomponent (*clone);
2568 for (prop = icalcomponent_get_first_property (icomp,
2569 ICAL_FREEBUSY_PROPERTY);
2570 prop != NULL;
2571 prop = icalcomponent_get_next_property (
2572 icomp,
2573 ICAL_FREEBUSY_PROPERTY))
2575 icalproperty *p;
2577 p = icalproperty_new_clone (prop);
2578 icalcomponent_add_property (icomp_clone, p);
2583 return TRUE;
2586 static gboolean
2587 check_time (const struct icaltimetype tmval,
2588 gboolean can_null_time)
2590 if (icaltime_is_null_time (tmval))
2591 return can_null_time;
2593 return icaltime_is_valid_time (tmval) &&
2594 tmval.month >= 1 && tmval.month <= 12 &&
2595 tmval.day >= 1 && tmval.day <= 31 &&
2596 tmval.hour >= 0 && tmval.hour < 24 &&
2597 tmval.minute >= 0 && tmval.minute < 60 &&
2598 tmval.second >= 0 && tmval.second < 60;
2601 /* Returns whether the passed-in icalcomponent is valid or not.
2602 * It does some sanity checks on values too. */
2603 gboolean
2604 is_icalcomp_valid (icalcomponent *icalcomp)
2606 if (!icalcomp || !icalcomponent_is_valid (icalcomp))
2607 return FALSE;
2609 switch (icalcomponent_isa (icalcomp)) {
2610 case ICAL_VEVENT_COMPONENT:
2611 return check_time (icalcomponent_get_dtstart (icalcomp), FALSE) &&
2612 check_time (icalcomponent_get_dtend (icalcomp), TRUE);
2613 case ICAL_VTODO_COMPONENT:
2614 return check_time (icalcomponent_get_dtstart (icalcomp), TRUE) &&
2615 check_time (icalcomponent_get_due (icalcomp), TRUE);
2616 case ICAL_VJOURNAL_COMPONENT:
2617 return check_time (icalcomponent_get_dtstart (icalcomp), TRUE) &&
2618 check_time (icalcomponent_get_dtend (icalcomp), TRUE);
2619 default:
2620 break;
2623 return TRUE;
2626 gboolean
2627 itip_component_has_recipients (ECalComponent *comp)
2629 GSList *attendees = NULL;
2630 ECalComponentAttendee *attendee;
2631 ECalComponentOrganizer organizer;
2632 gboolean res = FALSE;
2634 g_return_val_if_fail (comp != NULL, FALSE);
2636 e_cal_component_get_organizer (comp, &organizer);
2637 e_cal_component_get_attendee_list (comp, &attendees);
2639 if (!attendees) {
2640 if (organizer.value && e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_JOURNAL) {
2641 /* memos store recipients in an extra property */
2642 icalcomponent *icalcomp;
2643 icalproperty *icalprop;
2645 icalcomp = e_cal_component_get_icalcomponent (comp);
2647 for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
2648 icalprop != NULL;
2649 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) {
2650 const gchar *x_name;
2652 x_name = icalproperty_get_x_name (icalprop);
2654 if (g_str_equal (x_name, "X-EVOLUTION-RECIPIENTS")) {
2655 const gchar *str_recipients = icalproperty_get_x (icalprop);
2657 res = str_recipients && g_ascii_strcasecmp (organizer.value, str_recipients) != 0;
2658 break;
2663 return res;
2666 if (g_slist_length (attendees) > 1 || !e_cal_component_has_organizer (comp)) {
2667 e_cal_component_free_attendee_list (attendees);
2668 return TRUE;
2671 attendee = attendees->data;
2673 res = organizer.value && attendee && attendee->value && g_ascii_strcasecmp (organizer.value, attendee->value) != 0;
2675 e_cal_component_free_attendee_list (attendees);
2677 return res;