Updated Traditional Chinese translation(Hong Kong and Taiwan)
[evolution.git] / calendar / gui / itip-utils.c
blob05b5f0a24af3f2f6ef82ac26a3f6bf8395003ff2
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 * Authors:
17 * JP Rosevear <jpr@ximian.com>
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <glib/gi18n.h>
28 #include <libedataserver/e-time-utils.h>
29 #include <gtk/gtk.h>
30 #include <libical/ical.h>
31 #include <e-util/e-dialog-utils.h>
32 #include <libecal/e-cal-time-util.h>
33 #include <libecal/e-cal-util.h>
34 #include <libsoup/soup.h>
35 #include "itip-utils.h"
36 #include <time.h>
37 #include "dialogs/comp-editor-util.h"
39 #include <composer/e-msg-composer.h>
41 static const gchar *itip_methods[] = {
42 "PUBLISH",
43 "REQUEST",
44 "REPLY",
45 "ADD",
46 "CANCEL",
47 "RERESH",
48 "COUNTER",
49 "DECLINECOUNTER"
52 static icalproperty_method itip_methods_enum[] = {
53 ICAL_METHOD_PUBLISH,
54 ICAL_METHOD_REQUEST,
55 ICAL_METHOD_REPLY,
56 ICAL_METHOD_ADD,
57 ICAL_METHOD_CANCEL,
58 ICAL_METHOD_REFRESH,
59 ICAL_METHOD_COUNTER,
60 ICAL_METHOD_DECLINECOUNTER,
63 static EAccountList *accounts = NULL;
65 EAccountList *
66 itip_addresses_get (void)
68 if (accounts == NULL) {
69 GConfClient *gconf_client = gconf_client_get_default ();
70 accounts = e_account_list_new (gconf_client);
71 g_object_unref (gconf_client);
74 return accounts;
77 EAccount *
78 itip_addresses_get_default (void)
80 return (EAccount *)e_account_list_get_default (itip_addresses_get ());
83 gboolean
84 itip_organizer_is_user_ex (ECalComponent *comp, ECal *client, gboolean skip_cap_test)
86 ECalComponentOrganizer organizer;
87 const gchar *strip;
88 gboolean user_org = FALSE;
90 if (!e_cal_component_has_organizer (comp) || (!skip_cap_test && e_cal_get_static_capability (client, CAL_STATIC_CAPABILITY_NO_ORGANIZER)))
91 return FALSE;
93 e_cal_component_get_organizer (comp, &organizer);
94 if (organizer.value != NULL) {
96 strip = itip_strip_mailto (organizer.value);
98 if (e_cal_get_static_capability (client, CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS)) {
99 gchar *email = NULL;
101 if (e_cal_get_cal_address (client, &email, NULL) && !g_ascii_strcasecmp (email, strip)) {
102 g_free (email);
104 return TRUE;
107 g_free (email);
108 return FALSE;
111 user_org = e_account_list_find (itip_addresses_get (), E_ACCOUNT_FIND_ID_ADDRESS, strip) != NULL;
114 return user_org;
117 gboolean
118 itip_organizer_is_user (ECalComponent *comp, ECal *client)
120 return itip_organizer_is_user_ex (comp, client, FALSE);
123 gboolean
124 itip_sentby_is_user (ECalComponent *comp, ECal *client)
126 ECalComponentOrganizer organizer;
127 const gchar *strip;
128 gboolean user_sentby = FALSE;
130 if (!e_cal_component_has_organizer (comp) ||e_cal_get_static_capability (client, CAL_STATIC_CAPABILITY_NO_ORGANIZER))
131 return FALSE;
133 e_cal_component_get_organizer (comp, &organizer);
134 if (organizer.sentby != NULL) {
135 strip = itip_strip_mailto (organizer.sentby);
136 user_sentby = e_account_list_find (itip_addresses_get (), E_ACCOUNT_FIND_ID_ADDRESS, strip) != NULL;
139 return user_sentby;
142 static ECalComponentAttendee *
143 get_attendee (GSList *attendees, gchar *address)
145 GSList *l;
147 if (!address)
148 return NULL;
150 for (l = attendees; l; l = l->next) {
151 ECalComponentAttendee *attendee = l->data;
153 if (!g_ascii_strcasecmp (itip_strip_mailto (attendee->value), address)) {
154 return attendee;
158 return NULL;
161 static ECalComponentAttendee *
162 get_attendee_if_attendee_sentby_is_user (GSList *attendees, gchar *address)
164 GSList *l;
166 for (l = attendees; l; l = l->next) {
167 ECalComponentAttendee *attendee = l->data;
169 if (attendee->sentby && g_str_equal (itip_strip_mailto (attendee->sentby), address)) {
170 return attendee;
174 return NULL;
177 static gchar *
178 html_new_lines_for (const gchar *string)
180 gchar **lines;
181 gchar *joined;
183 lines = g_strsplit_set (string, "\n", -1);
184 joined = g_strjoinv ("<br>", lines);
185 g_strfreev (lines);
187 return joined;
190 gchar *
191 itip_get_comp_attendee (ECalComponent *comp, ECal *client)
193 GSList *attendees;
194 EAccountList *al;
195 EAccount *a;
196 EIterator *it;
197 ECalComponentAttendee *attendee = NULL;
198 gchar *address = NULL;
200 e_cal_component_get_attendee_list (comp, &attendees);
201 al = itip_addresses_get ();
203 if (client)
204 e_cal_get_cal_address (client, &address, NULL);
206 if (address && *address) {
207 attendee = get_attendee (attendees, address);
209 if (attendee) {
210 gchar *user_email = g_strdup (itip_strip_mailto (attendee->value));
212 e_cal_component_free_attendee_list (attendees);
213 g_free (address);
214 return user_email;
217 attendee = get_attendee_if_attendee_sentby_is_user (attendees, address);
219 if (attendee) {
220 gchar *user_email = g_strdup (itip_strip_mailto (attendee->sentby));
222 e_cal_component_free_attendee_list (attendees);
223 g_free (address);
224 return user_email;
227 g_free (address);
228 address = NULL;
231 for (it = e_list_get_iterator ((EList *)al);
232 e_iterator_is_valid (it);
233 e_iterator_next (it)) {
234 a = (EAccount *) e_iterator_get (it);
236 if (!a->enabled)
237 continue;
239 attendee = get_attendee (attendees, a->id->address);
240 if (attendee) {
241 gchar *user_email = g_strdup (itip_strip_mailto (attendee->value));
243 e_cal_component_free_attendee_list (attendees);
244 return user_email;
247 /* If the account was not found in the attendees list, then let's
248 check the 'sentby' fields of the attendees if we can find the account */
249 attendee = get_attendee_if_attendee_sentby_is_user (attendees, a->id->address);
250 if (attendee) {
251 gchar *user_email = g_strdup (itip_strip_mailto (attendee->sentby));
253 e_cal_component_free_attendee_list (attendees);
254 return user_email;
258 /* We could not find the attendee in the component, so just give the default
259 account address if the email address is not set in the backend */
260 /* FIXME do we have a better way ? */
261 a = itip_addresses_get_default ();
262 address = g_strdup ((a != NULL) ? a->id->address : "");
264 e_cal_component_free_attendee_list (attendees);
265 return address;
268 const gchar *
269 itip_strip_mailto (const gchar *address)
271 if (address == NULL)
272 return NULL;
274 if (!g_ascii_strncasecmp (address, "mailto:", 7))
275 address += 7;
277 return address;
280 static gchar *
281 get_label (struct icaltimetype *tt,
282 gboolean use_24_hour_format)
284 gchar buffer[1000];
285 struct tm tmp_tm;
287 tmp_tm = icaltimetype_to_tm (tt);
289 e_time_format_date_and_time (
290 &tmp_tm, use_24_hour_format, FALSE, FALSE, buffer, 1000);
292 return g_strdup (buffer);
295 typedef struct {
296 GHashTable *tzids;
297 icalcomponent *icomp;
298 ECal *client;
299 icalcomponent *zones;
300 } ItipUtilTZData;
302 static void
303 foreach_tzid_callback (icalparameter *param, gpointer data)
305 ItipUtilTZData *tz_data = data;
306 const gchar *tzid;
307 icaltimezone *zone = NULL;
308 icalcomponent *vtimezone_comp;
310 /* Get the TZID string from the parameter. */
311 tzid = icalparameter_get_tzid (param);
312 if (!tzid || g_hash_table_lookup (tz_data->tzids, tzid))
313 return;
315 /* Look for the timezone */
316 if (tz_data->zones != NULL)
317 zone = icalcomponent_get_timezone (tz_data->zones, tzid);
318 if (zone == NULL)
319 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
320 if (zone == NULL && tz_data->client != NULL)
321 e_cal_get_timezone (tz_data->client, tzid, &zone, NULL);
322 if (zone == NULL)
323 return;
325 /* Convert it to a string and add it to the hash. */
326 vtimezone_comp = icaltimezone_get_component (zone);
327 if (!vtimezone_comp)
328 return;
330 icalcomponent_add_component (tz_data->icomp, icalcomponent_new_clone (vtimezone_comp));
331 g_hash_table_insert (tz_data->tzids, (gchar *)tzid, (gchar *)tzid);
334 static icalcomponent *
335 comp_toplevel_with_zones (ECalComponentItipMethod method, ECalComponent *comp, ECal *client, icalcomponent *zones)
337 icalcomponent *top_level, *icomp;
338 icalproperty *prop;
339 icalvalue *value;
340 ItipUtilTZData tz_data;
342 top_level = e_cal_util_new_top_level ();
344 prop = icalproperty_new (ICAL_METHOD_PROPERTY);
345 value = icalvalue_new_method (itip_methods_enum[method]);
346 icalproperty_set_value (prop, value);
347 icalcomponent_add_property (top_level, prop);
349 icomp = e_cal_component_get_icalcomponent (comp);
350 icomp = icalcomponent_new_clone (icomp);
352 tz_data.tzids = g_hash_table_new (g_str_hash, g_str_equal);
353 tz_data.icomp = top_level;
354 tz_data.client = client;
355 tz_data.zones = zones;
356 icalcomponent_foreach_tzid (icomp, foreach_tzid_callback, &tz_data);
357 g_hash_table_destroy (tz_data.tzids);
359 icalcomponent_add_component (top_level, icomp);
361 return top_level;
364 static gboolean
365 users_has_attendee (GList *users, const gchar *address)
367 GList *l;
369 for (l = users; l != NULL; l = l->next) {
370 if (!g_ascii_strcasecmp (address, l->data))
371 return TRUE;
374 return FALSE;
377 static gchar *
378 comp_from (ECalComponentItipMethod method, ECalComponent *comp)
380 ECalComponentOrganizer organizer;
381 ECalComponentAttendee *attendee;
382 GSList *attendees;
383 gchar *from;
384 gchar *sender = NULL;
386 switch (method) {
387 case E_CAL_COMPONENT_METHOD_PUBLISH:
388 return NULL;
390 case E_CAL_COMPONENT_METHOD_REQUEST:
391 return itip_get_comp_attendee (comp, NULL);
393 case E_CAL_COMPONENT_METHOD_REPLY:
394 sender = itip_get_comp_attendee (comp, NULL);
395 if (sender != NULL)
396 return sender;
397 if (!e_cal_component_has_attendees (comp))
398 return NULL;
400 case E_CAL_COMPONENT_METHOD_CANCEL:
402 case E_CAL_COMPONENT_METHOD_ADD:
404 e_cal_component_get_organizer (comp, &organizer);
405 if (organizer.value == NULL) {
406 e_notice (NULL, GTK_MESSAGE_ERROR,
407 _("An organizer must be set."));
408 return NULL;
410 return g_strdup (itip_strip_mailto (organizer.value));
412 default:
413 if (!e_cal_component_has_attendees (comp))
414 return NULL;
416 e_cal_component_get_attendee_list (comp, &attendees);
417 attendee = attendees->data;
418 if (attendee->value != NULL)
419 from = g_strdup (itip_strip_mailto (attendee->value));
420 else
421 from = NULL;
422 e_cal_component_free_attendee_list (attendees);
424 return from;
428 static EDestination **
429 comp_to_list (ECalComponentItipMethod method, ECalComponent *comp, GList *users, gboolean reply_all, const GSList *only_attendees)
431 ECalComponentOrganizer organizer;
432 GSList *attendees, *l;
433 GPtrArray *array = NULL;
434 EDestination *destination;
435 gint len;
436 gchar *sender = NULL;
438 union {
439 gpointer *pdata;
440 EDestination **destinations;
441 } convert;
443 switch (method) {
444 case E_CAL_COMPONENT_METHOD_REQUEST:
445 case E_CAL_COMPONENT_METHOD_CANCEL:
446 e_cal_component_get_attendee_list (comp, &attendees);
447 len = g_slist_length (attendees);
448 if (len <= 0) {
449 e_notice (NULL, GTK_MESSAGE_ERROR,
450 _("At least one attendee is necessary"));
451 e_cal_component_free_attendee_list (attendees);
452 return NULL;
455 e_cal_component_get_organizer (comp, &organizer);
456 if (organizer.value == NULL) {
457 e_notice (NULL, GTK_MESSAGE_ERROR,
458 _("An organizer must be set."));
459 return NULL;
462 array = g_ptr_array_new ();
464 sender = itip_get_comp_attendee (comp, NULL);
466 for (l = attendees; l != NULL; l = l->next) {
467 ECalComponentAttendee *att = l->data;
469 if (att->cutype != ICAL_CUTYPE_INDIVIDUAL && att->cutype != ICAL_CUTYPE_GROUP)
470 continue;
471 else if (users_has_attendee (users, att->value))
472 continue;
473 else if (att->sentby && users_has_attendee (users, att->sentby))
474 continue;
475 else if (!g_ascii_strcasecmp (att->value, organizer.value))
476 continue;
477 else if (att->sentby && !g_ascii_strcasecmp (att->sentby, organizer.sentby))
478 continue;
479 else if (!g_ascii_strcasecmp (itip_strip_mailto (att->value), sender))
480 continue;
481 else if (att->status == ICAL_PARTSTAT_DELEGATED && (att->delto && *att->delto)
482 && !(att->rsvp) && method == E_CAL_COMPONENT_METHOD_REQUEST)
483 continue;
484 else if (only_attendees && !comp_editor_have_in_new_attendees_lst (only_attendees, itip_strip_mailto (att->value)))
485 continue;
487 destination = e_destination_new ();
488 if (att->cn != NULL)
489 e_destination_set_name (destination, att->cn);
490 e_destination_set_email (
491 destination, itip_strip_mailto (att->value));
492 g_ptr_array_add (array, destination);
494 g_free (sender);
495 e_cal_component_free_attendee_list (attendees);
496 break;
498 case E_CAL_COMPONENT_METHOD_REPLY:
500 if (reply_all) {
501 e_cal_component_get_attendee_list (comp, &attendees);
502 len = g_slist_length (attendees);
504 if (len <= 0)
505 return NULL;
507 array = g_ptr_array_new ();
509 e_cal_component_get_organizer (comp, &organizer);
510 sender = itip_get_comp_attendee (comp, NULL);
512 for (l = attendees; l != NULL; l = l->next) {
513 ECalComponentAttendee *att = l->data;
515 if (att->cutype != ICAL_CUTYPE_INDIVIDUAL && att->cutype != ICAL_CUTYPE_GROUP)
516 continue;
517 else if (only_attendees && !comp_editor_have_in_new_attendees_lst (only_attendees, itip_strip_mailto (att->value)))
518 continue;
520 destination = e_destination_new ();
521 if (att->cn != NULL)
522 e_destination_set_name (destination, att->cn);
523 e_destination_set_email (
524 destination, itip_strip_mailto (att->value));
525 g_ptr_array_add (array, destination);
528 g_free (sender);
529 e_cal_component_free_attendee_list (attendees);
531 } else {
532 array = g_ptr_array_new ();
534 destination = e_destination_new ();
535 e_cal_component_get_organizer (comp, &organizer);
536 if (organizer.value)
537 e_destination_set_email (
538 destination, itip_strip_mailto (organizer.value));
539 g_ptr_array_add (array, destination);
541 break;
543 case E_CAL_COMPONENT_METHOD_ADD:
544 case E_CAL_COMPONENT_METHOD_REFRESH:
545 case E_CAL_COMPONENT_METHOD_COUNTER:
546 case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
547 e_cal_component_get_organizer (comp, &organizer);
548 if (organizer.value == NULL) {
549 e_notice (NULL, GTK_MESSAGE_ERROR,
550 _("An organizer must be set."));
551 return NULL;
554 array = g_ptr_array_new ();
556 destination = e_destination_new ();
557 if (organizer.cn != NULL)
558 e_destination_set_name (destination, organizer.cn);
559 e_destination_set_email (
560 destination, itip_strip_mailto (organizer.value));
561 g_ptr_array_add (array, destination);
563 /* send the status to delegatee to the delegate also*/
564 e_cal_component_get_attendee_list (comp, &attendees);
565 sender = itip_get_comp_attendee (comp, NULL);
567 for (l = attendees; l != NULL; l = l->next) {
568 ECalComponentAttendee *att = l->data;
570 if (att->cutype != ICAL_CUTYPE_INDIVIDUAL && att->cutype != ICAL_CUTYPE_GROUP)
571 continue;
573 if (!g_ascii_strcasecmp (itip_strip_mailto (att->value), sender) || (att->sentby && !g_ascii_strcasecmp (itip_strip_mailto (att->sentby), sender))) {
575 if (!(att->delfrom && *att->delfrom))
576 break;
578 destination = e_destination_new ();
579 e_destination_set_email (
580 destination, itip_strip_mailto (att->delfrom));
581 g_ptr_array_add (array, destination);
585 e_cal_component_free_attendee_list (attendees);
587 break;
588 case E_CAL_COMPONENT_METHOD_PUBLISH:
589 if (users) {
590 GList *list;
592 array = g_ptr_array_new ();
594 for (list = users; list != NULL; list = list->next) {
595 destination = e_destination_new ();
596 e_destination_set_email (destination, list->data);
597 g_ptr_array_add (array, destination);
600 break;
602 default:
603 break;
606 if (array == NULL)
607 return NULL;
609 g_ptr_array_add (array, NULL);
610 convert.pdata = g_ptr_array_free (array, FALSE);
612 return convert.destinations;
615 static gchar *
616 comp_subject (ECalComponentItipMethod method, ECalComponent *comp)
618 ECalComponentText caltext;
619 const gchar *description, *prefix = NULL;
620 GSList *alist, *l;
621 gchar *subject;
622 gchar *sender;
623 ECalComponentAttendee *a = NULL;
625 e_cal_component_get_summary (comp, &caltext);
626 if (caltext.value != NULL)
627 description = caltext.value;
628 else {
629 switch (e_cal_component_get_vtype (comp)) {
630 case E_CAL_COMPONENT_EVENT:
631 description = _("Event information");
632 break;
633 case E_CAL_COMPONENT_TODO:
634 description = _("Task information");
635 break;
636 case E_CAL_COMPONENT_JOURNAL:
637 description = _("Memo information");
638 break;
639 case E_CAL_COMPONENT_FREEBUSY:
640 description = _("Free/Busy information");
641 break;
642 default:
643 description = _("Calendar information");
647 switch (method) {
648 case E_CAL_COMPONENT_METHOD_PUBLISH:
649 case E_CAL_COMPONENT_METHOD_REQUEST:
650 /* FIXME: If this is an update to a previous
651 * PUBLISH or REQUEST, then
652 prefix = U_("Updated");
654 break;
656 case E_CAL_COMPONENT_METHOD_REPLY:
657 e_cal_component_get_attendee_list (comp, &alist);
658 sender = itip_get_comp_attendee (comp, NULL);
659 if (sender) {
661 for (l = alist; l != NULL; l = l->next) {
662 a = l->data;
663 if ((sender && *sender) && (g_ascii_strcasecmp (itip_strip_mailto (a->value), sender) || (a->sentby && g_ascii_strcasecmp (itip_strip_mailto (a->sentby), sender))))
664 break;
666 g_free (sender);
669 if (alist != NULL) {
671 switch (a->status) {
672 case ICAL_PARTSTAT_ACCEPTED:
673 /* Translators: This is part of the subject
674 * line of a meeting request or update email.
675 * The full subject line would be:
676 * "Accepted: Meeting Name". */
677 prefix = C_("Meeting", "Accepted");
678 break;
679 case ICAL_PARTSTAT_TENTATIVE:
680 /* Translators: This is part of the subject
681 * line of a meeting request or update email.
682 * The full subject line would be:
683 * "Tentatively Accepted: Meeting Name". */
684 prefix = C_("Meeting", "Tentatively Accepted");
685 break;
686 case ICAL_PARTSTAT_DECLINED:
687 /* Translators: This is part of the subject
688 * line of a meeting request or update email.
689 * The full subject line would be:
690 * "Declined: Meeting Name". */
691 prefix = C_("Meeting", "Declined");
692 break;
693 case ICAL_PARTSTAT_DELEGATED:
694 /* Translators: This is part of the subject
695 * line of a meeting request or update email.
696 * The full subject line would be:
697 * "Delegated: Meeting Name". */
698 prefix = C_("Meeting", "Delegated");
699 break;
700 default:
701 break;
703 e_cal_component_free_attendee_list (alist);
705 break;
707 case E_CAL_COMPONENT_METHOD_ADD:
708 /* Translators: This is part of the subject line of a
709 * meeting request or update email. The full subject
710 * line would be: "Updated: Meeting Name". */
711 prefix = C_("Meeting", "Updated");
712 break;
714 case E_CAL_COMPONENT_METHOD_CANCEL:
715 /* Translators: This is part of the subject line of a
716 * meeting request or update email. The full subject
717 * line would be: "Cancel: Meeting Name". */
718 prefix = C_("Meeting", "Cancel");
719 break;
721 case E_CAL_COMPONENT_METHOD_REFRESH:
722 /* Translators: This is part of the subject line of a
723 * meeting request or update email. The full subject
724 * line would be: "Refresh: Meeting Name". */
725 prefix = C_("Meeting", "Refresh");
726 break;
728 case E_CAL_COMPONENT_METHOD_COUNTER:
729 /* Translators: This is part of the subject line of a
730 * meeting request or update email. The full subject
731 * line would be: "Counter-proposal: Meeting Name". */
732 prefix = C_("Meeting", "Counter-proposal");
733 break;
735 case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
736 /* Translators: This is part of the subject line of a
737 * meeting request or update email. The full subject
738 * line would be: "Declined: Meeting Name". */
739 prefix = C_("Meeting", "Declined");
740 break;
742 default:
743 break;
746 if (prefix != NULL)
747 subject = g_strdup_printf ("%s: %s", prefix, description);
748 else
749 subject = g_strdup (description);
751 return subject;
754 static gchar *
755 comp_content_type (ECalComponent *comp, ECalComponentItipMethod method)
757 return g_strdup_printf (
758 "text/calendar; name=\"%s\"; charset=utf-8; METHOD=%s",
759 e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_FREEBUSY ?
760 "freebusy.ifb" : "calendar.ics", itip_methods[method]);
763 static const gchar *
764 comp_filename (ECalComponent *comp)
766 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_FREEBUSY)
767 return "freebusy.ifb";
768 else
769 return "calendar.ics";
772 static gchar *
773 comp_description (ECalComponent *comp,
774 gboolean use_24_hour_format)
776 gchar *description;
777 ECalComponentDateTime dt;
778 gchar *start = NULL, *end = NULL;
780 switch (e_cal_component_get_vtype (comp)) {
781 case E_CAL_COMPONENT_EVENT:
782 description = g_strdup (_("Event information"));
783 break;
784 case E_CAL_COMPONENT_TODO:
785 description = g_strdup (_("Task information"));
786 break;
787 case E_CAL_COMPONENT_JOURNAL:
788 description = g_strdup (_("Memo information"));
789 break;
790 case E_CAL_COMPONENT_FREEBUSY:
791 e_cal_component_get_dtstart (comp, &dt);
792 if (dt.value)
793 start = get_label (dt.value, use_24_hour_format);
794 e_cal_component_free_datetime (&dt);
796 e_cal_component_get_dtend (comp, &dt);
797 if (dt.value)
798 end = get_label (dt.value, use_24_hour_format);
799 e_cal_component_free_datetime (&dt);
801 if (start != NULL && end != NULL)
802 description = g_strdup_printf (
803 _("Free/Busy information (%s to %s)"),
804 start, end);
805 else
806 description = g_strdup (_("Free/Busy information"));
807 g_free (start);
808 g_free (end);
809 break;
810 default:
811 description = g_strdup (_("iCalendar information"));
812 break;
815 return description;
818 static gboolean
819 comp_server_send (ECalComponentItipMethod method, ECalComponent *comp, ECal *client,
820 icalcomponent *zones, GList **users)
822 icalcomponent *top_level, *returned_icalcomp = NULL;
823 gboolean retval = TRUE;
824 GError *error = NULL;
826 top_level = comp_toplevel_with_zones (method, comp, client, zones);
827 if (!e_cal_send_objects (client, top_level, users, &returned_icalcomp, &error)) {
828 /* FIXME Really need a book problem status code */
829 if (error->code != E_CALENDAR_STATUS_OK) {
830 if (error->code == E_CALENDAR_STATUS_OBJECT_ID_ALREADY_EXISTS) {
831 e_notice (NULL, GTK_MESSAGE_ERROR, _("Unable to book a resource, the new event collides with some other."));
832 } else {
833 gchar *msg = g_strconcat (_("Unable to book a resource, error: "), error->message, NULL);
834 e_notice (NULL, GTK_MESSAGE_ERROR, msg);
835 g_free (msg);
838 retval = FALSE;
842 g_clear_error (&error);
844 if (returned_icalcomp)
845 icalcomponent_free (returned_icalcomp);
846 icalcomponent_free (top_level);
848 return retval;
851 static gboolean
852 comp_limit_attendees (ECalComponent *comp)
854 icalcomponent *icomp;
855 icalproperty *prop;
856 gboolean found = FALSE, match = FALSE;
857 GSList *l, *list = NULL;
859 icomp = e_cal_component_get_icalcomponent (comp);
861 for (prop = icalcomponent_get_first_property (icomp, ICAL_ATTENDEE_PROPERTY);
862 prop != NULL;
863 prop = icalcomponent_get_next_property (icomp, ICAL_ATTENDEE_PROPERTY))
865 gchar *attendee;
866 gchar *attendee_text;
867 icalparameter *param;
868 const gchar *attendee_sentby;
869 gchar *attendee_sentby_text = NULL;
871 /* If we've already found something, just erase the rest */
872 if (found) {
873 list = g_slist_prepend (list, prop);
874 continue;
877 attendee = icalproperty_get_value_as_string_r (prop);
878 if (!attendee)
879 continue;
881 attendee_text = g_strdup (itip_strip_mailto (attendee));
882 g_free (attendee);
883 attendee_text = g_strstrip (attendee_text);
884 found = match = e_account_list_find (itip_addresses_get (), E_ACCOUNT_FIND_ID_ADDRESS, attendee_text) != NULL;
886 if (!found) {
887 param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
888 if (param) {
889 attendee_sentby = icalparameter_get_sentby (param);
890 attendee_sentby_text = g_strdup (itip_strip_mailto (attendee_sentby));
891 attendee_sentby_text = g_strstrip (attendee_sentby_text);
892 found = match = e_account_list_find (itip_addresses_get (), E_ACCOUNT_FIND_ID_ADDRESS, attendee_sentby_text) != NULL;
896 g_free (attendee_text);
897 g_free (attendee_sentby_text);
899 if (!match)
900 list = g_slist_prepend (list, prop);
903 for (l = list; l != NULL; l = l->next) {
904 prop = l->data;
906 icalcomponent_remove_property (icomp, prop);
907 icalproperty_free (prop);
909 g_slist_free (list);
911 return found;
914 static void
915 comp_sentby (ECalComponent *comp, ECal *client)
917 ECalComponentOrganizer organizer;
918 GSList * attendees, *l;
919 gchar *user = NULL;
921 e_cal_component_get_organizer (comp, &organizer);
922 if (!organizer.value) {
923 EAccount *a = itip_addresses_get_default ();
925 organizer.value = g_strdup_printf ("MAILTO:%s", a->id->address);
926 organizer.sentby = NULL;
927 organizer.cn = a->id->name;
928 organizer.language = NULL;
930 e_cal_component_set_organizer (comp, &organizer);
931 g_free ((gchar *) organizer.value);
933 return;
936 e_cal_component_get_attendee_list (comp, &attendees);
937 user = itip_get_comp_attendee (comp, client);
938 for (l = attendees; l; l = l->next) {
939 ECalComponentAttendee *a = l->data;
941 if (!g_ascii_strcasecmp (itip_strip_mailto (a->value), user) || (a->sentby && !g_ascii_strcasecmp (itip_strip_mailto (a->sentby), user))) {
942 g_free (user);
943 return;
947 if (!itip_organizer_is_user (comp, client) && !itip_sentby_is_user (comp, client)) {
948 EAccount *a = itip_addresses_get_default ();
950 organizer.value = g_strdup (organizer.value);
951 organizer.sentby = g_strdup_printf ("MAILTO:%s", a->id->address);
952 organizer.cn = g_strdup (organizer.cn);
953 organizer.language = g_strdup (organizer.language);
955 e_cal_component_set_organizer (comp, &organizer);
957 g_free ((gchar *)organizer.value);
958 g_free ((gchar *)organizer.sentby);
959 g_free ((gchar *)organizer.cn);
960 g_free ((gchar *)organizer.language);
963 static ECalComponent *
964 comp_minimal (ECalComponent *comp, gboolean attendee)
966 ECalComponent *clone;
967 icalcomponent *icomp, *icomp_clone;
968 icalproperty *prop;
969 ECalComponentOrganizer organizer;
970 const gchar *uid;
971 GSList *comments;
972 struct icaltimetype itt;
973 ECalComponentRange recur_id;
975 clone = e_cal_component_new ();
976 e_cal_component_set_new_vtype (clone, e_cal_component_get_vtype (comp));
978 if (attendee) {
979 GSList *attendees;
981 e_cal_component_get_attendee_list (comp, &attendees);
982 e_cal_component_set_attendee_list (clone, attendees);
984 if (!comp_limit_attendees (clone)) {
985 e_notice (NULL, GTK_MESSAGE_ERROR,
986 _("You must be an attendee of the event."));
987 goto error;
991 itt = icaltime_from_timet_with_zone (time (NULL), FALSE,
992 icaltimezone_get_utc_timezone ());
993 e_cal_component_set_dtstamp (clone, &itt);
995 e_cal_component_get_organizer (comp, &organizer);
996 if (organizer.value == NULL)
997 goto error;
998 e_cal_component_set_organizer (clone, &organizer);
1000 e_cal_component_get_uid (comp, &uid);
1001 e_cal_component_set_uid (clone, uid);
1003 e_cal_component_get_comment_list (comp, &comments);
1004 if (g_slist_length (comments) <= 1) {
1005 e_cal_component_set_comment_list (clone, comments);
1006 } else {
1007 GSList *l = comments;
1009 comments = g_slist_remove_link (comments, l);
1010 e_cal_component_set_comment_list (clone, l);
1011 e_cal_component_free_text_list (l);
1013 e_cal_component_free_text_list (comments);
1015 e_cal_component_get_recurid (comp, &recur_id);
1016 if (recur_id.datetime.value != NULL)
1017 e_cal_component_set_recurid (clone, &recur_id);
1019 icomp = e_cal_component_get_icalcomponent (comp);
1020 icomp_clone = e_cal_component_get_icalcomponent (clone);
1021 for (prop = icalcomponent_get_first_property (icomp, ICAL_X_PROPERTY);
1022 prop != NULL;
1023 prop = icalcomponent_get_next_property (icomp, ICAL_X_PROPERTY))
1025 icalproperty *p;
1027 p = icalproperty_new_clone (prop);
1028 icalcomponent_add_property (icomp_clone, p);
1031 e_cal_component_rescan (clone);
1033 return clone;
1035 error:
1036 g_object_unref (clone);
1037 return NULL;
1040 static void
1041 strip_x_microsoft_props (ECalComponent *comp)
1043 GSList *lst = NULL, *l;
1044 icalcomponent *icalcomp;
1045 icalproperty *icalprop;
1047 g_return_if_fail (comp != NULL);
1049 icalcomp = e_cal_component_get_icalcomponent (comp);
1050 g_return_if_fail (icalcomp != NULL);
1052 for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
1053 icalprop;
1054 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) {
1055 const gchar *x_name = icalproperty_get_x_name (icalprop);
1057 if (x_name && g_ascii_strncasecmp (x_name, "X-MICROSOFT-", 12) == 0)
1058 lst = g_slist_prepend (lst, icalprop);
1061 for (l = lst; l != NULL; l = l->next) {
1062 icalprop = l->data;
1063 icalcomponent_remove_property (icalcomp, icalprop);
1064 icalproperty_free (icalprop);
1067 g_slist_free (lst);
1070 static ECalComponent *
1071 comp_compliant (ECalComponentItipMethod method,
1072 ECalComponent *comp,
1073 ECal *client,
1074 icalcomponent *zones,
1075 icaltimezone *default_zone,
1076 gboolean strip_alarms)
1078 ECalComponent *clone, *temp_clone;
1079 struct icaltimetype itt;
1081 clone = e_cal_component_clone (comp);
1082 itt = icaltime_from_timet_with_zone (time (NULL), FALSE,
1083 icaltimezone_get_utc_timezone ());
1084 e_cal_component_set_dtstamp (clone, &itt);
1086 /* Make UNTIL date a datetime in a simple recurrence */
1087 if (e_cal_component_has_recurrences (clone)
1088 && e_cal_component_has_simple_recurrence (clone)) {
1089 GSList *rrule_list;
1090 struct icalrecurrencetype *r;
1092 e_cal_component_get_rrule_list (clone, &rrule_list);
1093 r = rrule_list->data;
1095 if (!icaltime_is_null_time (r->until) && r->until.is_date) {
1096 ECalComponentDateTime dt;
1097 icaltimezone *from_zone = NULL, *to_zone;
1099 e_cal_component_get_dtstart (clone, &dt);
1101 if (dt.value->is_date) {
1102 from_zone = default_zone;
1103 } else if (dt.tzid == NULL) {
1104 from_zone = icaltimezone_get_utc_timezone ();
1105 } else {
1106 if (zones != NULL)
1107 from_zone = icalcomponent_get_timezone (zones, dt.tzid);
1108 if (from_zone == NULL)
1109 from_zone = icaltimezone_get_builtin_timezone_from_tzid (dt.tzid);
1110 if (from_zone == NULL && client != NULL)
1111 /* FIXME Error checking */
1112 e_cal_get_timezone (client, dt.tzid, &from_zone, NULL);
1115 to_zone = icaltimezone_get_utc_timezone ();
1117 r->until.hour = dt.value->hour;
1118 r->until.minute = dt.value->minute;
1119 r->until.second = dt.value->second;
1120 r->until.is_date = FALSE;
1122 icaltimezone_convert_time (&r->until, from_zone, to_zone);
1123 r->until.is_utc = TRUE;
1125 e_cal_component_free_datetime (&dt);
1126 e_cal_component_set_rrule_list (clone, rrule_list);
1127 e_cal_component_abort_sequence (clone);
1130 e_cal_component_free_recur_list (rrule_list);
1133 /* We delete incoming alarms if requested, even this helps with outlook */
1134 if (strip_alarms) {
1135 e_cal_component_remove_all_alarms (clone);
1136 } else {
1137 /* Always strip procedure alarms, because of security */
1138 GList *uids, *l;
1140 uids = e_cal_component_get_alarm_uids (clone);
1142 for (l = uids; l; l = l->next) {
1143 ECalComponentAlarm *alarm;
1144 ECalComponentAlarmAction action = E_CAL_COMPONENT_ALARM_UNKNOWN;
1146 alarm = e_cal_component_get_alarm (clone, (const gchar *)l->data);
1147 if (alarm) {
1148 e_cal_component_alarm_get_action (alarm, &action);
1149 e_cal_component_alarm_free (alarm);
1151 if (action == E_CAL_COMPONENT_ALARM_PROCEDURE)
1152 e_cal_component_remove_alarm (clone, (const gchar *)l->data);
1156 cal_obj_uid_list_free (uids);
1159 strip_x_microsoft_props (clone);
1161 /* Strip X-LIC-ERROR stuff */
1162 e_cal_component_strip_errors (clone);
1164 /* Comply with itip spec */
1165 switch (method) {
1166 case E_CAL_COMPONENT_METHOD_PUBLISH:
1167 comp_sentby (clone, client);
1168 e_cal_component_set_attendee_list (clone, NULL);
1169 break;
1170 case E_CAL_COMPONENT_METHOD_REQUEST:
1171 comp_sentby (clone, client);
1172 break;
1173 case E_CAL_COMPONENT_METHOD_CANCEL:
1174 comp_sentby (clone, client);
1175 break;
1176 case E_CAL_COMPONENT_METHOD_REPLY:
1177 break;
1178 case E_CAL_COMPONENT_METHOD_ADD:
1179 break;
1180 case E_CAL_COMPONENT_METHOD_REFRESH:
1181 /* Need to remove almost everything */
1182 temp_clone = comp_minimal (clone, TRUE);
1183 g_object_unref (clone);
1184 clone = temp_clone;
1185 break;
1186 case E_CAL_COMPONENT_METHOD_COUNTER:
1187 break;
1188 case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
1189 /* Need to remove almost everything */
1190 temp_clone = comp_minimal (clone, FALSE);
1191 g_object_unref (clone);
1192 clone = temp_clone;
1193 break;
1194 default:
1195 break;
1198 return clone;
1201 static void
1202 append_cal_attachments (EMsgComposer *composer,
1203 ECalComponent *comp,
1204 GSList *attach_list)
1206 struct CalMimeAttach *mime_attach;
1207 GSList *l;
1209 for (l = attach_list; l; l = l->next) {
1210 CamelMimePart *attachment;
1212 mime_attach = (struct CalMimeAttach *) l->data;
1214 attachment = camel_mime_part_new ();
1215 camel_mime_part_set_content (
1216 attachment, mime_attach->encoded_data,
1217 mime_attach->length, mime_attach->content_type);
1218 if (mime_attach->content_id)
1219 camel_mime_part_set_content_id (attachment, mime_attach->content_id);
1220 if (mime_attach->filename != NULL)
1221 camel_mime_part_set_filename (
1222 attachment, mime_attach->filename);
1223 if (mime_attach->description != NULL)
1224 camel_mime_part_set_description (
1225 attachment, mime_attach->description);
1226 if (mime_attach->disposition)
1227 camel_mime_part_set_disposition (
1228 attachment, "inline");
1229 else
1230 camel_mime_part_set_disposition (
1231 attachment, "attachment");
1232 e_msg_composer_attach (composer, attachment);
1233 g_object_unref (attachment);
1235 g_free (mime_attach->filename);
1236 g_free (mime_attach->content_type);
1237 g_free (mime_attach->content_id);
1238 g_free (mime_attach->description);
1239 g_free (mime_attach->encoded_data);
1240 g_free (mime_attach);
1243 g_slist_free (attach_list);
1246 static EAccount *
1247 find_enabled_account (EAccountList *accounts, const gchar *id_address)
1249 EIterator *it;
1250 EAccount *account = NULL;
1252 g_return_val_if_fail (accounts != NULL, NULL);
1254 if (!id_address)
1255 return NULL;
1257 for (it = e_list_get_iterator ((EList *)accounts);
1258 e_iterator_is_valid (it);
1259 e_iterator_next (it)) {
1260 account = (EAccount *)e_iterator_get (it);
1262 if (account
1263 && account->enabled
1264 && account->id
1265 && account->id->address
1266 && g_ascii_strcasecmp (account->id->address, id_address) == 0)
1267 break;
1269 account = NULL;
1272 return account;
1275 static void
1276 setup_from (ECalComponentItipMethod method, ECalComponent *comp, ECal *client, EComposerHeaderTable *table)
1278 EAccountList *accounts;
1280 accounts = e_composer_header_table_get_account_list (table);
1281 if (accounts) {
1282 EAccount *account = NULL;
1284 /* always use organizer's email when user is an organizer */
1285 if (itip_organizer_is_user (comp, client)) {
1286 ECalComponentOrganizer organizer = {0};
1288 e_cal_component_get_organizer (comp, &organizer);
1289 if (organizer.value != NULL) {
1290 account = find_enabled_account (accounts, itip_strip_mailto (organizer.value));
1294 if (!account) {
1295 gchar *from = comp_from (method, comp);
1297 if (from)
1298 account = find_enabled_account (accounts, from);
1300 g_free (from);
1303 if (account)
1304 e_composer_header_table_set_account (table, account);
1308 gboolean
1309 itip_send_comp (ECalComponentItipMethod method,
1310 ECalComponent *send_comp,
1311 ECal *client,
1312 icalcomponent *zones,
1313 GSList *attachments_list,
1314 GList *users,
1315 gboolean strip_alarms,
1316 gboolean only_new_attendees)
1318 EShell *shell;
1319 EShellSettings *shell_settings;
1320 EMsgComposer *composer;
1321 EComposerHeaderTable *table;
1322 EDestination **destinations;
1323 ECalComponent *comp = NULL;
1324 icalcomponent *top_level = NULL;
1325 icaltimezone *default_zone;
1326 gchar *ical_string = NULL;
1327 gchar *content_type = NULL;
1328 gchar *subject = NULL;
1329 gboolean use_24_hour_format;
1330 gboolean retval = FALSE;
1332 /* FIXME Pass this in. */
1333 shell = e_shell_get_default ();
1334 shell_settings = e_shell_get_shell_settings (shell);
1336 default_zone = e_shell_settings_get_pointer (
1337 shell_settings, "cal-timezone");
1339 use_24_hour_format = e_shell_settings_get_boolean (
1340 shell_settings, "cal-use-24-hour-format");
1342 /* check whether backend could handle auto-saving requests/updates */
1343 if (method != E_CAL_COMPONENT_METHOD_PUBLISH && e_cal_get_save_schedules (client))
1344 return TRUE;
1346 /* Give the server a chance to manipulate the comp */
1347 if (method != E_CAL_COMPONENT_METHOD_PUBLISH) {
1348 if (!comp_server_send (method, send_comp, client, zones, &users))
1349 goto cleanup;
1352 /* check whether backend could handle sending requests/updates */
1353 if (method != E_CAL_COMPONENT_METHOD_PUBLISH && e_cal_get_static_capability (client, CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) {
1354 if (users) {
1355 g_list_foreach (users, (GFunc) g_free, NULL);
1356 g_list_free (users);
1358 return TRUE;
1361 /* Tidy up the comp */
1362 comp = comp_compliant (
1363 method, send_comp, client, zones, default_zone, strip_alarms);
1365 if (comp == NULL)
1366 goto cleanup;
1368 /* Recipients */
1369 destinations = comp_to_list (method, comp, users, FALSE, only_new_attendees ? g_object_get_data (G_OBJECT (send_comp), "new-attendees") : NULL);
1370 if (method != E_CAL_COMPONENT_METHOD_PUBLISH) {
1371 if (destinations == NULL) {
1372 /* We sent them all via the server */
1373 retval = TRUE;
1374 goto cleanup;
1378 /* Subject information */
1379 subject = comp_subject (method, comp);
1381 composer = e_msg_composer_new (shell);
1382 table = e_msg_composer_get_header_table (composer);
1384 setup_from (method, send_comp, client, table);
1385 e_composer_header_table_set_subject (table, subject);
1386 e_composer_header_table_set_destinations_to (table, destinations);
1388 e_destination_freev (destinations);
1390 /* Content type */
1391 content_type = comp_content_type (comp, method);
1393 top_level = comp_toplevel_with_zones (method, comp, client, zones);
1394 ical_string = icalcomponent_as_ical_string_r (top_level);
1396 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_EVENT) {
1397 e_msg_composer_set_body (composer, ical_string, content_type);
1398 } else {
1399 CamelMimePart *attachment;
1400 const gchar *filename;
1401 gchar *description;
1402 gchar *body;
1404 filename = comp_filename (comp);
1405 description = comp_description (comp, use_24_hour_format);
1407 body = camel_text_to_html (
1408 description, CAMEL_MIME_FILTER_TOHTML_PRE, 0);
1409 e_msg_composer_set_body_text (composer, body, -1);
1410 g_free (body);
1412 attachment = camel_mime_part_new ();
1413 camel_mime_part_set_content (
1414 attachment, ical_string,
1415 strlen (ical_string), content_type);
1416 if (filename != NULL && *filename != '\0')
1417 camel_mime_part_set_filename (attachment, filename);
1418 if (description != NULL && *description != '\0')
1419 camel_mime_part_set_description (attachment, description);
1420 camel_mime_part_set_disposition (attachment, "inline");
1421 e_msg_composer_attach (composer, attachment);
1422 g_object_unref (attachment);
1424 g_free (description);
1427 append_cal_attachments (composer, comp, attachments_list);
1429 if ((method == E_CAL_COMPONENT_METHOD_PUBLISH) && !users)
1430 gtk_widget_show (GTK_WIDGET (composer));
1431 else
1432 e_msg_composer_send (composer);
1434 retval = TRUE;
1436 cleanup:
1437 if (comp != NULL)
1438 g_object_unref (comp);
1439 if (top_level != NULL)
1440 icalcomponent_free (top_level);
1442 if (users) {
1443 g_list_foreach (users, (GFunc) g_free, NULL);
1444 g_list_free (users);
1447 g_free (content_type);
1448 g_free (subject);
1449 g_free (ical_string);
1451 return retval;
1454 gboolean
1455 reply_to_calendar_comp (ECalComponentItipMethod method,
1456 ECalComponent *send_comp,
1457 ECal *client,
1458 gboolean reply_all,
1459 icalcomponent *zones,
1460 GSList *attachments_list)
1462 EShell *shell;
1463 EShellSettings *shell_settings;
1464 EMsgComposer *composer;
1465 EComposerHeaderTable *table;
1466 EDestination **destinations;
1467 ECalComponent *comp = NULL;
1468 icalcomponent *top_level = NULL;
1469 icaltimezone *default_zone;
1470 GList *users = NULL;
1471 gchar *subject = NULL;
1472 gchar *ical_string = NULL;
1473 gboolean retval = FALSE;
1475 /* FIXME Pass this in. */
1476 shell = e_shell_get_default ();
1477 shell_settings = e_shell_get_shell_settings (shell);
1479 default_zone = e_shell_settings_get_pointer (
1480 shell_settings, "cal-timezone");
1482 /* Tidy up the comp */
1483 comp = comp_compliant (
1484 method, send_comp, client, zones, default_zone, TRUE);
1485 if (comp == NULL)
1486 goto cleanup;
1488 /* Recipients */
1489 destinations = comp_to_list (method, comp, users, reply_all, NULL);
1491 /* Subject information */
1492 subject = comp_subject (method, comp);
1494 composer = e_msg_composer_new (shell);
1495 table = e_msg_composer_get_header_table (composer);
1497 setup_from (method, send_comp, client, table);
1498 e_composer_header_table_set_subject (table, subject);
1499 e_composer_header_table_set_destinations_to (table, destinations);
1501 e_destination_freev (destinations);
1503 top_level = comp_toplevel_with_zones (method, comp, client, zones);
1504 ical_string = icalcomponent_as_ical_string_r (top_level);
1506 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_EVENT) {
1508 GString *body;
1509 gchar *orig_from = NULL;
1510 const gchar *description = NULL;
1511 gchar *subject = NULL;
1512 const gchar *location = NULL;
1513 gchar *time = NULL;
1514 gchar *html_description = NULL;
1515 GSList *text_list = NULL;
1516 ECalComponentOrganizer organizer;
1517 ECalComponentText text;
1518 ECalComponentDateTime dtstart;
1519 icaltimezone *start_zone = NULL;
1520 time_t start;
1522 e_cal_component_get_description_list (comp, &text_list);
1524 if (text_list) {
1525 ECalComponentText text = *((ECalComponentText *)text_list->data);
1526 if (text.value)
1527 description = text.value;
1528 else
1529 description = "";
1530 } else {
1531 description = "";
1534 e_cal_component_free_text_list (text_list);
1536 e_cal_component_get_summary (comp, &text);
1537 if (text.value)
1538 subject = g_strdup (text.value);
1540 e_cal_component_get_organizer (comp, &organizer);
1541 if (organizer.value)
1542 orig_from = g_strdup (itip_strip_mailto (organizer.value));
1544 e_cal_component_get_location (comp, &location);
1545 if (!location)
1546 location = "Unspecified";
1548 e_cal_component_get_dtstart (comp, &dtstart);
1549 if (dtstart.value) {
1550 start_zone = icaltimezone_get_builtin_timezone_from_tzid (dtstart.tzid);
1551 if (!start_zone) {
1552 if (!e_cal_get_timezone (client, dtstart.tzid, &start_zone, NULL))
1553 g_warning ("Couldn't get timezone from server: %s", dtstart.tzid ? dtstart.tzid : "");
1556 if (!start_zone || dtstart.value->is_date)
1557 start_zone = default_zone;
1559 start = icaltime_as_timet_with_zone (*dtstart.value, start_zone);
1560 time = g_strdup (ctime (&start));
1563 body = g_string_new ("<br><br><hr><br><b>______ Original Appointment ______ </b><br><br><table>");
1565 if (orig_from && *orig_from)
1566 g_string_append_printf (body,
1567 "<tr><td><b>From</b></td>"
1568 "<td>:</td><td>%s</td></tr>", orig_from);
1569 g_free (orig_from);
1571 if (subject)
1572 g_string_append_printf (body,
1573 "<tr><td><b>Subject</b></td>"
1574 "<td>:</td><td>%s</td></tr>", subject);
1575 g_free (subject);
1577 g_string_append_printf (body,
1578 "<tr><td><b>Location</b></td>"
1579 "<td>:</td><td>%s</td></tr>", location);
1581 if (time)
1582 g_string_append_printf (body,
1583 "<tr><td><b>Time</b></td>"
1584 "<td>:</td><td>%s</td></tr>", time);
1585 g_free (time);
1587 g_string_append_printf (body, "</table><br>");
1589 html_description = html_new_lines_for (description);
1590 g_string_append (body, html_description);
1591 g_free (html_description);
1593 e_msg_composer_set_body_text (composer, body->str, -1);
1594 g_string_free (body, TRUE);
1597 gtk_widget_show (GTK_WIDGET (composer));
1599 retval = TRUE;
1601 cleanup:
1603 if (comp != NULL)
1604 g_object_unref (comp);
1605 if (top_level != NULL)
1606 icalcomponent_free (top_level);
1608 if (users) {
1609 g_list_foreach (users, (GFunc) g_free, NULL);
1610 g_list_free (users);
1613 g_free (subject);
1614 g_free (ical_string);
1615 return retval;
1618 gboolean
1619 itip_publish_begin (ECalComponent *pub_comp, ECal *client,
1620 gboolean cloned, ECalComponent **clone)
1622 icalcomponent *icomp =NULL, *icomp_clone = NULL;
1623 icalproperty *prop;
1625 if (e_cal_component_get_vtype (pub_comp) == E_CAL_COMPONENT_FREEBUSY) {
1627 if (!cloned)
1628 *clone = e_cal_component_clone (pub_comp);
1629 else {
1631 icomp = e_cal_component_get_icalcomponent (pub_comp);
1632 icomp_clone = e_cal_component_get_icalcomponent (*clone);
1633 for (prop = icalcomponent_get_first_property (icomp,
1634 ICAL_FREEBUSY_PROPERTY);
1635 prop != NULL;
1636 prop = icalcomponent_get_next_property (icomp,
1637 ICAL_FREEBUSY_PROPERTY))
1639 icalproperty *p;
1641 p = icalproperty_new_clone (prop);
1642 icalcomponent_add_property (icomp_clone, p);
1647 return TRUE;
1650 static void
1651 fb_sort (struct icalperiodtype *ipt, gint fb_count)
1653 gint i,j;
1655 if (ipt == NULL || fb_count == 0)
1656 return;
1658 for (i = 0; i < fb_count-1; i++) {
1659 for (j = i+1; j < fb_count; j++) {
1660 struct icalperiodtype temp;
1662 if (icaltime_compare (ipt[i].start, ipt[j].start) < 0)
1663 continue;
1665 if (icaltime_compare (ipt[i].start, ipt[j].start) == 0) {
1666 if (icaltime_compare (ipt[i].end,
1667 ipt[j].start) < 0)
1668 continue;
1670 temp = ipt[i];
1671 ipt[i] = ipt[j];
1672 ipt[j] = temp;
1677 static icalcomponent *
1678 comp_fb_normalize (icalcomponent *icomp)
1680 icalcomponent *iclone;
1681 icalproperty *prop, *p;
1682 const gchar *uid, *comment;
1683 struct icaltimetype itt;
1684 gint fb_count, i = 0, j;
1685 struct icalperiodtype *ipt;
1687 iclone = icalcomponent_new (ICAL_VFREEBUSY_COMPONENT);
1689 prop = icalcomponent_get_first_property (icomp,
1690 ICAL_ORGANIZER_PROPERTY);
1691 if (prop) {
1692 p = icalproperty_new_clone (prop);
1693 icalcomponent_add_property (iclone, p);
1696 itt = icalcomponent_get_dtstart (icomp);
1697 icalcomponent_set_dtstart (iclone, itt);
1699 itt = icalcomponent_get_dtend (icomp);
1700 icalcomponent_set_dtend (iclone, itt);
1702 fb_count = icalcomponent_count_properties (icomp,
1703 ICAL_FREEBUSY_PROPERTY);
1704 ipt = g_new0 (struct icalperiodtype, fb_count+1);
1706 for (prop = icalcomponent_get_first_property (icomp,
1707 ICAL_FREEBUSY_PROPERTY);
1708 prop != NULL;
1709 prop = icalcomponent_get_next_property (icomp,
1710 ICAL_FREEBUSY_PROPERTY))
1712 ipt[i] = icalproperty_get_freebusy (prop);
1713 i++;
1716 fb_sort (ipt, fb_count);
1718 for (j = 0; j <= fb_count-1; j++) {
1719 icalparameter *param;
1721 prop = icalproperty_new_freebusy (ipt[j]);
1722 param = icalparameter_new_fbtype (ICAL_FBTYPE_BUSY);
1723 icalproperty_add_parameter (prop, param);
1724 icalcomponent_add_property (iclone, prop);
1726 g_free (ipt);
1728 /* Should I strip this RFC 2446 says there must not be a UID
1729 if the METHOD is PUBLISH?? */
1730 uid = icalcomponent_get_uid (icomp);
1731 if (uid)
1732 icalcomponent_set_uid (iclone, uid);
1734 itt = icaltime_from_timet_with_zone (time (NULL), FALSE,
1735 icaltimezone_get_utc_timezone ());
1736 icalcomponent_set_dtstamp (iclone, itt);
1738 prop = icalcomponent_get_first_property (icomp, ICAL_URL_PROPERTY);
1739 if (prop) {
1740 p = icalproperty_new_clone (prop);
1741 icalcomponent_add_property (iclone, p);
1744 comment = icalcomponent_get_comment (icomp);
1745 if (comment)
1746 icalcomponent_set_comment (iclone, comment);
1748 for (prop = icalcomponent_get_first_property (icomp, ICAL_X_PROPERTY);
1749 prop != NULL;
1750 prop = icalcomponent_get_next_property (icomp, ICAL_X_PROPERTY))
1752 p = icalproperty_new_clone (prop);
1753 icalcomponent_add_property (iclone, p);
1756 return iclone;
1757 /* this will never be reached */
1758 g_object_unref (iclone);
1759 return NULL;
1762 gboolean
1763 itip_publish_comp (ECal *client, gchar *uri, gchar *username,
1764 gchar *password, ECalComponent **pub_comp)
1766 icalcomponent *toplevel = NULL, *icalcomp = NULL;
1767 icalcomponent *icomp = NULL;
1768 SoupSession *session;
1769 SoupMessage *msg;
1770 SoupURI *real_uri;
1771 gchar *ical_string = NULL;
1773 toplevel = e_cal_util_new_top_level ();
1774 icalcomponent_set_method (toplevel, ICAL_METHOD_PUBLISH);
1776 e_cal_component_set_url (*pub_comp, uri);
1778 icalcomp = e_cal_component_get_icalcomponent (*pub_comp);
1780 icomp = comp_fb_normalize (icalcomp);
1782 icalcomponent_add_component (toplevel, icomp);
1784 /* Publish the component */
1785 session = soup_session_async_new ();
1787 real_uri = soup_uri_new (uri);
1788 if (!real_uri || !real_uri->host) {
1789 g_warning (G_STRLOC ": Invalid URL: %s", uri);
1790 g_object_unref (session);
1791 return FALSE;
1794 soup_uri_set_user (real_uri, username);
1795 soup_uri_set_password (real_uri, password);
1797 /* build the message */
1798 msg = soup_message_new_from_uri (SOUP_METHOD_PUT, real_uri);
1799 soup_uri_free (real_uri);
1800 if (!msg) {
1801 g_warning (G_STRLOC ": Could not build SOAP message");
1802 g_object_unref (session);
1803 return FALSE;
1806 soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
1807 ical_string = icalcomponent_as_ical_string_r (toplevel);
1808 soup_message_set_request (msg, "text/calendar", SOUP_MEMORY_TEMPORARY,
1809 ical_string, strlen (ical_string));
1811 /* send message to server */
1812 soup_session_send_message (session, msg);
1813 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
1814 g_warning(G_STRLOC ": Could not publish Free/Busy: %d: %s",
1815 msg->status_code,
1816 msg->reason_phrase);
1817 g_object_unref (msg);
1818 g_object_unref (session);
1819 g_free (ical_string);
1820 return FALSE;
1823 g_object_unref (msg);
1824 g_object_unref (session);
1825 g_free (ical_string);
1827 return TRUE;
1830 static gboolean
1831 check_time (const struct icaltimetype tmval, gboolean can_null_time)
1833 if (icaltime_is_null_time (tmval))
1834 return can_null_time;
1836 return icaltime_is_valid_time (tmval) &&
1837 tmval.month >= 1 && tmval.month <= 12 &&
1838 tmval.day >= 1 && tmval.day <= 31 &&
1839 tmval.hour >= 0 && tmval.hour < 24 &&
1840 tmval.minute >= 0 && tmval.minute < 60 &&
1841 tmval.second >= 0 && tmval.second < 60;
1844 /* returns whether the passed-in icalcomponent is valid or not. It does some sanity checks on values too. */
1845 gboolean
1846 is_icalcomp_valid (icalcomponent *icalcomp)
1848 return icalcomp &&
1849 icalcomponent_is_valid (icalcomp) &&
1850 check_time (icalcomponent_get_dtstart (icalcomp), FALSE) &&
1851 check_time (icalcomponent_get_dtend (icalcomp), TRUE);