No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * JP Rosevear <jpr@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <time.h>
28 #include <glib/gi18n.h>
29 #include <libical/ical.h>
30 #include <libsoup/soup.h>
31
32 #include <e-util/e-dialog-utils.h>
33
34 #include <composer/e-msg-composer.h>
35
36 #include "itip-utils.h"
37 #include "dialogs/comp-editor-util.h"
38
39 #define d(x)
40
41 static const gchar *itip_methods[] = {
42 "PUBLISH",
43 "REQUEST",
44 "REPLY",
45 "ADD",
46 "CANCEL",
47 "RERESH",
48 "COUNTER",
49 "DECLINECOUNTER"
50 };
51
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,
61 };
62
63 /**
64 * itip_get_default_name_and_address:
65 * @registry: an #ESourceRegistry
66 * @name: return location for the user's real name, or %NULL
67 * @address: return location for the user's email address, or %NULL
68 *
69 * Returns the real name and email address of the default mail identity,
70 * if available. If no default mail identity is available, @name and
71 * @address are set to %NULL and the function returns %FALSE.
72 *
73 * Returns: %TRUE if @name and/or @address were set
74 **/
75 gboolean
76 itip_get_default_name_and_address (ESourceRegistry *registry,
77 gchar **name,
78 gchar **address)
79 {
80 ESource *source;
81 ESourceExtension *extension;
82 const gchar *extension_name;
83 gboolean success;
84
85 source = e_source_registry_ref_default_mail_identity (registry);
86
87 if (source != NULL) {
88 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
89 extension = e_source_get_extension (source, extension_name);
90
91 if (name != NULL)
92 *name = e_source_mail_identity_dup_name (
93 E_SOURCE_MAIL_IDENTITY (extension));
94
95 if (address != NULL)
96 *address = e_source_mail_identity_dup_address (
97 E_SOURCE_MAIL_IDENTITY (extension));
98
99 g_object_unref (source);
100
101 success = TRUE;
102
103 } else {
104 if (name != NULL)
105 *name = NULL;
106
107 if (address != NULL)
108 *address = NULL;
109
110 success = FALSE;
111 }
112
113 return success;
114 }
115
116 /**
117 * itip_get_user_identities:
118 * @registry: an #ESourceRegistry
119 *
120 * Returns a %NULL-terminated array of name + address strings based on
121 * registered mail identities. Free the returned array with g_strfreev().
122 *
123 * Returns: an %NULL-terminated array of mail identity strings
124 **/
125 gchar **
126 itip_get_user_identities (ESourceRegistry *registry)
127 {
128 GList *list, *iter;
129 const gchar *extension_name;
130 gchar **identities;
131 guint ii = 0;
132
133 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
134
135 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
136
137 list = e_source_registry_list_sources (registry, extension_name);
138
139 identities = g_new0 (gchar *, g_list_length (list) + 1);
140
141 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
142 ESource *source = E_SOURCE (iter->data);
143 ESourceMailIdentity *extension;
144 const gchar *name, *address;
145
146 extension = e_source_get_extension (source, extension_name);
147
148 name = e_source_mail_identity_get_name (extension);
149 address = e_source_mail_identity_get_address (extension);
150
151 if (name == NULL || address == NULL)
152 continue;
153
154 identities[ii++] = g_strdup_printf ("%s <%s>", name, address);
155 }
156
157 g_list_free_full (list, (GDestroyNotify) g_object_unref);
158
159 return identities;
160 }
161
162 /**
163 * itip_get_fallback_identity:
164 * @registry: an #ESourceRegistry
165 *
166 * Returns a name + address string taken from the default mail identity,
167 * but only if the corresponding account is enabled. If the account is
168 * disabled, the function returns %NULL. This is meant to be used as a
169 * fallback identity for organizers. Free the returned string with
170 * g_free().
171 *
172 * Returns: a fallback mail identity, or %NULL
173 **/
174 gchar *
175 itip_get_fallback_identity (ESourceRegistry *registry)
176 {
177 ESource *source;
178 ESourceMailIdentity *mail_identity;
179 const gchar *extension_name;
180 const gchar *address;
181 const gchar *name;
182 gchar *identity = NULL;
183
184 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
185
186 source = e_source_registry_ref_default_mail_identity (registry);
187
188 if (source == NULL)
189 return NULL;
190
191 if (!e_source_get_enabled (source)) {
192 g_object_unref (source);
193 return NULL;
194 }
195
196 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
197 mail_identity = e_source_get_extension (source, extension_name);
198
199 name = e_source_mail_identity_get_name (mail_identity);
200 address = e_source_mail_identity_get_address (mail_identity);
201
202 if (name != NULL && address != NULL)
203 identity = g_strdup_printf ("%s <%s>", name, address);
204
205 g_object_unref (source);
206
207 return identity;
208 }
209
210 /**
211 * itip_address_is_user:
212 * @registry: an #ESourceRegistry
213 * @address: an email address
214 *
215 * Looks for a registered mail identity with a matching email address.
216 *
217 * Returns: %TRUE if a match was found, %FALSE if not
218 **/
219 gboolean
220 itip_address_is_user (ESourceRegistry *registry,
221 const gchar *address)
222 {
223 GList *list, *iter;
224 const gchar *extension_name;
225 gboolean match = FALSE;
226
227 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
228 g_return_val_if_fail (address != NULL, FALSE);
229
230 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
231
232 list = e_source_registry_list_sources (registry, extension_name);
233
234 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
235 ESource *source = E_SOURCE (iter->data);
236 ESourceMailIdentity *extension;
237 const gchar *id_address;
238
239 extension = e_source_get_extension (source, extension_name);
240 id_address = e_source_mail_identity_get_address (extension);
241
242 if (id_address == NULL)
243 continue;
244
245 if (g_ascii_strcasecmp (address, id_address) == 0) {
246 match = TRUE;
247 break;
248 }
249 }
250
251 g_list_free_full (list, (GDestroyNotify) g_object_unref);
252
253 return match;
254 }
255
256 gboolean
257 itip_organizer_is_user (ESourceRegistry *registry,
258 ECalComponent *comp,
259 ECalClient *cal_client)
260 {
261 return itip_organizer_is_user_ex (registry, comp, cal_client, FALSE);
262 }
263
264 gboolean
265 itip_organizer_is_user_ex (ESourceRegistry *registry,
266 ECalComponent *comp,
267 ECalClient *cal_client,
268 gboolean skip_cap_test)
269 {
270 ECalComponentOrganizer organizer;
271 const gchar *strip;
272 gboolean user_org = FALSE;
273
274 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
275
276 if (!e_cal_component_has_organizer (comp) ||
277 (!skip_cap_test && e_client_check_capability (
278 E_CLIENT (cal_client), CAL_STATIC_CAPABILITY_NO_ORGANIZER)))
279 return FALSE;
280
281 e_cal_component_get_organizer (comp, &organizer);
282 if (organizer.value != NULL) {
283
284 strip = itip_strip_mailto (organizer.value);
285
286 if (e_client_check_capability (E_CLIENT (cal_client), CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS)) {
287 gchar *email = NULL;
288
289 if (e_client_get_backend_property_sync (E_CLIENT (cal_client), CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS, &email, NULL, NULL) &&
290 !g_ascii_strcasecmp (email, strip)) {
291 g_free (email);
292
293 return TRUE;
294 }
295
296 g_free (email);
297 return FALSE;
298 }
299
300 user_org = itip_address_is_user (registry, strip);
301 }
302
303 return user_org;
304 }
305
306 gboolean
307 itip_sentby_is_user (ESourceRegistry *registry,
308 ECalComponent *comp,
309 ECalClient *cal_client)
310 {
311 ECalComponentOrganizer organizer;
312 const gchar *strip;
313 gboolean user_sentby = FALSE;
314
315 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
316
317 if (!e_cal_component_has_organizer (comp) ||
318 e_client_check_capability (
319 E_CLIENT (cal_client), CAL_STATIC_CAPABILITY_NO_ORGANIZER))
320 return FALSE;
321
322 e_cal_component_get_organizer (comp, &organizer);
323 if (organizer.sentby != NULL) {
324 strip = itip_strip_mailto (organizer.sentby);
325 user_sentby = itip_address_is_user (registry, strip);
326 }
327
328 return user_sentby;
329 }
330
331 static ECalComponentAttendee *
332 get_attendee (GSList *attendees,
333 gchar *address)
334 {
335 GSList *l;
336
337 if (!address)
338 return NULL;
339
340 for (l = attendees; l; l = l->next) {
341 ECalComponentAttendee *attendee = l->data;
342
343 if (!g_ascii_strcasecmp (itip_strip_mailto (attendee->value), address)) {
344 return attendee;
345 }
346 }
347
348 return NULL;
349 }
350
351 static ECalComponentAttendee *
352 get_attendee_if_attendee_sentby_is_user (GSList *attendees,
353 gchar *address)
354 {
355 GSList *l;
356
357 for (l = attendees; l; l = l->next) {
358 ECalComponentAttendee *attendee = l->data;
359
360 if (attendee->sentby && g_str_equal (
361 itip_strip_mailto (attendee->sentby), address)) {
362 return attendee;
363 }
364 }
365
366 return NULL;
367 }
368
369 static gchar *
370 html_new_lines_for (const gchar *string)
371 {
372 gchar **lines;
373 gchar *joined;
374
375 lines = g_strsplit_set (string, "\n", -1);
376 joined = g_strjoinv ("<br>", lines);
377 g_strfreev (lines);
378
379 return joined;
380 }
381
382 gchar *
383 itip_get_comp_attendee (ESourceRegistry *registry,
384 ECalComponent *comp,
385 ECalClient *cal_client)
386 {
387 ESource *source;
388 GSList *attendees;
389 ECalComponentAttendee *attendee = NULL;
390 GList *list, *link;
391 const gchar *extension_name;
392 gchar *address = NULL;
393
394 e_cal_component_get_attendee_list (comp, &attendees);
395
396 if (cal_client)
397 e_client_get_backend_property_sync (
398 E_CLIENT (cal_client),
399 CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS,
400 &address, NULL, NULL);
401
402 if (address != NULL && *address != '\0') {
403 attendee = get_attendee (attendees, address);
404
405 if (attendee) {
406 gchar *user_email;
407
408 user_email = g_strdup (
409 itip_strip_mailto (attendee->value));
410 e_cal_component_free_attendee_list (attendees);
411 g_free (address);
412
413 return user_email;
414 }
415
416 attendee = get_attendee_if_attendee_sentby_is_user (
417 attendees, address);
418
419 if (attendee != NULL) {
420 gchar *user_email;
421
422 user_email = g_strdup (
423 itip_strip_mailto (attendee->sentby));
424 e_cal_component_free_attendee_list (attendees);
425 g_free (address);
426
427 return user_email;
428 }
429
430 g_free (address);
431 address = NULL;
432 }
433
434 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
435 list = e_source_registry_list_sources (registry, extension_name);
436
437 for (link = list; link != NULL; link = g_list_next (link)) {
438 ESourceExtension *extension;
439
440 source = E_SOURCE (link->data);
441
442 if (!e_source_get_enabled (source))
443 continue;
444
445 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
446 extension = e_source_get_extension (source, extension_name);
447
448 address = e_source_mail_identity_dup_address (
449 E_SOURCE_MAIL_IDENTITY (extension));
450
451 if (address == NULL)
452 continue;
453
454 attendee = get_attendee (attendees, address);
455 if (attendee != NULL) {
456 gchar *user_email;
457
458 user_email = g_strdup (
459 itip_strip_mailto (attendee->value));
460 e_cal_component_free_attendee_list (attendees);
461
462 g_free (address);
463
464 return user_email;
465 }
466
467 /* If the account was not found in the attendees list, then
468 * let's check the 'sentby' fields of the attendees if we can
469 * find the account. */
470 attendee = get_attendee_if_attendee_sentby_is_user (
471 attendees, address);
472 if (attendee) {
473 gchar *user_email;
474
475 user_email = g_strdup (
476 itip_strip_mailto (attendee->sentby));
477 e_cal_component_free_attendee_list (attendees);
478
479 g_free (address);
480
481 return user_email;
482 }
483
484 g_free (address);
485 }
486
487 g_list_free_full (list, (GDestroyNotify) g_object_unref);
488
489 /* We could not find the attendee in the component, so just give
490 * the default account address if the email address is not set in
491 * the backend. */
492 /* FIXME do we have a better way ? */
493 itip_get_default_name_and_address (registry, NULL, &address);
494
495 e_cal_component_free_attendee_list (attendees);
496
497 if (address == NULL)
498 address = g_strdup ("");
499
500 return address;
501 }
502
503 const gchar *
504 itip_strip_mailto (const gchar *address)
505 {
506 if (address == NULL)
507 return NULL;
508
509 if (!g_ascii_strncasecmp (address, "mailto:", 7))
510 address += 7;
511
512 return address;
513 }
514
515 static gchar *
516 get_label (struct icaltimetype *tt,
517 gboolean use_24_hour_format)
518 {
519 gchar buffer[1000];
520 struct tm tmp_tm;
521
522 tmp_tm = icaltimetype_to_tm (tt);
523
524 e_time_format_date_and_time (
525 &tmp_tm, use_24_hour_format, FALSE, FALSE, buffer, 1000);
526
527 return g_strdup (buffer);
528 }
529
530 typedef struct {
531 GHashTable *tzids;
532 icalcomponent *icomp;
533 ECalClient *client;
534 icalcomponent *zones;
535 } ItipUtilTZData;
536
537 static void
538 foreach_tzid_callback (icalparameter *param,
539 gpointer data)
540 {
541 ItipUtilTZData *tz_data = data;
542 const gchar *tzid;
543 icaltimezone *zone = NULL;
544 icalcomponent *vtimezone_comp;
545
546 /* Get the TZID string from the parameter. */
547 tzid = icalparameter_get_tzid (param);
548 if (!tzid || g_hash_table_lookup (tz_data->tzids, tzid))
549 return;
550
551 /* Look for the timezone */
552 if (tz_data->zones != NULL)
553 zone = icalcomponent_get_timezone (tz_data->zones, tzid);
554 if (zone == NULL)
555 zone = icaltimezone_get_builtin_timezone_from_tzid (tzid);
556 if (zone == NULL && tz_data->client != NULL)
557 e_cal_client_get_timezone_sync (tz_data->client, tzid, &zone, NULL, NULL);
558 if (zone == NULL)
559 return;
560
561 /* Convert it to a string and add it to the hash. */
562 vtimezone_comp = icaltimezone_get_component (zone);
563 if (!vtimezone_comp)
564 return;
565
566 icalcomponent_add_component (
567 tz_data->icomp, icalcomponent_new_clone (vtimezone_comp));
568 g_hash_table_insert (tz_data->tzids, (gchar *) tzid, (gchar *) tzid);
569 }
570
571 static icalcomponent *
572 comp_toplevel_with_zones (ECalComponentItipMethod method,
573 ECalComponent *comp,
574 ECalClient *cal_client,
575 icalcomponent *zones)
576 {
577 icalcomponent *top_level, *icomp;
578 icalproperty *prop;
579 icalvalue *value;
580 ItipUtilTZData tz_data;
581
582 top_level = e_cal_util_new_top_level ();
583
584 prop = icalproperty_new (ICAL_METHOD_PROPERTY);
585 value = icalvalue_new_method (itip_methods_enum[method]);
586 icalproperty_set_value (prop, value);
587 icalcomponent_add_property (top_level, prop);
588
589 icomp = e_cal_component_get_icalcomponent (comp);
590 icomp = icalcomponent_new_clone (icomp);
591
592 tz_data.tzids = g_hash_table_new (g_str_hash, g_str_equal);
593 tz_data.icomp = top_level;
594 tz_data.client = cal_client;
595 tz_data.zones = zones;
596 icalcomponent_foreach_tzid (icomp, foreach_tzid_callback, &tz_data);
597 g_hash_table_destroy (tz_data.tzids);
598
599 icalcomponent_add_component (top_level, icomp);
600
601 return top_level;
602 }
603
604 static gboolean
605 users_has_attendee (const GSList *users,
606 const gchar *address)
607 {
608 const GSList *l;
609
610 for (l = users; l != NULL; l = l->next) {
611 if (!g_ascii_strcasecmp (address, l->data))
612 return TRUE;
613 }
614
615 return FALSE;
616 }
617
618 static gchar *
619 comp_from (ECalComponentItipMethod method,
620 ECalComponent *comp,
621 ESourceRegistry *registry)
622 {
623 ECalComponentOrganizer organizer;
624 ECalComponentAttendee *attendee;
625 GSList *attendees;
626 gchar *from;
627 gchar *sender = NULL;
628
629 switch (method) {
630 case E_CAL_COMPONENT_METHOD_PUBLISH:
631 return NULL;
632
633 case E_CAL_COMPONENT_METHOD_REQUEST:
634 return itip_get_comp_attendee (registry, comp, NULL);
635
636 case E_CAL_COMPONENT_METHOD_REPLY:
637 sender = itip_get_comp_attendee (registry, comp, NULL);
638 if (sender != NULL)
639 return sender;
640 if (!e_cal_component_has_attendees (comp))
641 return NULL;
642
643 case E_CAL_COMPONENT_METHOD_CANCEL:
644
645 case E_CAL_COMPONENT_METHOD_ADD:
646
647 e_cal_component_get_organizer (comp, &organizer);
648 if (organizer.value == NULL) {
649 e_notice (
650 NULL, GTK_MESSAGE_ERROR,
651 _("An organizer must be set."));
652 return NULL;
653 }
654 return g_strdup (itip_strip_mailto (organizer.value));
655
656 default:
657 if (!e_cal_component_has_attendees (comp))
658 return NULL;
659
660 e_cal_component_get_attendee_list (comp, &attendees);
661 attendee = attendees->data;
662 if (attendee->value != NULL)
663 from = g_strdup (itip_strip_mailto (attendee->value));
664 else
665 from = NULL;
666 e_cal_component_free_attendee_list (attendees);
667
668 return from;
669 }
670 }
671
672 static EDestination **
673 comp_to_list (ESourceRegistry *registry,
674 ECalComponentItipMethod method,
675 ECalComponent *comp,
676 const GSList *users,
677 gboolean reply_all,
678 const GSList *only_attendees)
679 {
680 ECalComponentOrganizer organizer;
681 GSList *attendees, *l;
682 GPtrArray *array = NULL;
683 EDestination *destination;
684 gint len;
685 gchar *sender = NULL;
686
687 union {
688 gpointer *pdata;
689 EDestination **destinations;
690 } convert;
691
692 switch (method) {
693 case E_CAL_COMPONENT_METHOD_REQUEST:
694 case E_CAL_COMPONENT_METHOD_CANCEL:
695 e_cal_component_get_attendee_list (comp, &attendees);
696 len = g_slist_length (attendees);
697 if (len <= 0) {
698 e_notice (
699 NULL, GTK_MESSAGE_ERROR,
700 _("At least one attendee is necessary"));
701 e_cal_component_free_attendee_list (attendees);
702 return NULL;
703 }
704
705 e_cal_component_get_organizer (comp, &organizer);
706 if (organizer.value == NULL) {
707 e_notice (
708 NULL, GTK_MESSAGE_ERROR,
709 _("An organizer must be set."));
710 return NULL;
711 }
712
713 array = g_ptr_array_new ();
714
715 sender = itip_get_comp_attendee (registry, comp, NULL);
716
717 for (l = attendees; l != NULL; l = l->next) {
718 ECalComponentAttendee *att = l->data;
719
720 /* Bugfix: 688711 - Varadhan
721 * Resource is also considered as a "attendee". If the respective backend
722 * is able to successfully book resources automagically, it will appear
723 * in the users list and thereby won't get added to the list of destinations
724 * to send the meeting invite, otherwise, as a safety measure, a meeting
725 * invite will be sent to the resources as well. */
726 if (att->cutype != ICAL_CUTYPE_INDIVIDUAL &&
727 att->cutype != ICAL_CUTYPE_GROUP &&
728 att->cutype != ICAL_CUTYPE_RESOURCE)
729 continue;
730 else if (users_has_attendee (users, att->value))
731 continue;
732 else if (att->sentby &&
733 users_has_attendee (users, att->sentby))
734 continue;
735 else if (!g_ascii_strcasecmp (
736 att->value, organizer.value))
737 continue;
738 else if (att->sentby && !g_ascii_strcasecmp (
739 att->sentby, organizer.sentby))
740 continue;
741 else if (!g_ascii_strcasecmp (
742 itip_strip_mailto (att->value), sender))
743 continue;
744 else if (att->status == ICAL_PARTSTAT_DELEGATED &&
745 (att->delto && *att->delto) && !(att->rsvp) &&
746 method == E_CAL_COMPONENT_METHOD_REQUEST)
747 continue;
748 else if (only_attendees &&
749 !comp_editor_have_in_new_attendees_lst (
750 only_attendees, itip_strip_mailto (att->value)))
751 continue;
752
753 destination = e_destination_new ();
754 if (att->cn != NULL)
755 e_destination_set_name (destination, att->cn);
756 e_destination_set_email (
757 destination, itip_strip_mailto (att->value));
758 g_ptr_array_add (array, destination);
759 }
760 g_free (sender);
761 e_cal_component_free_attendee_list (attendees);
762 break;
763
764 case E_CAL_COMPONENT_METHOD_REPLY:
765
766 if (reply_all) {
767 e_cal_component_get_attendee_list (comp, &attendees);
768 len = g_slist_length (attendees);
769
770 if (len <= 0)
771 return NULL;
772
773 array = g_ptr_array_new ();
774
775 e_cal_component_get_organizer (comp, &organizer);
776 sender = itip_get_comp_attendee (registry, comp, NULL);
777
778 for (l = attendees; l != NULL; l = l->next) {
779 ECalComponentAttendee *att = l->data;
780
781 if (att->cutype != ICAL_CUTYPE_INDIVIDUAL &&
782 att->cutype != ICAL_CUTYPE_GROUP)
783 continue;
784 else if (only_attendees &&
785 !comp_editor_have_in_new_attendees_lst (
786 only_attendees, itip_strip_mailto (att->value)))
787 continue;
788
789 destination = e_destination_new ();
790 if (att->cn != NULL)
791 e_destination_set_name (destination, att->cn);
792 e_destination_set_email (
793 destination, itip_strip_mailto (att->value));
794 g_ptr_array_add (array, destination);
795 }
796
797 g_free (sender);
798 e_cal_component_free_attendee_list (attendees);
799
800 } else {
801 array = g_ptr_array_new ();
802
803 destination = e_destination_new ();
804 e_cal_component_get_organizer (comp, &organizer);
805 if (organizer.value)
806 e_destination_set_email (
807 destination, itip_strip_mailto (organizer.value));
808 g_ptr_array_add (array, destination);
809 }
810 break;
811
812 case E_CAL_COMPONENT_METHOD_ADD:
813 case E_CAL_COMPONENT_METHOD_REFRESH:
814 case E_CAL_COMPONENT_METHOD_COUNTER:
815 case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
816 e_cal_component_get_organizer (comp, &organizer);
817 if (organizer.value == NULL) {
818 e_notice (
819 NULL, GTK_MESSAGE_ERROR,
820 _("An organizer must be set."));
821 return NULL;
822 }
823
824 array = g_ptr_array_new ();
825
826 destination = e_destination_new ();
827 if (organizer.cn != NULL)
828 e_destination_set_name (destination, organizer.cn);
829 e_destination_set_email (
830 destination, itip_strip_mailto (organizer.value));
831 g_ptr_array_add (array, destination);
832
833 /* send the status to delegatee to the delegate also*/
834 e_cal_component_get_attendee_list (comp, &attendees);
835 sender = itip_get_comp_attendee (registry, comp, NULL);
836
837 for (l = attendees; l != NULL; l = l->next) {
838 ECalComponentAttendee *att = l->data;
839
840 if (att->cutype != ICAL_CUTYPE_INDIVIDUAL &&
841 att->cutype != ICAL_CUTYPE_GROUP)
842 continue;
843
844 if (!g_ascii_strcasecmp (
845 itip_strip_mailto (att->value), sender) ||
846 (att->sentby && !g_ascii_strcasecmp (
847 itip_strip_mailto (att->sentby), sender))) {
848
849 if (!(att->delfrom && *att->delfrom))
850 break;
851
852 destination = e_destination_new ();
853 e_destination_set_email (
854 destination, itip_strip_mailto (att->delfrom));
855 g_ptr_array_add (array, destination);
856 }
857
858 }
859 e_cal_component_free_attendee_list (attendees);
860
861 break;
862 case E_CAL_COMPONENT_METHOD_PUBLISH:
863 if (users) {
864 const GSList *list;
865
866 array = g_ptr_array_new ();
867
868 for (list = users; list != NULL; list = list->next) {
869 destination = e_destination_new ();
870 e_destination_set_email (destination, list->data);
871 g_ptr_array_add (array, destination);
872 }
873
874 break;
875 }
876 default:
877 break;
878 }
879
880 if (array == NULL)
881 return NULL;
882
883 g_ptr_array_add (array, NULL);
884 convert.pdata = g_ptr_array_free (array, FALSE);
885
886 return convert.destinations;
887 }
888
889 static gchar *
890 comp_subject (ESourceRegistry *registry,
891 ECalComponentItipMethod method,
892 ECalComponent *comp)
893 {
894 ECalComponentText caltext;
895 const gchar *description, *prefix = NULL;
896 GSList *alist, *l;
897 gchar *subject;
898 gchar *sender;
899 ECalComponentAttendee *a = NULL;
900
901 e_cal_component_get_summary (comp, &caltext);
902 if (caltext.value != NULL)
903 description = caltext.value;
904 else {
905 switch (e_cal_component_get_vtype (comp)) {
906 case E_CAL_COMPONENT_EVENT:
907 description = _("Event information");
908 break;
909 case E_CAL_COMPONENT_TODO:
910 description = _("Task information");
911 break;
912 case E_CAL_COMPONENT_JOURNAL:
913 description = _("Memo information");
914 break;
915 case E_CAL_COMPONENT_FREEBUSY:
916 description = _("Free/Busy information");
917 break;
918 default:
919 description = _("Calendar information");
920 }
921 }
922
923 switch (method) {
924 case E_CAL_COMPONENT_METHOD_PUBLISH:
925 case E_CAL_COMPONENT_METHOD_REQUEST:
926 /* FIXME: If this is an update to a previous
927 * PUBLISH or REQUEST, then
928 prefix = U_("Updated");
929 */
930 break;
931
932 case E_CAL_COMPONENT_METHOD_REPLY:
933 e_cal_component_get_attendee_list (comp, &alist);
934 sender = itip_get_comp_attendee (registry, comp, NULL);
935 if (sender) {
936
937 for (l = alist; l != NULL; l = l->next) {
938 a = l->data;
939 if ((sender && *sender) && (g_ascii_strcasecmp (
940 itip_strip_mailto (a->value), sender) ||
941 (a->sentby && g_ascii_strcasecmp (
942 itip_strip_mailto (a->sentby), sender))))
943 break;
944 }
945 g_free (sender);
946 }
947
948 if (alist != NULL) {
949
950 switch (a->status) {
951 case ICAL_PARTSTAT_ACCEPTED:
952 /* Translators: This is part of the subject
953 * line of a meeting request or update email.
954 * The full subject line would be:
955 * "Accepted: Meeting Name". */
956 prefix = C_("Meeting", "Accepted");
957 break;
958 case ICAL_PARTSTAT_TENTATIVE:
959 /* Translators: This is part of the subject
960 * line of a meeting request or update email.
961 * The full subject line would be:
962 * "Tentatively Accepted: Meeting Name". */
963 prefix = C_("Meeting", "Tentatively Accepted");
964 break;
965 case ICAL_PARTSTAT_DECLINED:
966 /* Translators: This is part of the subject
967 * line of a meeting request or update email.
968 * The full subject line would be:
969 * "Declined: Meeting Name". */
970 prefix = C_("Meeting", "Declined");
971 break;
972 case ICAL_PARTSTAT_DELEGATED:
973 /* Translators: This is part of the subject
974 * line of a meeting request or update email.
975 * The full subject line would be:
976 * "Delegated: Meeting Name". */
977 prefix = C_("Meeting", "Delegated");
978 break;
979 default:
980 break;
981 }
982 e_cal_component_free_attendee_list (alist);
983 }
984 break;
985
986 case E_CAL_COMPONENT_METHOD_ADD:
987 /* Translators: This is part of the subject line of a
988 * meeting request or update email. The full subject
989 * line would be: "Updated: Meeting Name". */
990 prefix = C_("Meeting", "Updated");
991 break;
992
993 case E_CAL_COMPONENT_METHOD_CANCEL:
994 /* Translators: This is part of the subject line of a
995 * meeting request or update email. The full subject
996 * line would be: "Cancel: Meeting Name". */
997 prefix = C_("Meeting", "Cancel");
998 break;
999
1000 case E_CAL_COMPONENT_METHOD_REFRESH:
1001 /* Translators: This is part of the subject line of a
1002 * meeting request or update email. The full subject
1003 * line would be: "Refresh: Meeting Name". */
1004 prefix = C_("Meeting", "Refresh");
1005 break;
1006
1007 case E_CAL_COMPONENT_METHOD_COUNTER:
1008 /* Translators: This is part of the subject line of a
1009 * meeting request or update email. The full subject
1010 * line would be: "Counter-proposal: Meeting Name". */
1011 prefix = C_("Meeting", "Counter-proposal");
1012 break;
1013
1014 case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
1015 /* Translators: This is part of the subject line of a
1016 * meeting request or update email. The full subject
1017 * line would be: "Declined: Meeting Name". */
1018 prefix = C_("Meeting", "Declined");
1019 break;
1020
1021 default:
1022 break;
1023 }
1024
1025 if (prefix != NULL)
1026 subject = g_strdup_printf ("%s: %s", prefix, description);
1027 else
1028 subject = g_strdup (description);
1029
1030 return subject;
1031 }
1032
1033 static gchar *
1034 comp_content_type (ECalComponent *comp,
1035 ECalComponentItipMethod method)
1036 {
1037 const gchar *name;
1038
1039 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_FREEBUSY)
1040 name = "freebusy.ifb";
1041 else
1042 name = "calendar.ics";
1043
1044 return g_strdup_printf (
1045 "text/calendar; name=\"%s\"; charset=utf-8; METHOD=%s",
1046 name, itip_methods[method]);
1047 }
1048
1049 static const gchar *
1050 comp_filename (ECalComponent *comp)
1051 {
1052 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_FREEBUSY)
1053 return "freebusy.ifb";
1054 else
1055 return "calendar.ics";
1056 }
1057
1058 static gchar *
1059 comp_description (ECalComponent *comp,
1060 gboolean use_24_hour_format)
1061 {
1062 gchar *description;
1063 ECalComponentDateTime dt;
1064 gchar *start = NULL, *end = NULL;
1065
1066 switch (e_cal_component_get_vtype (comp)) {
1067 case E_CAL_COMPONENT_EVENT:
1068 description = g_strdup (_("Event information"));
1069 break;
1070 case E_CAL_COMPONENT_TODO:
1071 description = g_strdup (_("Task information"));
1072 break;
1073 case E_CAL_COMPONENT_JOURNAL:
1074 description = g_strdup (_("Memo information"));
1075 break;
1076 case E_CAL_COMPONENT_FREEBUSY:
1077 e_cal_component_get_dtstart (comp, &dt);
1078 if (dt.value)
1079 start = get_label (dt.value, use_24_hour_format);
1080 e_cal_component_free_datetime (&dt);
1081
1082 e_cal_component_get_dtend (comp, &dt);
1083 if (dt.value)
1084 end = get_label (dt.value, use_24_hour_format);
1085 e_cal_component_free_datetime (&dt);
1086
1087 if (start != NULL && end != NULL)
1088 description = g_strdup_printf (
1089 _("Free/Busy information (%s to %s)"),
1090 start, end);
1091 else
1092 description = g_strdup (_("Free/Busy information"));
1093 g_free (start);
1094 g_free (end);
1095 break;
1096 default:
1097 description = g_strdup (_("iCalendar information"));
1098 break;
1099 }
1100
1101 return description;
1102 }
1103
1104 static gboolean
1105 comp_server_send (ECalComponentItipMethod method,
1106 ECalComponent *comp,
1107 ECalClient *cal_client,
1108 icalcomponent *zones,
1109 GSList **users)
1110 {
1111 icalcomponent *top_level, *returned_icalcomp = NULL;
1112 gboolean retval = TRUE;
1113 GError *error = NULL;
1114
1115 top_level = comp_toplevel_with_zones (method, comp, cal_client, zones);
1116 d (printf ("itip-utils.c: comp_server_send: calling e_cal_send_objects... \n"));
1117 if (!e_cal_client_send_objects_sync (cal_client, top_level, users, &returned_icalcomp, NULL, &error)) {
1118 /* FIXME Really need a book problem status code */
1119 d (printf ("itip-utils.c: return value from e_cal_send_objects is: %d", error->code));
1120 if (error) {
1121 if (g_error_matches (error, E_CAL_CLIENT_ERROR, E_CAL_CLIENT_ERROR_OBJECT_ID_ALREADY_EXISTS)) {
1122 e_notice (
1123 NULL, GTK_MESSAGE_ERROR,
1124 _("Unable to book a resource, the "
1125 "new event collides with some other."));
1126 } else {
1127 gchar *msg = g_strconcat (
1128 _("Unable to book a resource, error: "),
1129 error->message, NULL);
1130 e_notice (NULL, GTK_MESSAGE_ERROR, msg);
1131 g_free (msg);
1132 }
1133
1134 retval = FALSE;
1135 }
1136 } else {
1137 d (printf ("itip-utils.c: e_cal_send_objects returned without errors\n"));
1138 }
1139
1140 g_clear_error (&error);
1141
1142 if (returned_icalcomp)
1143 icalcomponent_free (returned_icalcomp);
1144 icalcomponent_free (top_level);
1145
1146 return retval;
1147 }
1148
1149 static gboolean
1150 comp_limit_attendees (ESourceRegistry *registry,
1151 ECalComponent *comp)
1152 {
1153 icalcomponent *icomp;
1154 icalproperty *prop;
1155 gboolean found = FALSE, match = FALSE;
1156 GSList *l, *list = NULL;
1157
1158 icomp = e_cal_component_get_icalcomponent (comp);
1159
1160 for (prop = icalcomponent_get_first_property (icomp, ICAL_ATTENDEE_PROPERTY);
1161 prop != NULL;
1162 prop = icalcomponent_get_next_property (icomp, ICAL_ATTENDEE_PROPERTY))
1163 {
1164 gchar *attendee;
1165 gchar *attendee_text;
1166 icalparameter *param;
1167 const gchar *attendee_sentby;
1168 gchar *attendee_sentby_text = NULL;
1169
1170 /* If we've already found something, just erase the rest */
1171 if (found) {
1172 list = g_slist_prepend (list, prop);
1173 continue;
1174 }
1175
1176 attendee = icalproperty_get_value_as_string_r (prop);
1177 if (!attendee)
1178 continue;
1179
1180 attendee_text = g_strdup (itip_strip_mailto (attendee));
1181 g_free (attendee);
1182 attendee_text = g_strstrip (attendee_text);
1183 found = match = itip_address_is_user (registry, attendee_text);
1184
1185 if (!found) {
1186 param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER);
1187 if (param) {
1188 attendee_sentby =
1189 icalparameter_get_sentby (param);
1190 attendee_sentby =
1191 itip_strip_mailto (attendee_sentby);
1192 attendee_sentby_text =
1193 g_strstrip (g_strdup (attendee_sentby));
1194 found = match = itip_address_is_user (
1195 registry, attendee_sentby_text);
1196 }
1197 }
1198
1199 g_free (attendee_text);
1200 g_free (attendee_sentby_text);
1201
1202 if (!match)
1203 list = g_slist_prepend (list, prop);
1204 }
1205
1206 for (l = list; l != NULL; l = l->next) {
1207 prop = l->data;
1208
1209 icalcomponent_remove_property (icomp, prop);
1210 icalproperty_free (prop);
1211 }
1212 g_slist_free (list);
1213
1214 return found;
1215 }
1216
1217 static void
1218 comp_sentby (ECalComponent *comp,
1219 ECalClient *cal_client,
1220 ESourceRegistry *registry)
1221 {
1222 ECalComponentOrganizer organizer;
1223 GSList * attendees, *l;
1224 gchar *name;
1225 gchar *address;
1226 gchar *user = NULL;
1227
1228 itip_get_default_name_and_address (registry, &name, &address);
1229
1230 e_cal_component_get_organizer (comp, &organizer);
1231 if (!organizer.value && name != NULL && address != NULL) {
1232 organizer.value = g_strdup_printf ("MAILTO:%s", address);
1233 organizer.sentby = NULL;
1234 organizer.cn = name;
1235 organizer.language = NULL;
1236
1237 e_cal_component_set_organizer (comp, &organizer);
1238 g_free ((gchar *) organizer.value);
1239
1240 g_free (name);
1241 g_free (address);
1242 return;
1243 }
1244
1245 e_cal_component_get_attendee_list (comp, &attendees);
1246 user = itip_get_comp_attendee (registry, comp, cal_client);
1247 for (l = attendees; l; l = l->next) {
1248 ECalComponentAttendee *a = l->data;
1249
1250 if (!g_ascii_strcasecmp (
1251 itip_strip_mailto (a->value), user) ||
1252 (a->sentby && !g_ascii_strcasecmp (
1253 itip_strip_mailto (a->sentby), user))) {
1254 g_free (user);
1255
1256 g_free (name);
1257 g_free (address);
1258 return;
1259 }
1260 }
1261
1262 if (!itip_organizer_is_user (registry, comp, cal_client) &&
1263 !itip_sentby_is_user (registry, comp, cal_client) &&
1264 address != NULL) {
1265 organizer.value = g_strdup (organizer.value);
1266 organizer.sentby = g_strdup_printf ("MAILTO:%s", address);
1267 organizer.cn = g_strdup (organizer.cn);
1268 organizer.language = g_strdup (organizer.language);
1269
1270 e_cal_component_set_organizer (comp, &organizer);
1271
1272 g_free ((gchar *) organizer.value);
1273 g_free ((gchar *) organizer.sentby);
1274 g_free ((gchar *) organizer.cn);
1275 g_free ((gchar *) organizer.language);
1276 }
1277
1278 g_free (name);
1279 g_free (address);
1280 }
1281
1282 static ECalComponent *
1283 comp_minimal (ESourceRegistry *registry,
1284 ECalComponent *comp,
1285 gboolean attendee)
1286 {
1287 ECalComponent *clone;
1288 icalcomponent *icomp, *icomp_clone;
1289 icalproperty *prop;
1290 ECalComponentOrganizer organizer;
1291 const gchar *uid;
1292 GSList *comments;
1293 struct icaltimetype itt;
1294 ECalComponentRange recur_id;
1295
1296 clone = e_cal_component_new ();
1297 e_cal_component_set_new_vtype (clone, e_cal_component_get_vtype (comp));
1298
1299 if (attendee) {
1300 GSList *attendees;
1301
1302 e_cal_component_get_attendee_list (comp, &attendees);
1303 e_cal_component_set_attendee_list (clone, attendees);
1304
1305 if (!comp_limit_attendees (registry, clone)) {
1306 e_notice (
1307 NULL, GTK_MESSAGE_ERROR,
1308 _("You must be an attendee of the event."));
1309 goto error;
1310 }
1311 }
1312
1313 itt = icaltime_from_timet_with_zone (
1314 time (NULL), FALSE,
1315 icaltimezone_get_utc_timezone ());
1316 e_cal_component_set_dtstamp (clone, &itt);
1317
1318 e_cal_component_get_organizer (comp, &organizer);
1319 if (organizer.value == NULL)
1320 goto error;
1321 e_cal_component_set_organizer (clone, &organizer);
1322
1323 e_cal_component_get_uid (comp, &uid);
1324 e_cal_component_set_uid (clone, uid);
1325
1326 e_cal_component_get_comment_list (comp, &comments);
1327 if (g_slist_length (comments) <= 1) {
1328 e_cal_component_set_comment_list (clone, comments);
1329 } else {
1330 GSList *l = comments;
1331
1332 comments = g_slist_remove_link (comments, l);
1333 e_cal_component_set_comment_list (clone, l);
1334 e_cal_component_free_text_list (l);
1335 }
1336 e_cal_component_free_text_list (comments);
1337
1338 e_cal_component_get_recurid (comp, &recur_id);
1339 if (recur_id.datetime.value != NULL)
1340 e_cal_component_set_recurid (clone, &recur_id);
1341
1342 icomp = e_cal_component_get_icalcomponent (comp);
1343 icomp_clone = e_cal_component_get_icalcomponent (clone);
1344 for (prop = icalcomponent_get_first_property (icomp, ICAL_X_PROPERTY);
1345 prop != NULL;
1346 prop = icalcomponent_get_next_property (icomp, ICAL_X_PROPERTY))
1347 {
1348 icalproperty *p;
1349
1350 p = icalproperty_new_clone (prop);
1351 icalcomponent_add_property (icomp_clone, p);
1352 }
1353
1354 e_cal_component_rescan (clone);
1355
1356 return clone;
1357
1358 error:
1359 g_object_unref (clone);
1360 return NULL;
1361 }
1362
1363 static void
1364 strip_x_microsoft_props (ECalComponent *comp)
1365 {
1366 GSList *lst = NULL, *l;
1367 icalcomponent *icalcomp;
1368 icalproperty *icalprop;
1369
1370 g_return_if_fail (comp != NULL);
1371
1372 icalcomp = e_cal_component_get_icalcomponent (comp);
1373 g_return_if_fail (icalcomp != NULL);
1374
1375 for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
1376 icalprop;
1377 icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) {
1378 const gchar *x_name = icalproperty_get_x_name (icalprop);
1379
1380 if (x_name && g_ascii_strncasecmp (x_name, "X-MICROSOFT-", 12) == 0)
1381 lst = g_slist_prepend (lst, icalprop);
1382 }
1383
1384 for (l = lst; l != NULL; l = l->next) {
1385 icalprop = l->data;
1386 icalcomponent_remove_property (icalcomp, icalprop);
1387 icalproperty_free (icalprop);
1388 }
1389
1390 g_slist_free (lst);
1391 }
1392
1393 static ECalComponent *
1394 comp_compliant (ESourceRegistry *registry,
1395 ECalComponentItipMethod method,
1396 ECalComponent *comp,
1397 ECalClient *client,
1398 icalcomponent *zones,
1399 icaltimezone *default_zone,
1400 gboolean strip_alarms)
1401 {
1402 ECalComponent *clone, *temp_clone;
1403 struct icaltimetype itt;
1404
1405 clone = e_cal_component_clone (comp);
1406 itt = icaltime_from_timet_with_zone (
1407 time (NULL), FALSE,
1408 icaltimezone_get_utc_timezone ());
1409 e_cal_component_set_dtstamp (clone, &itt);
1410
1411 /* Make UNTIL date a datetime in a simple recurrence */
1412 if (e_cal_component_has_recurrences (clone)
1413 && e_cal_component_has_simple_recurrence (clone)) {
1414 GSList *rrule_list;
1415 struct icalrecurrencetype *r;
1416
1417 e_cal_component_get_rrule_list (clone, &rrule_list);
1418 r = rrule_list->data;
1419
1420 if (!icaltime_is_null_time (r->until) && r->until.is_date) {
1421 ECalComponentDateTime dt;
1422 icaltimezone *from_zone = NULL, *to_zone;
1423
1424 e_cal_component_get_dtstart (clone, &dt);
1425
1426 if (dt.value->is_date) {
1427 from_zone = default_zone;
1428 } else if (dt.tzid == NULL) {
1429 from_zone = icaltimezone_get_utc_timezone ();
1430 } else {
1431 if (zones != NULL)
1432 from_zone = icalcomponent_get_timezone (zones, dt.tzid);
1433 if (from_zone == NULL)
1434 from_zone = icaltimezone_get_builtin_timezone_from_tzid (dt.tzid);
1435 if (from_zone == NULL && client != NULL)
1436 /* FIXME Error checking */
1437 e_cal_client_get_timezone_sync (
1438 client, dt.tzid,
1439 &from_zone, NULL, NULL);
1440 }
1441
1442 to_zone = icaltimezone_get_utc_timezone ();
1443
1444 r->until.hour = dt.value->hour;
1445 r->until.minute = dt.value->minute;
1446 r->until.second = dt.value->second;
1447 r->until.is_date = FALSE;
1448
1449 icaltimezone_convert_time (&r->until, from_zone, to_zone);
1450 r->until.is_utc = TRUE;
1451
1452 e_cal_component_free_datetime (&dt);
1453 e_cal_component_set_rrule_list (clone, rrule_list);
1454 e_cal_component_abort_sequence (clone);
1455 }
1456
1457 e_cal_component_free_recur_list (rrule_list);
1458 }
1459
1460 /* We delete incoming alarms if requested, even this helps with outlook */
1461 if (strip_alarms) {
1462 e_cal_component_remove_all_alarms (clone);
1463 } else {
1464 /* Always strip procedure alarms, because of security */
1465 GList *uids, *l;
1466
1467 uids = e_cal_component_get_alarm_uids (clone);
1468
1469 for (l = uids; l; l = l->next) {
1470 ECalComponentAlarm *alarm;
1471 ECalComponentAlarmAction action = E_CAL_COMPONENT_ALARM_UNKNOWN;
1472
1473 alarm = e_cal_component_get_alarm (clone, (const gchar *) l->data);
1474 if (alarm) {
1475 e_cal_component_alarm_get_action (alarm, &action);
1476 e_cal_component_alarm_free (alarm);
1477
1478 if (action == E_CAL_COMPONENT_ALARM_PROCEDURE)
1479 e_cal_component_remove_alarm (clone, (const gchar *) l->data);
1480 }
1481 }
1482
1483 cal_obj_uid_list_free (uids);
1484 }
1485
1486 strip_x_microsoft_props (clone);
1487
1488 /* Strip X-LIC-ERROR stuff */
1489 e_cal_component_strip_errors (clone);
1490
1491 /* Comply with itip spec */
1492 switch (method) {
1493 case E_CAL_COMPONENT_METHOD_PUBLISH:
1494 comp_sentby (clone, client, registry);
1495 e_cal_component_set_attendee_list (clone, NULL);
1496 break;
1497 case E_CAL_COMPONENT_METHOD_REQUEST:
1498 comp_sentby (clone, client, registry);
1499 break;
1500 case E_CAL_COMPONENT_METHOD_CANCEL:
1501 comp_sentby (clone, client, registry);
1502 break;
1503 case E_CAL_COMPONENT_METHOD_REPLY:
1504 break;
1505 case E_CAL_COMPONENT_METHOD_ADD:
1506 break;
1507 case E_CAL_COMPONENT_METHOD_REFRESH:
1508 /* Need to remove almost everything */
1509 temp_clone = comp_minimal (registry, clone, TRUE);
1510 g_object_unref (clone);
1511 clone = temp_clone;
1512 break;
1513 case E_CAL_COMPONENT_METHOD_COUNTER:
1514 break;
1515 case E_CAL_COMPONENT_METHOD_DECLINECOUNTER:
1516 /* Need to remove almost everything */
1517 temp_clone = comp_minimal (registry, clone, FALSE);
1518 g_object_unref (clone);
1519 clone = temp_clone;
1520 break;
1521 default:
1522 break;
1523 }
1524
1525 return clone;
1526 }
1527
1528 static void
1529 append_cal_attachments (EMsgComposer *composer,
1530 ECalComponent *comp,
1531 GSList *attach_list)
1532 {
1533 struct CalMimeAttach *mime_attach;
1534 GSList *l;
1535
1536 for (l = attach_list; l; l = l->next) {
1537 CamelMimePart *attachment;
1538
1539 mime_attach = (struct CalMimeAttach *) l->data;
1540
1541 attachment = camel_mime_part_new ();
1542 camel_mime_part_set_content (
1543 attachment, mime_attach->encoded_data,
1544 mime_attach->length, mime_attach->content_type);
1545 if (mime_attach->content_id)
1546 camel_mime_part_set_content_id (
1547 attachment, mime_attach->content_id);
1548 if (mime_attach->filename != NULL)
1549 camel_mime_part_set_filename (
1550 attachment, mime_attach->filename);
1551 if (mime_attach->description != NULL)
1552 camel_mime_part_set_description (
1553 attachment, mime_attach->description);
1554 if (mime_attach->disposition)
1555 camel_mime_part_set_disposition (
1556 attachment, "inline");
1557 else
1558 camel_mime_part_set_disposition (
1559 attachment, "attachment");
1560 e_msg_composer_attach (composer, attachment);
1561 g_object_unref (attachment);
1562
1563 g_free (mime_attach->filename);
1564 g_free (mime_attach->content_type);
1565 g_free (mime_attach->content_id);
1566 g_free (mime_attach->description);
1567 g_free (mime_attach->encoded_data);
1568 g_free (mime_attach);
1569 }
1570
1571 g_slist_free (attach_list);
1572 }
1573
1574 static ESource *
1575 find_enabled_identity (ESourceRegistry *registry,
1576 const gchar *id_address)
1577 {
1578 GList *list, *link;
1579 ESource *mail_identity = NULL;
1580 const gchar *extension_name;
1581
1582 if (id_address == NULL)
1583 return NULL;
1584
1585 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1586 list = e_source_registry_list_sources (registry, extension_name);
1587
1588 for (link = list; link != NULL; link = g_list_next (link)) {
1589 ESource *source = E_SOURCE (link->data);
1590 ESourceMailIdentity *extension;
1591 const gchar *address;
1592
1593 if (!e_source_get_enabled (source))
1594 continue;
1595
1596 extension = e_source_get_extension (source, extension_name);
1597 address = e_source_mail_identity_get_address (extension);
1598
1599 if (address == NULL)
1600 continue;
1601
1602 if (g_ascii_strcasecmp (address, id_address) == 0) {
1603 mail_identity = g_object_ref (source);
1604 break;
1605 }
1606 }
1607
1608 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1609
1610 return mail_identity;
1611 }
1612
1613 static void
1614 setup_from (ECalComponentItipMethod method,
1615 ECalComponent *comp,
1616 ECalClient *cal_client,
1617 EComposerHeaderTable *table)
1618 {
1619 ESourceRegistry *registry;
1620 ESource *source = NULL;
1621
1622 registry = e_composer_header_table_get_registry (table);
1623
1624 /* always use organizer's email when user is an organizer */
1625 if (itip_organizer_is_user (registry, comp, cal_client)) {
1626 ECalComponentOrganizer organizer = {0};
1627
1628 e_cal_component_get_organizer (comp, &organizer);
1629 if (organizer.value != NULL)
1630 source = find_enabled_identity (
1631 registry,
1632 itip_strip_mailto (organizer.value));
1633 }
1634
1635 if (source == NULL) {
1636 gchar *from = comp_from (method, comp, registry);
1637
1638 if (from != NULL)
1639 source = find_enabled_identity (registry, from);
1640
1641 g_free (from);
1642 }
1643
1644 if (source != NULL) {
1645 const gchar *uid;
1646
1647 uid = e_source_get_uid (source);
1648 e_composer_header_table_set_identity_uid (table, uid);
1649
1650 g_object_unref (source);
1651 }
1652 }
1653
1654 gboolean
1655 itip_send_comp (ESourceRegistry *registry,
1656 ECalComponentItipMethod method,
1657 ECalComponent *send_comp,
1658 ECalClient *cal_client,
1659 icalcomponent *zones,
1660 GSList *attachments_list,
1661 GSList *users,
1662 gboolean strip_alarms,
1663 gboolean only_new_attendees)
1664 {
1665 EShell *shell;
1666 EShellSettings *shell_settings;
1667 EMsgComposer *composer;
1668 EComposerHeaderTable *table;
1669 EDestination **destinations;
1670 ECalComponent *comp = NULL;
1671 icalcomponent *top_level = NULL;
1672 icaltimezone *default_zone;
1673 gchar *ical_string = NULL;
1674 gchar *content_type = NULL;
1675 gchar *subject = NULL;
1676 gboolean use_24_hour_format;
1677 gboolean retval = FALSE;
1678
1679 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
1680
1681 /* FIXME Pass this in. */
1682 shell = e_shell_get_default ();
1683 shell_settings = e_shell_get_shell_settings (shell);
1684
1685 default_zone = e_shell_settings_get_pointer (
1686 shell_settings, "cal-timezone");
1687
1688 use_24_hour_format = e_shell_settings_get_boolean (
1689 shell_settings, "cal-use-24-hour-format");
1690
1691 /* check whether backend could handle auto-saving requests/updates */
1692 if (method != E_CAL_COMPONENT_METHOD_PUBLISH && e_cal_client_check_save_schedules (cal_client))
1693 return TRUE;
1694
1695 /* Give the server a chance to manipulate the comp */
1696 if (method != E_CAL_COMPONENT_METHOD_PUBLISH) {
1697 d (printf ("itip-utils.c: itip_send_comp: calling comp_server_send... \n"));
1698 if (!comp_server_send (method, send_comp, cal_client, zones, &users))
1699 goto cleanup;
1700 }
1701
1702 /* check whether backend could handle sending requests/updates */
1703 if (method != E_CAL_COMPONENT_METHOD_PUBLISH &&
1704 e_client_check_capability (
1705 E_CLIENT (cal_client),
1706 CAL_STATIC_CAPABILITY_CREATE_MESSAGES)) {
1707 if (users) {
1708 g_slist_foreach (users, (GFunc) g_free, NULL);
1709 g_slist_free (users);
1710 }
1711 return TRUE;
1712 }
1713
1714 /* Tidy up the comp */
1715 comp = comp_compliant (
1716 registry, method, send_comp, cal_client,
1717 zones, default_zone, strip_alarms);
1718
1719 if (comp == NULL)
1720 goto cleanup;
1721
1722 /* Recipients */
1723 destinations = comp_to_list (
1724 registry, method, comp, users, FALSE,
1725 only_new_attendees ? g_object_get_data (
1726 G_OBJECT (send_comp), "new-attendees") : NULL);
1727 if (method != E_CAL_COMPONENT_METHOD_PUBLISH) {
1728 if (destinations == NULL) {
1729 /* We sent them all via the server */
1730 retval = TRUE;
1731 goto cleanup;
1732 }
1733 }
1734
1735 /* Subject information */
1736 subject = comp_subject (registry, method, comp);
1737
1738 composer = e_msg_composer_new (shell);
1739 table = e_msg_composer_get_header_table (composer);
1740
1741 setup_from (method, send_comp, cal_client, table);
1742 e_composer_header_table_set_subject (table, subject);
1743 e_composer_header_table_set_destinations_to (table, destinations);
1744
1745 e_destination_freev (destinations);
1746
1747 /* Content type */
1748 content_type = comp_content_type (comp, method);
1749
1750 top_level = comp_toplevel_with_zones (method, comp, cal_client, zones);
1751 ical_string = icalcomponent_as_ical_string_r (top_level);
1752
1753 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_EVENT) {
1754 e_msg_composer_set_body (composer, ical_string, content_type);
1755 } else {
1756 CamelMimePart *attachment;
1757 const gchar *filename;
1758 gchar *description;
1759 gchar *body;
1760
1761 filename = comp_filename (comp);
1762 description = comp_description (comp, use_24_hour_format);
1763
1764 body = camel_text_to_html (
1765 description, CAMEL_MIME_FILTER_TOHTML_PRE, 0);
1766 e_msg_composer_set_body_text (composer, body, TRUE);
1767 g_free (body);
1768
1769 attachment = camel_mime_part_new ();
1770 camel_mime_part_set_content (
1771 attachment, ical_string,
1772 strlen (ical_string), content_type);
1773 if (filename != NULL && *filename != '\0')
1774 camel_mime_part_set_filename (attachment, filename);
1775 if (description != NULL && *description != '\0')
1776 camel_mime_part_set_description (attachment, description);
1777 camel_mime_part_set_disposition (attachment, "inline");
1778 e_msg_composer_attach (composer, attachment);
1779 g_object_unref (attachment);
1780
1781 g_free (description);
1782 }
1783
1784 append_cal_attachments (composer, comp, attachments_list);
1785
1786 if ((method == E_CAL_COMPONENT_METHOD_PUBLISH) && !users)
1787 gtk_widget_show (GTK_WIDGET (composer));
1788 else
1789 e_msg_composer_send (composer);
1790
1791 retval = TRUE;
1792
1793 cleanup:
1794 if (comp != NULL)
1795 g_object_unref (comp);
1796 if (top_level != NULL)
1797 icalcomponent_free (top_level);
1798
1799 if (users) {
1800 g_slist_foreach (users, (GFunc) g_free, NULL);
1801 g_slist_free (users);
1802 }
1803
1804 g_free (content_type);
1805 g_free (subject);
1806 g_free (ical_string);
1807
1808 return retval;
1809 }
1810
1811 gboolean
1812 reply_to_calendar_comp (ESourceRegistry *registry,
1813 ECalComponentItipMethod method,
1814 ECalComponent *send_comp,
1815 ECalClient *cal_client,
1816 gboolean reply_all,
1817 icalcomponent *zones,
1818 GSList *attachments_list)
1819 {
1820 EShell *shell;
1821 EShellSettings *shell_settings;
1822 EMsgComposer *composer;
1823 EComposerHeaderTable *table;
1824 EDestination **destinations;
1825 ECalComponent *comp = NULL;
1826 icalcomponent *top_level = NULL;
1827 icaltimezone *default_zone;
1828 GSList *users = NULL;
1829 gchar *subject = NULL;
1830 gchar *ical_string = NULL;
1831 gboolean retval = FALSE;
1832
1833 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
1834
1835 /* FIXME Pass this in. */
1836 shell = e_shell_get_default ();
1837 shell_settings = e_shell_get_shell_settings (shell);
1838
1839 default_zone = e_shell_settings_get_pointer (
1840 shell_settings, "cal-timezone");
1841
1842 /* Tidy up the comp */
1843 comp = comp_compliant (
1844 registry, method, send_comp, cal_client,
1845 zones, default_zone, TRUE);
1846 if (comp == NULL)
1847 goto cleanup;
1848
1849 /* Recipients */
1850 destinations = comp_to_list (
1851 registry, method, comp, users, reply_all, NULL);
1852
1853 /* Subject information */
1854 subject = comp_subject (registry, method, comp);
1855
1856 composer = e_msg_composer_new (shell);
1857 table = e_msg_composer_get_header_table (composer);
1858
1859 setup_from (method, send_comp, cal_client, table);
1860 e_composer_header_table_set_subject (table, subject);
1861 e_composer_header_table_set_destinations_to (table, destinations);
1862
1863 e_destination_freev (destinations);
1864
1865 top_level = comp_toplevel_with_zones (method, comp, cal_client, zones);
1866 ical_string = icalcomponent_as_ical_string_r (top_level);
1867
1868 if (e_cal_component_get_vtype (comp) == E_CAL_COMPONENT_EVENT) {
1869
1870 GString *body;
1871 gchar *orig_from = NULL;
1872 const gchar *description = NULL;
1873 gchar *subject = NULL;
1874 const gchar *location = NULL;
1875 gchar *time = NULL;
1876 gchar *html_description = NULL;
1877 GSList *text_list = NULL;
1878 ECalComponentOrganizer organizer;
1879 ECalComponentText text;
1880 ECalComponentDateTime dtstart;
1881 icaltimezone *start_zone = NULL;
1882 time_t start;
1883
1884 e_cal_component_get_description_list (comp, &text_list);
1885
1886 if (text_list) {
1887 ECalComponentText text = *((ECalComponentText *) text_list->data);
1888 if (text.value)
1889 description = text.value;
1890 else
1891 description = "";
1892 } else {
1893 description = "";
1894 }
1895
1896 e_cal_component_free_text_list (text_list);
1897
1898 e_cal_component_get_summary (comp, &text);
1899 if (text.value)
1900 subject = g_strdup (text.value);
1901
1902 e_cal_component_get_organizer (comp, &organizer);
1903 if (organizer.value)
1904 orig_from = g_strdup (itip_strip_mailto (organizer.value));
1905
1906 e_cal_component_get_location (comp, &location);
1907 if (!location)
1908 location = "Unspecified";
1909
1910 e_cal_component_get_dtstart (comp, &dtstart);
1911 if (dtstart.value) {
1912 start_zone = icaltimezone_get_builtin_timezone_from_tzid (dtstart.tzid);
1913 if (!start_zone && dtstart.tzid) {
1914 GError *error = NULL;
1915
1916 e_cal_client_get_timezone_sync (
1917 cal_client, dtstart.tzid,
1918 &start_zone, NULL, &error);
1919
1920 if (error != NULL) {
1921 g_warning (
1922 "%s: Couldn't get timezone '%s' from server: %s",
1923 G_STRFUNC,
1924 dtstart.tzid ? dtstart.tzid : "",
1925 error->message);
1926 g_error_free (error);
1927 }
1928 }
1929
1930 if (!start_zone || dtstart.value->is_date)
1931 start_zone = default_zone;
1932
1933 start = icaltime_as_timet_with_zone (*dtstart.value, start_zone);
1934 time = g_strdup (ctime (&start));
1935 }
1936
1937 body = g_string_new (
1938 "<br><br><hr><br><b>"
1939 "______ Original Appointment ______ "
1940 "</b><br><br><table>");
1941
1942 if (orig_from && *orig_from)
1943 g_string_append_printf (
1944 body,
1945 "<tr><td><b>From</b></td>"
1946 "<td>:</td><td>%s</td></tr>", orig_from);
1947 g_free (orig_from);
1948
1949 if (subject)
1950 g_string_append_printf (
1951 body,
1952 "<tr><td><b>Subject</b></td>"
1953 "<td>:</td><td>%s</td></tr>", subject);
1954 g_free (subject);
1955
1956 g_string_append_printf (
1957 body,
1958 "<tr><td><b>Location</b></td>"
1959 "<td>:</td><td>%s</td></tr>", location);
1960
1961 if (time)
1962 g_string_append_printf (
1963 body,
1964 "<tr><td><b>Time</b></td>"
1965 "<td>:</td><td>%s</td></tr>", time);
1966 g_free (time);
1967
1968 g_string_append_printf (body, "</table><br>");
1969
1970 html_description = html_new_lines_for (description);
1971 g_string_append (body, html_description);
1972 g_free (html_description);
1973
1974 e_msg_composer_set_body_text (composer, body->str, TRUE);
1975 g_string_free (body, TRUE);
1976 }
1977
1978 gtk_widget_show (GTK_WIDGET (composer));
1979
1980 retval = TRUE;
1981
1982 cleanup:
1983
1984 if (comp != NULL)
1985 g_object_unref (comp);
1986 if (top_level != NULL)
1987 icalcomponent_free (top_level);
1988
1989 if (users) {
1990 g_slist_foreach (users, (GFunc) g_free, NULL);
1991 g_slist_free (users);
1992 }
1993
1994 g_free (subject);
1995 g_free (ical_string);
1996 return retval;
1997 }
1998
1999 gboolean
2000 itip_publish_begin (ECalComponent *pub_comp,
2001 ECalClient *cal_client,
2002 gboolean cloned,
2003 ECalComponent **clone)
2004 {
2005 icalcomponent *icomp = NULL, *icomp_clone = NULL;
2006 icalproperty *prop;
2007
2008 if (e_cal_component_get_vtype (pub_comp) == E_CAL_COMPONENT_FREEBUSY) {
2009
2010 if (!cloned)
2011 *clone = e_cal_component_clone (pub_comp);
2012 else {
2013
2014 icomp = e_cal_component_get_icalcomponent (pub_comp);
2015 icomp_clone = e_cal_component_get_icalcomponent (*clone);
2016 for (prop = icalcomponent_get_first_property (icomp,
2017 ICAL_FREEBUSY_PROPERTY);
2018 prop != NULL;
2019 prop = icalcomponent_get_next_property (
2020 icomp,
2021 ICAL_FREEBUSY_PROPERTY))
2022 {
2023 icalproperty *p;
2024
2025 p = icalproperty_new_clone (prop);
2026 icalcomponent_add_property (icomp_clone, p);
2027 }
2028 }
2029 }
2030
2031 return TRUE;
2032 }
2033
2034 static void
2035 fb_sort (struct icalperiodtype *ipt,
2036 gint fb_count)
2037 {
2038 gint i,j;
2039
2040 if (ipt == NULL || fb_count == 0)
2041 return;
2042
2043 for (i = 0; i < fb_count - 1; i++) {
2044 for (j = i + 1; j < fb_count; j++) {
2045 struct icalperiodtype temp;
2046
2047 if (icaltime_compare (ipt[i].start, ipt[j].start) < 0)
2048 continue;
2049
2050 if (icaltime_compare (ipt[i].start, ipt[j].start) == 0) {
2051 if (icaltime_compare (ipt[i].end,
2052 ipt[j].start) < 0)
2053 continue;
2054 }
2055 temp = ipt[i];
2056 ipt[i] = ipt[j];
2057 ipt[j] = temp;
2058 }
2059 }
2060 }
2061
2062 static icalcomponent *
2063 comp_fb_normalize (icalcomponent *icomp)
2064 {
2065 icalcomponent *iclone;
2066 icalproperty *prop, *p;
2067 const gchar *uid, *comment;
2068 struct icaltimetype itt;
2069 gint fb_count, i = 0, j;
2070 struct icalperiodtype *ipt;
2071
2072 iclone = icalcomponent_new (ICAL_VFREEBUSY_COMPONENT);
2073
2074 prop = icalcomponent_get_first_property (
2075 icomp, ICAL_ORGANIZER_PROPERTY);
2076 if (prop) {
2077 p = icalproperty_new_clone (prop);
2078 icalcomponent_add_property (iclone, p);
2079 }
2080
2081 itt = icalcomponent_get_dtstart (icomp);
2082 icalcomponent_set_dtstart (iclone, itt);
2083
2084 itt = icalcomponent_get_dtend (icomp);
2085 icalcomponent_set_dtend (iclone, itt);
2086
2087 fb_count = icalcomponent_count_properties (
2088 icomp, ICAL_FREEBUSY_PROPERTY);
2089 ipt = g_new0 (struct icalperiodtype, fb_count + 1);
2090
2091 for (prop = icalcomponent_get_first_property (icomp, ICAL_FREEBUSY_PROPERTY);
2092 prop != NULL;
2093 prop = icalcomponent_get_next_property (icomp, ICAL_FREEBUSY_PROPERTY))
2094 {
2095 ipt[i] = icalproperty_get_freebusy (prop);
2096 i++;
2097 }
2098
2099 fb_sort (ipt, fb_count);
2100
2101 for (j = 0; j <= fb_count - 1; j++) {
2102 icalparameter *param;
2103
2104 prop = icalproperty_new_freebusy (ipt[j]);
2105 param = icalparameter_new_fbtype (ICAL_FBTYPE_BUSY);
2106 icalproperty_add_parameter (prop, param);
2107 icalcomponent_add_property (iclone, prop);
2108 }
2109 g_free (ipt);
2110
2111 /* Should I strip this RFC 2446 says there must not be a UID
2112 if the METHOD is PUBLISH?? */
2113 uid = icalcomponent_get_uid (icomp);
2114 if (uid)
2115 icalcomponent_set_uid (iclone, uid);
2116
2117 itt = icaltime_from_timet_with_zone (
2118 time (NULL), FALSE,
2119 icaltimezone_get_utc_timezone ());
2120 icalcomponent_set_dtstamp (iclone, itt);
2121
2122 prop = icalcomponent_get_first_property (icomp, ICAL_URL_PROPERTY);
2123 if (prop) {
2124 p = icalproperty_new_clone (prop);
2125 icalcomponent_add_property (iclone, p);
2126 }
2127
2128 comment = icalcomponent_get_comment (icomp);
2129 if (comment)
2130 icalcomponent_set_comment (iclone, comment);
2131
2132 for (prop = icalcomponent_get_first_property (icomp, ICAL_X_PROPERTY);
2133 prop != NULL;
2134 prop = icalcomponent_get_next_property (icomp, ICAL_X_PROPERTY))
2135 {
2136 p = icalproperty_new_clone (prop);
2137 icalcomponent_add_property (iclone, p);
2138 }
2139
2140 return iclone;
2141 }
2142
2143 gboolean
2144 itip_publish_comp (ECalClient *cal_client,
2145 gchar *uri,
2146 gchar *username,
2147 gchar *password,
2148 ECalComponent **pub_comp)
2149 {
2150 icalcomponent *toplevel = NULL, *icalcomp = NULL;
2151 icalcomponent *icomp = NULL;
2152 SoupSession *session;
2153 SoupMessage *msg;
2154 SoupURI *real_uri;
2155 gchar *ical_string = NULL;
2156
2157 toplevel = e_cal_util_new_top_level ();
2158 icalcomponent_set_method (toplevel, ICAL_METHOD_PUBLISH);
2159
2160 e_cal_component_set_url (*pub_comp, uri);
2161
2162 icalcomp = e_cal_component_get_icalcomponent (*pub_comp);
2163
2164 icomp = comp_fb_normalize (icalcomp);
2165
2166 icalcomponent_add_component (toplevel, icomp);
2167
2168 /* Publish the component */
2169 session = soup_session_async_new ();
2170 g_object_set (session, SOUP_SESSION_TIMEOUT, 90, NULL);
2171
2172 real_uri = soup_uri_new (uri);
2173 if (!real_uri || !real_uri->host) {
2174 g_warning (G_STRLOC ": Invalid URL: %s", uri);
2175 g_object_unref (session);
2176 return FALSE;
2177 }
2178
2179 soup_uri_set_user (real_uri, username);
2180 soup_uri_set_password (real_uri, password);
2181
2182 /* build the message */
2183 msg = soup_message_new_from_uri (SOUP_METHOD_PUT, real_uri);
2184 soup_uri_free (real_uri);
2185 if (!msg) {
2186 g_warning (G_STRLOC ": Could not build SOAP message");
2187 g_object_unref (session);
2188 return FALSE;
2189 }
2190
2191 soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
2192 ical_string = icalcomponent_as_ical_string_r (toplevel);
2193 soup_message_set_request (
2194 msg, "text/calendar", SOUP_MEMORY_TEMPORARY,
2195 ical_string, strlen (ical_string));
2196
2197 /* send message to server */
2198 soup_session_send_message (session, msg);
2199 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
2200 g_warning (
2201 G_STRLOC ": Could not publish Free/Busy: %d: %s",
2202 msg->status_code,
2203 msg->reason_phrase);
2204 g_object_unref (msg);
2205 g_object_unref (session);
2206 g_free (ical_string);
2207 return FALSE;
2208 }
2209
2210 g_object_unref (msg);
2211 g_object_unref (session);
2212 g_free (ical_string);
2213
2214 return TRUE;
2215 }
2216
2217 static gboolean
2218 check_time (const struct icaltimetype tmval,
2219 gboolean can_null_time)
2220 {
2221 if (icaltime_is_null_time (tmval))
2222 return can_null_time;
2223
2224 return icaltime_is_valid_time (tmval) &&
2225 tmval.month >= 1 && tmval.month <= 12 &&
2226 tmval.day >= 1 && tmval.day <= 31 &&
2227 tmval.hour >= 0 && tmval.hour < 24 &&
2228 tmval.minute >= 0 && tmval.minute < 60 &&
2229 tmval.second >= 0 && tmval.second < 60;
2230 }
2231
2232 /* Returns whether the passed-in icalcomponent is valid or not.
2233 * It does some sanity checks on values too. */
2234 gboolean
2235 is_icalcomp_valid (icalcomponent *icalcomp)
2236 {
2237 if (!icalcomp || !icalcomponent_is_valid (icalcomp))
2238 return FALSE;
2239
2240 switch (icalcomponent_isa (icalcomp)) {
2241 case ICAL_VEVENT_COMPONENT:
2242 return check_time (icalcomponent_get_dtstart (icalcomp), FALSE) &&
2243 check_time (icalcomponent_get_dtend (icalcomp), TRUE);
2244 case ICAL_VTODO_COMPONENT:
2245 return check_time (icalcomponent_get_dtstart (icalcomp), TRUE) &&
2246 check_time (icalcomponent_get_due (icalcomp), TRUE);
2247 case ICAL_VJOURNAL_COMPONENT:
2248 return check_time (icalcomponent_get_dtstart (icalcomp), TRUE) &&
2249 check_time (icalcomponent_get_dtend (icalcomp), TRUE);
2250 default:
2251 break;
2252 }
2253
2254 return TRUE;
2255 }