1 /*======================================================================
2 FILE: icalcomponent.c
3 CREATOR: eric 28 April 1999
4
5 (C) COPYRIGHT 2000, Eric Busboom <eric@civicknowledge.com>
6
7 This library is free software; you can redistribute it and/or modify
8 it under the terms of either:
9
10 The LGPL as published by the Free Software Foundation, version
11 2.1, available at: https://www.gnu.org/licenses/lgpl-2.1.html
12
13 Or:
14
15 The Mozilla Public License Version 2.0. You may obtain a copy of
16 the License at https://www.mozilla.org/MPL/
17 ======================================================================*/
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include "icalcomponent.h"
24 #include "icalerror.h"
25 #include "icalmemory.h"
26 #include "icalparser.h"
27 #include "icalproperty_p.h"
28 #include "icalrestriction.h"
29 #include "icaltimezone.h"
30
31 #include <assert.h>
32 #include <stdlib.h>
33 #include <limits.h>
34
35 struct icalcomponent_impl
36 {
37 char id[5];
38 icalcomponent_kind kind;
39 char *x_name;
40 pvl_list properties;
41 pvl_elem property_iterator;
42 pvl_list components;
43 pvl_elem component_iterator;
44 struct icalcomponent_impl *parent;
45
46 /** An array of icaltimezone structs. We use this so we can do fast
47 lookup of timezones using binary searches. timezones_sorted is
48 set to 0 whenever we add a timezone, so we remember to sort the
49 array before doing a binary search. */
50 icalarray *timezones;
51 int timezones_sorted;
52 };
53
54 static void icalcomponent_add_children(icalcomponent *impl, va_list args);
55 static icalcomponent *icalcomponent_new_impl(icalcomponent_kind kind);
56
57 static void icalcomponent_merge_vtimezone(icalcomponent *comp,
58 icalcomponent *vtimezone, icalarray *tzids_to_rename);
59 static void icalcomponent_handle_conflicting_vtimezones(icalcomponent *comp,
60 icalcomponent *vtimezone,
61 icalproperty *tzid_prop,
62 const char *tzid,
63 icalarray *tzids_to_rename);
64 static size_t icalcomponent_get_tzid_prefix_len(const char *tzid);
65 static void icalcomponent_rename_tzids(icalcomponent *comp, icalarray *rename_table);
66 static void icalcomponent_rename_tzids_callback(icalparameter *param, void *data);
67 static int icalcomponent_compare_vtimezones(icalcomponent *vtimezone1, icalcomponent *vtimezone2);
68 static int icalcomponent_compare_timezone_fn(const void *elem1, const void *elem2);
69
icalcomponent_add_children(icalcomponent * impl,va_list args)70 void icalcomponent_add_children(icalcomponent *impl, va_list args)
71 {
72 void *vp;
73
74 while ((vp = va_arg(args, void *)) != 0)
75 {
76 assert(icalcomponent_isa_component(vp) != 0 || icalproperty_isa_property(vp) != 0);
77
78 if (icalcomponent_isa_component(vp) != 0) {
79 icalcomponent_add_component(impl, (icalcomponent *) vp);
80
81 } else if (icalproperty_isa_property(vp) != 0) {
82 icalcomponent_add_property(impl, (icalproperty *) vp);
83 }
84 }
85 }
86
icalcomponent_new_impl(icalcomponent_kind kind)87 static icalcomponent *icalcomponent_new_impl(icalcomponent_kind kind)
88 {
89 icalcomponent *comp;
90
91 if (!icalcomponent_kind_is_valid(kind))
92 return NULL;
93
94 if ((comp = (icalcomponent *) malloc(sizeof(icalcomponent))) == 0) {
95 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
96 return 0;
97 }
98
99 memset(comp, 0, sizeof(icalcomponent));
100
101 strcpy(comp->id, "comp");
102
103 comp->kind = kind;
104 comp->properties = pvl_newlist();
105 comp->components = pvl_newlist();
106 comp->timezones_sorted = 1;
107
108 return comp;
109 }
110
111 /** @brief Constructor
112 */
icalcomponent_new(icalcomponent_kind kind)113 icalcomponent *icalcomponent_new(icalcomponent_kind kind)
114 {
115 return icalcomponent_new_impl(kind);
116 }
117
118 /** @brief Constructor
119 */
120 #pragma clang diagnostic push
121 #pragma clang diagnostic ignored "-Wvarargs"
icalcomponent_vanew(icalcomponent_kind kind,...)122 icalcomponent *icalcomponent_vanew(icalcomponent_kind kind, ...)
123 {
124 va_list args;
125
126 icalcomponent *impl = icalcomponent_new_impl(kind);
127
128 if (impl == 0) {
129 return 0;
130 }
131
132 va_start(args, kind);
133 icalcomponent_add_children(impl, args);
134 va_end(args);
135
136 return impl;
137 }
138 #pragma clang diagnostic pop
139
140 /** @brief Constructor
141 */
icalcomponent_new_from_string(const char * str)142 icalcomponent *icalcomponent_new_from_string(const char *str)
143 {
144 return icalparser_parse_string(str);
145 }
146
147 /** @brief Constructor
148 */
icalcomponent_new_clone(icalcomponent * old)149 icalcomponent *icalcomponent_new_clone(icalcomponent *old)
150 {
151 icalcomponent *new;
152 icalproperty *p;
153 icalcomponent *c;
154 pvl_elem itr;
155
156 icalerror_check_arg_rz((old != 0), "component");
157
158 new = icalcomponent_new_impl(old->kind);
159
160 if (new == 0) {
161 return 0;
162 }
163
164 for (itr = pvl_head(old->properties); itr != 0; itr = pvl_next(itr)) {
165 p = (icalproperty *) pvl_data(itr);
166 icalcomponent_add_property(new, icalproperty_new_clone(p));
167 }
168
169 for (itr = pvl_head(old->components); itr != 0; itr = pvl_next(itr)) {
170 c = (icalcomponent *) pvl_data(itr);
171 icalcomponent_add_component(new, icalcomponent_new_clone(c));
172 }
173
174 return new;
175 }
176
177 /** @brief Constructor
178 */
icalcomponent_new_x(const char * x_name)179 icalcomponent *icalcomponent_new_x(const char *x_name)
180 {
181 icalcomponent *comp = icalcomponent_new_impl(ICAL_X_COMPONENT);
182
183 if (!comp) {
184 return 0;
185 }
186 comp->x_name = icalmemory_strdup(x_name);
187 return comp;
188 }
189
190 /*** @brief Destructor
191 */
icalcomponent_free(icalcomponent * c)192 void icalcomponent_free(icalcomponent *c)
193 {
194 icalproperty *prop;
195 icalcomponent *comp;
196
197 icalerror_check_arg_rv((c != 0), "component");
198
199 if (c != 0) {
200 if (c->parent != 0) {
201 return;
202 }
203
204 if (c->properties != 0) {
205 while ((prop = pvl_pop(c->properties)) != 0) {
206 icalproperty_set_parent(prop, 0);
207 icalproperty_free(prop);
208 }
209 pvl_free(c->properties);
210 }
211
212 while ((comp = pvl_data(pvl_head(c->components))) != 0) {
213 icalcomponent_remove_component(c, comp);
214 icalcomponent_free(comp);
215 }
216
217 pvl_free(c->components);
218
219 if (c->x_name != 0) {
220 free(c->x_name);
221 }
222
223 if (c->timezones) {
224 icaltimezone_array_free(c->timezones);
225 c->timezones = 0;
226 }
227
228 c->kind = ICAL_NO_COMPONENT;
229 c->properties = 0;
230 c->property_iterator = 0;
231 c->components = 0;
232 c->component_iterator = 0;
233 c->x_name = 0;
234 c->id[0] = 'X';
235 c->timezones = NULL;
236
237 free(c);
238 }
239 }
240
icalcomponent_as_ical_string(icalcomponent * impl)241 char *icalcomponent_as_ical_string(icalcomponent *impl)
242 {
243 char *buf;
244
245 buf = icalcomponent_as_ical_string_r(impl);
246 icalmemory_add_tmp_buffer(buf);
247 return buf;
248 }
249
icalcomponent_as_ical_string_r(icalcomponent * impl)250 char *icalcomponent_as_ical_string_r(icalcomponent *impl)
251 {
252 char *buf;
253 char *tmp_buf;
254 size_t buf_size = 1024;
255 char *buf_ptr = 0;
256 pvl_elem itr;
257
258 /* RFC5545 explicitly says that the newline is *ALWAYS* a \r\n (CRLF)!!!! */
259 const char newline[] = "\r\n";
260
261 icalcomponent *c;
262 icalproperty *p;
263 icalcomponent_kind kind = icalcomponent_isa(impl);
264
265 const char *kind_string;
266
267 icalerror_check_arg_rz((impl != 0), "component");
268 icalerror_check_arg_rz((kind != ICAL_NO_COMPONENT), "component kind is ICAL_NO_COMPONENT");
269
270 if (kind != ICAL_X_COMPONENT) {
271 kind_string = icalcomponent_kind_to_string(kind);
272 } else {
273 kind_string = impl->x_name;
274 }
275
276 icalerror_check_arg_rz((kind_string != 0), "Unknown kind of component");
277
278 buf = icalmemory_new_buffer(buf_size);
279 buf_ptr = buf;
280
281 icalmemory_append_string(&buf, &buf_ptr, &buf_size, "BEGIN:");
282 icalmemory_append_string(&buf, &buf_ptr, &buf_size, kind_string);
283 icalmemory_append_string(&buf, &buf_ptr, &buf_size, newline);
284
285 for (itr = pvl_head(impl->properties); itr != 0; itr = pvl_next(itr)) {
286 p = (icalproperty *) pvl_data(itr);
287
288 icalerror_assert((p != 0), "Got a null property");
289 tmp_buf = icalproperty_as_ical_string_r(p);
290
291 icalmemory_append_string(&buf, &buf_ptr, &buf_size, tmp_buf);
292 free(tmp_buf);
293 }
294
295 for (itr = pvl_head(impl->components); itr != 0; itr = pvl_next(itr)) {
296 c = (icalcomponent *) pvl_data(itr);
297
298 tmp_buf = icalcomponent_as_ical_string_r(c);
299
300 icalmemory_append_string(&buf, &buf_ptr, &buf_size, tmp_buf);
301 free(tmp_buf);
302 }
303
304 icalmemory_append_string(&buf, &buf_ptr, &buf_size, "END:");
305 icalmemory_append_string(&buf, &buf_ptr, &buf_size, icalcomponent_kind_to_string(kind));
306 icalmemory_append_string(&buf, &buf_ptr, &buf_size, newline);
307
308 return buf;
309 }
310
icalcomponent_is_valid(icalcomponent * component)311 int icalcomponent_is_valid(icalcomponent *component)
312 {
313 if ((strcmp(component->id, "comp") == 0) && component->kind != ICAL_NO_COMPONENT) {
314 return 1;
315 } else {
316 return 0;
317 }
318 }
319
icalcomponent_isa(const icalcomponent * component)320 icalcomponent_kind icalcomponent_isa(const icalcomponent *component)
321 {
322 icalerror_check_arg_rx((component != 0), "component", ICAL_NO_COMPONENT);
323
324 return component->kind;
325 }
326
icalcomponent_isa_component(void * component)327 int icalcomponent_isa_component(void *component)
328 {
329 icalcomponent *impl = component;
330
331 icalerror_check_arg_rz((component != 0), "component");
332
333 if (strcmp(impl->id, "comp") == 0) {
334 return 1;
335 } else {
336 return 0;
337 }
338 }
339
icalcomponent_add_property(icalcomponent * component,icalproperty * property)340 void icalcomponent_add_property(icalcomponent *component, icalproperty *property)
341 {
342 icalerror_check_arg_rv((component != 0), "component");
343 icalerror_check_arg_rv((property != 0), "property");
344
345 icalerror_assert((!icalproperty_get_parent(property)),
346 "The property has already been added to a component. "
347 "Remove the property with icalcomponent_remove_property "
348 "before calling icalcomponent_add_property");
349
350 icalproperty_set_parent(property, component);
351
352 pvl_push(component->properties, property);
353 }
354
icalcomponent_remove_property(icalcomponent * component,icalproperty * property)355 void icalcomponent_remove_property(icalcomponent *component, icalproperty *property)
356 {
357 pvl_elem itr, next_itr;
358
359 icalerror_check_arg_rv((component != 0), "component");
360 icalerror_check_arg_rv((property != 0), "property");
361
362 #if defined(ICAL_REMOVE_NONMEMBER_COMPONENT_IS_ERROR)
363 icalerror_assert((icalproperty_get_parent(property)),
364 "The property is not a member of a component");
365 #else
366 if (icalproperty_get_parent(property) == 0) {
367 return;
368 }
369 #endif
370
371 for (itr = pvl_head(component->properties); itr != 0; itr = next_itr) {
372 next_itr = pvl_next(itr);
373
374 if (pvl_data(itr) == (void *)property) {
375
376 if (component->property_iterator == itr) {
377 component->property_iterator = pvl_next(itr);
378 }
379
380 (void)pvl_remove(component->properties, itr);
381 icalproperty_set_parent(property, 0);
382 }
383 }
384 }
385
icalcomponent_count_properties(icalcomponent * component,icalproperty_kind kind)386 int icalcomponent_count_properties(icalcomponent *component, icalproperty_kind kind)
387 {
388 int count = 0;
389 pvl_elem itr;
390
391 icalerror_check_arg_rz((component != 0), "component");
392
393 for (itr = pvl_head(component->properties); itr != 0; itr = pvl_next(itr)) {
394 if (kind == icalproperty_isa((icalproperty *) pvl_data(itr)) || kind == ICAL_ANY_PROPERTY) {
395 count++;
396 }
397 }
398
399 return count;
400 }
401
icalcomponent_get_current_property(icalcomponent * component)402 icalproperty *icalcomponent_get_current_property(icalcomponent *component)
403 {
404 icalerror_check_arg_rz((component != 0), "component");
405
406 if (component->property_iterator == 0) {
407 return 0;
408 }
409
410 return (icalproperty *) pvl_data(component->property_iterator);
411 }
412
icalcomponent_get_first_property(icalcomponent * c,icalproperty_kind kind)413 icalproperty *icalcomponent_get_first_property(icalcomponent *c, icalproperty_kind kind)
414 {
415 icalerror_check_arg_rz((c != 0), "component");
416
417 for (c->property_iterator = pvl_head(c->properties);
418 c->property_iterator != 0; c->property_iterator = pvl_next(c->property_iterator)) {
419
420 icalproperty *p = (icalproperty *) pvl_data(c->property_iterator);
421
422 if (icalproperty_isa(p) == kind || kind == ICAL_ANY_PROPERTY) {
423
424 return p;
425 }
426 }
427 return 0;
428 }
429
icalcomponent_get_next_property(icalcomponent * c,icalproperty_kind kind)430 icalproperty *icalcomponent_get_next_property(icalcomponent *c, icalproperty_kind kind)
431 {
432 icalerror_check_arg_rz((c != 0), "component");
433
434 if (c->property_iterator == 0) {
435 return 0;
436 }
437
438 for (c->property_iterator = pvl_next(c->property_iterator);
439 c->property_iterator != 0; c->property_iterator = pvl_next(c->property_iterator)) {
440
441 icalproperty *p = (icalproperty *) pvl_data(c->property_iterator);
442
443 if (icalproperty_isa(p) == kind || kind == ICAL_ANY_PROPERTY) {
444
445 return p;
446 }
447 }
448
449 return 0;
450 }
451
452 icalproperty **icalcomponent_get_properties(icalcomponent *component, icalproperty_kind kind);
453
icalcomponent_add_component(icalcomponent * parent,icalcomponent * child)454 void icalcomponent_add_component(icalcomponent *parent, icalcomponent *child)
455 {
456 icalerror_check_arg_rv((parent != 0), "parent");
457 icalerror_check_arg_rv((child != 0), "child");
458
459 if (child->parent != 0) {
460 icalerror_set_errno(ICAL_USAGE_ERROR);
461 }
462
463 child->parent = parent;
464
465 /* Fix for Mozilla - bug 327602 */
466 if (child->kind != ICAL_VTIMEZONE_COMPONENT) {
467 pvl_push(parent->components, child);
468 } else {
469 /* VTIMEZONES should be first in the resulting VCALENDAR. */
470 pvl_unshift(parent->components, child);
471
472 /* Add the VTIMEZONE to our array. */
473 /* FIXME: Currently we are also creating this array when loading in
474 a builtin VTIMEZONE, when we don't need it. */
475 if (!parent->timezones)
476 parent->timezones = icaltimezone_array_new();
477
478 icaltimezone_array_append_from_vtimezone(parent->timezones, child);
479
480 /* Flag that we need to sort it before doing any binary searches. */
481 parent->timezones_sorted = 0;
482 }
483 }
484
icalcomponent_remove_component(icalcomponent * parent,icalcomponent * child)485 void icalcomponent_remove_component(icalcomponent *parent, icalcomponent *child)
486 {
487 pvl_elem itr, next_itr;
488
489 icalerror_check_arg_rv((parent != 0), "parent");
490 icalerror_check_arg_rv((child != 0), "child");
491
492 /* If the component is a VTIMEZONE, remove it from our array as well. */
493 if (child->kind == ICAL_VTIMEZONE_COMPONENT) {
494 icaltimezone *zone;
495 size_t i, num_elements;
496
497 num_elements = parent->timezones ? parent->timezones->num_elements : 0;
498 for (i = 0; i < num_elements; i++) {
499 zone = icalarray_element_at(parent->timezones, i);
500 if (icaltimezone_get_component(zone) == child) {
501 icaltimezone_free(zone, 0);
502 icalarray_remove_element_at(parent->timezones, i);
503 break;
504 }
505 }
506 }
507
508 for (itr = pvl_head(parent->components); itr != 0; itr = next_itr) {
509 next_itr = pvl_next(itr);
510
511 if (pvl_data(itr) == (void *)child) {
512
513 if (parent->component_iterator == itr) {
514 /* Don't let the current iterator become invalid */
515
516 /* HACK. The semantics for this are troubling. */
517 parent->component_iterator = pvl_next(parent->component_iterator);
518 }
519 (void)pvl_remove(parent->components, itr);
520 child->parent = 0;
521 break;
522 }
523 }
524 }
525
icalcomponent_count_components(icalcomponent * component,icalcomponent_kind kind)526 int icalcomponent_count_components(icalcomponent *component, icalcomponent_kind kind)
527 {
528 int count = 0;
529 pvl_elem itr;
530
531 icalerror_check_arg_rz((component != 0), "component");
532
533 for (itr = pvl_head(component->components); itr != 0; itr = pvl_next(itr)) {
534 if (kind == icalcomponent_isa((icalcomponent *) pvl_data(itr)) ||
535 kind == ICAL_ANY_COMPONENT) {
536 count++;
537 }
538 }
539
540 return count;
541 }
542
icalcomponent_get_current_component(icalcomponent * component)543 icalcomponent *icalcomponent_get_current_component(icalcomponent *component)
544 {
545 icalerror_check_arg_rz((component != 0), "component");
546
547 if (component->component_iterator == 0) {
548 return 0;
549 }
550
551 return (icalcomponent *) pvl_data(component->component_iterator);
552 }
553
icalcomponent_get_first_component(icalcomponent * c,icalcomponent_kind kind)554 icalcomponent *icalcomponent_get_first_component(icalcomponent *c, icalcomponent_kind kind)
555 {
556 icalerror_check_arg_rz((c != 0), "component");
557
558 for (c->component_iterator = pvl_head(c->components);
559 c->component_iterator != 0; c->component_iterator = pvl_next(c->component_iterator)) {
560
561 icalcomponent *p = (icalcomponent *) pvl_data(c->component_iterator);
562
563 if (icalcomponent_isa(p) == kind || kind == ICAL_ANY_COMPONENT) {
564
565 return p;
566 }
567 }
568
569 return 0;
570 }
571
icalcomponent_get_next_component(icalcomponent * c,icalcomponent_kind kind)572 icalcomponent *icalcomponent_get_next_component(icalcomponent *c, icalcomponent_kind kind)
573 {
574 icalerror_check_arg_rz((c != 0), "component");
575
576 if (c->component_iterator == 0) {
577 return 0;
578 }
579
580 for (c->component_iterator = pvl_next(c->component_iterator);
581 c->component_iterator != 0; c->component_iterator = pvl_next(c->component_iterator)) {
582
583 icalcomponent *p = (icalcomponent *) pvl_data(c->component_iterator);
584
585 if (icalcomponent_isa(p) == kind || kind == ICAL_ANY_COMPONENT) {
586
587 return p;
588 }
589 }
590
591 return 0;
592 }
593
icalcomponent_get_first_real_component(icalcomponent * c)594 icalcomponent *icalcomponent_get_first_real_component(icalcomponent *c)
595 {
596 icalcomponent *comp;
597
598 for (comp = icalcomponent_get_first_component(c, ICAL_ANY_COMPONENT);
599 comp != 0; comp = icalcomponent_get_next_component(c, ICAL_ANY_COMPONENT)) {
600
601 icalcomponent_kind kind = icalcomponent_isa(comp);
602
603 if (kind == ICAL_VEVENT_COMPONENT ||
604 kind == ICAL_VTODO_COMPONENT ||
605 kind == ICAL_VJOURNAL_COMPONENT ||
606 kind == ICAL_VFREEBUSY_COMPONENT ||
607 kind == ICAL_VAVAILABILITY_COMPONENT ||
608 kind == ICAL_VPOLL_COMPONENT ||
609 kind == ICAL_VPATCH_COMPONENT ||
610 kind == ICAL_VQUERY_COMPONENT || kind == ICAL_VAGENDA_COMPONENT) {
611 return comp;
612 }
613 }
614 return 0;
615 }
616
617 /** @brief Get the timespan covered by this component, in UTC
618 *
619 * see icalcomponent_foreach_recurrence() for a better way to
620 * extract spans from an component.
621 *
622 * This method can be called on either a VCALENDAR or any real
623 * component. If the VCALENDAR contains no real component, but
624 * contains a VTIMEZONE, we return that span instead.
625 * This might not be a desirable behavior; we keep it for now
626 * for backward compatibility, but it might be deprecated at a
627 * future time.
628 *
629 * FIXME this API needs to be clarified. DTEND is defined as the
630 * first available time after the end of this event, so the span
631 * should actually end 1 second before DTEND.
632 */
633
icalcomponent_get_span(icalcomponent * comp)634 icaltime_span icalcomponent_get_span(icalcomponent *comp)
635 {
636 icalcomponent *inner;
637 icalcomponent_kind kind;
638 icaltime_span span;
639 struct icaltimetype start, end;
640
641 span.start = 0;
642 span.end = 0;
643 span.is_busy = 1;
644
645 /* initial Error checking */
646 if (comp == NULL) {
647 return span;
648 }
649
650 /* FIXME this might go away */
651 kind = icalcomponent_isa(comp);
652 if (kind == ICAL_VCALENDAR_COMPONENT) {
653 inner = icalcomponent_get_first_real_component(comp);
654
655 /* Maybe there is a VTIMEZONE in there */
656 if (inner == 0) {
657 inner = icalcomponent_get_first_component(comp, ICAL_VTIMEZONE_COMPONENT);
658 }
659
660 } else {
661 inner = comp;
662 }
663
664 if (inner == 0) {
665 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
666 /*icalerror_warn("icalcomponent_get_span: no component specified, \
667 or empty VCALENDAR component"); */
668 return span;
669 }
670
671 kind = icalcomponent_isa(inner);
672
673 if (!(kind == ICAL_VEVENT_COMPONENT ||
674 kind == ICAL_VJOURNAL_COMPONENT ||
675 kind == ICAL_VTODO_COMPONENT || kind == ICAL_VFREEBUSY_COMPONENT)) {
676 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
677 /*icalerror_warn("icalcomponent_get_span: no component specified, \
678 or empty VCALENDAR component"); */
679 return span;
680 }
681
682 /* Get to work. starting with DTSTART */
683 start = icalcomponent_get_dtstart(comp);
684 if (icaltime_is_null_time(start)) {
685 return span;
686 }
687 span.start = icaltime_as_timet_with_zone(start, icaltimezone_get_utc_timezone());
688
689 /* The end time could be specified as either a DTEND or a DURATION */
690 /* icalcomponent_get_dtend takes care of these cases. */
691 end = icalcomponent_get_dtend(comp);
692 if (icaltime_is_null_time(end)) {
693 if (!icaltime_is_date(start)) {
694 /* If dtstart is a DATE-TIME and there is no DTEND nor DURATION
695 it takes no time */
696 span.start = 0;
697 return span;
698 } else {
699 end = start;
700 }
701 }
702
703 span.end = icaltime_as_timet_with_zone(end, icaltimezone_get_utc_timezone());
704 if (icaltime_is_date(start)) {
705 /* Until the end of the day */
706 span.end += 60 * 60 * 24 - 1;
707 }
708
709 return span;
710 }
711
712 /**
713 * Decide if this recurrance is acceptable
714 *
715 * @param comp A valid icalcomponent.
716 * @param dtstart The base dtstart value for this component.
717 * @param recurtime The time to test against.
718 *
719 * @return true if the recurrence value is excluded, false otherwise.
720 *
721 * This function decides if a specific recurrence value is
722 * excluded by EXRULE or EXDATE properties.
723 *
724 * It's not the most efficient code. You might get better performance
725 * if you assume that recurtime is always increasing for each
726 * call. Then you could:
727 *
728 * - sort the EXDATE values
729 * - save the state of each EXRULE iterator for the next call.
730 *
731 * In this case though you don't need to worry how you call this
732 * function. It will always return the correct result.
733 */
734
icalproperty_recurrence_is_excluded(icalcomponent * comp,struct icaltimetype * dtstart,struct icaltimetype * recurtime)735 int icalproperty_recurrence_is_excluded(icalcomponent *comp,
736 struct icaltimetype *dtstart,
737 struct icaltimetype *recurtime)
738 {
739 icalproperty *exdate, *exrule;
740 pvl_elem property_iterator;
741
742 if (comp == NULL || dtstart == NULL || recurtime == NULL || icaltime_is_null_time(*recurtime)) {
743 /* BAD DATA */
744 return 1;
745 }
746
747 property_iterator = comp->property_iterator;
748
749 /** first test against the exdate values **/
750 for (exdate = icalcomponent_get_first_property(comp, ICAL_EXDATE_PROPERTY);
751 exdate != NULL; exdate = icalcomponent_get_next_property(comp, ICAL_EXDATE_PROPERTY)) {
752
753 struct icaltimetype exdatetime = icalproperty_get_datetime_with_component(exdate, comp);
754
755 if ((icaltime_is_date(exdatetime) &&
756 icaltime_compare_date_only(*recurtime, exdatetime) == 0) ||
757 (icaltime_compare(*recurtime, exdatetime) == 0)) {
758 /** MATCHED **/
759 comp->property_iterator = property_iterator;
760 return 1;
761 }
762 }
763
764 /** Now test against the EXRULEs **/
765 for (exrule = icalcomponent_get_first_property(comp, ICAL_EXRULE_PROPERTY);
766 exrule != NULL; exrule = icalcomponent_get_next_property(comp, ICAL_EXRULE_PROPERTY)) {
767
768 struct icalrecurrencetype recur = icalproperty_get_exrule(exrule);
769 icalrecur_iterator *exrule_itr = icalrecur_iterator_new(recur, *dtstart);
770 struct icaltimetype exrule_time;
771
772 while (exrule_itr) {
773 int result;
774
775 exrule_time = icalrecur_iterator_next(exrule_itr);
776
777 if (icaltime_is_null_time(exrule_time))
778 break;
779
780 result = icaltime_compare(*recurtime, exrule_time);
781 if (result == 0) {
782 icalrecur_iterator_free(exrule_itr);
783 comp->property_iterator = property_iterator;
784 return 1;
785 /** MATCH **/
786 }
787 if (result == 1)
788 break;
789 /** exrule_time > recurtime **/
790 }
791
792 icalrecur_iterator_free(exrule_itr);
793 }
794 comp->property_iterator = property_iterator;
795
796 return 0;/** no matches **/
797 }
798
799 /**
800 * @brief Return the busy status based on the TRANSP property.
801 *
802 * @param comp A valid icalcomponent.
803 *
804 * @return 1 if the event is a busy item, 0 if it is not.
805 */
806
icalcomponent_is_busy(icalcomponent * comp)807 static int icalcomponent_is_busy(icalcomponent *comp)
808 {
809 icalproperty *transp;
810 enum icalproperty_status status;
811 int ret = 1;
812
813 /** @todo check access control here, converting busy->free if the
814 permissions do not allow access... */
815
816 /* Is this a busy time? Check the TRANSP property */
817 transp = icalcomponent_get_first_property(comp, ICAL_TRANSP_PROPERTY);
818
819 if (transp) {
820 icalvalue *transp_val = icalproperty_get_value(transp);
821
822 switch (icalvalue_get_transp(transp_val)) {
823 case ICAL_TRANSP_OPAQUE:
824 case ICAL_TRANSP_OPAQUENOCONFLICT:
825 case ICAL_TRANSP_NONE:
826 ret = 1;
827 break;
828 case ICAL_TRANSP_TRANSPARENT:
829 case ICAL_TRANSP_TRANSPARENTNOCONFLICT:
830 ret = 0;
831 break;
832 default:
833 ret = 0;
834 break;
835 }
836 }
837 status = icalcomponent_get_status(comp);
838 if (ret && status) {
839 switch (status) {
840 case ICAL_STATUS_CANCELLED:
841 case ICAL_STATUS_TENTATIVE:
842 ret = 0;
843 break;
844 default:
845 break;
846 }
847 }
848 return (ret);
849 }
850
851 /**
852 * @brief cycle through all recurrances of an event
853 *
854 * @param comp A valid VEVENT component
855 * @param start Ignore timespans before this
856 * @param end Ignore timespans after this
857 * @param callback Function called for each timespan within the range
858 * @param callback_data Pointer passed back to the callback function
859 *
860 * This function will call the specified callback function for once
861 * for the base value of DTSTART, and foreach recurring date/time
862 * value.
863 *
864 * It will filter out events that are specified as an EXDATE or an EXRULE.
865 *
866 * @todo We do not filter out duplicate RRULES/RDATES
867 * @todo We do not handle RDATEs with explicit periods
868 */
869
icalcomponent_foreach_recurrence(icalcomponent * comp,struct icaltimetype start,struct icaltimetype end,void (* callback)(icalcomponent * comp,struct icaltime_span * span,void * data),void * callback_data)870 void icalcomponent_foreach_recurrence(icalcomponent *comp,
871 struct icaltimetype start,
872 struct icaltimetype end,
873 void (*callback) (icalcomponent *comp,
874 struct icaltime_span *span,
875 void *data), void *callback_data)
876 {
877 struct icaltimetype dtstart, dtend;
878 icaltime_span recurspan, basespan, limit_span;
879 time_t limit_start, limit_end;
880 time_t dtduration;
881 icalproperty *rrule, *rdate;
882 pvl_elem property_iterator; /* for saving the iterator */
883
884 if (comp == NULL || callback == NULL)
885 return;
886
887 dtstart = icalcomponent_get_dtstart(comp);
888
889 if (icaltime_is_null_time(dtstart) &&
890 icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) {
891 /* VTODO with no DTSTART - use DUE */
892 dtstart = icalcomponent_get_due(comp);
893 }
894 if (icaltime_is_null_time(dtstart))
895 return;
896
897 /* The end time could be specified as either a DTEND or a DURATION */
898 /* icalcomponent_get_dtend takes care of these cases. */
899 dtend = icalcomponent_get_dtend(comp);
900 if (icaltime_is_null_time(dtend) && icaltime_is_date(dtstart)) {
901 /* No DTEND or DURATION and DTSTART is DATE - duration is 1 day */
902 struct icaldurationtype dur = icaldurationtype_null_duration();
903
904 dur.days = 1;
905 dtend = icaltime_add(dtstart, dur);
906 }
907
908 /* Now set up the base span for this item, corresponding to the
909 base DTSTART and DTEND */
910 basespan = icaltime_span_new(dtstart, dtend, 1);
911
912 basespan.is_busy = icalcomponent_is_busy(comp);
913
914 /** Calculate the ceiling and floor values.. **/
915 limit_start = icaltime_as_timet_with_zone(start,
916 icaltimezone_get_utc_timezone());
917 if (!icaltime_is_null_time(end)) {
918 limit_end = icaltime_as_timet_with_zone(end,
919 icaltimezone_get_utc_timezone());
920 } else {
921 #if (SIZEOF_TIME_T > 4)
922 limit_end = (time_t) LONG_MAX;
923 #else
924 limit_end = (time_t) INT_MAX;
925 #endif
926 }
927 limit_span.start = limit_start;
928 limit_span.end = limit_end;
929
930 /* Do the callback for the DTSTART entry, ONLY if there is no RRULE.
931 Otherwise, the initial occurrence will be handled by the RRULE. */
932 rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
933 if ((rrule == NULL) &&
934 !icalproperty_recurrence_is_excluded(comp, &dtstart, &dtstart)) {
935 /** call callback action **/
936 if (icaltime_span_overlaps(&basespan, &limit_span))
937 (*callback) (comp, &basespan, callback_data);
938 }
939
940 recurspan = basespan;
941 dtduration = basespan.end - basespan.start;
942
943 /* Now cycle through the rrule entries */
944 for (; rrule != NULL;
945 rrule = icalcomponent_get_next_property(comp, ICAL_RRULE_PROPERTY)) {
946
947 struct icalrecurrencetype recur = icalproperty_get_rrule(rrule);
948 icalrecur_iterator *rrule_itr = icalrecur_iterator_new(recur, dtstart);
949 struct icaltimetype rrule_time;
950
951 if (!rrule_itr) continue;
952
953 if (recur.count == 0) {
954 icaltimetype mystart = start;
955
956 /* make sure we include any recurrence that ends in timespan */
957 icaltime_adjust(&mystart, 0, 0, 0, -(int)dtduration);
958 icalrecur_iterator_set_start(rrule_itr, mystart);
959 }
960
961 for (rrule_time = icalrecur_iterator_next(rrule_itr);
962 !icaltime_is_null_time(rrule_time);
963 rrule_time = icalrecur_iterator_next(rrule_itr)) {
964
965 /* if we have iterated past end time,
966 then no need to check any further */
967 if (icaltime_compare(rrule_time, end) > 0)
968 break;
969
970 recurspan.start =
971 icaltime_as_timet_with_zone(rrule_time,
972 rrule_time.zone ? rrule_time.zone :
973 icaltimezone_get_utc_timezone());
974 recurspan.end = recurspan.start + dtduration;
975
976 /** save the iterator ICK! **/
977 property_iterator = comp->property_iterator;
978
979 if (!icalproperty_recurrence_is_excluded(comp,
980 &dtstart, &rrule_time)) {
981 /** call callback action **/
982 if (icaltime_span_overlaps(&recurspan, &limit_span))
983 (*callback) (comp, &recurspan, callback_data);
984 }
985 comp->property_iterator = property_iterator;
986 } /* end of iteration over a specific RRULE */
987
988 icalrecur_iterator_free(rrule_itr);
989 } /* end of RRULE loop */
990
991 /** Now process RDATE entries **/
992 for (rdate = icalcomponent_get_first_property(comp, ICAL_RDATE_PROPERTY);
993 rdate != NULL;
994 rdate = icalcomponent_get_next_property(comp, ICAL_RDATE_PROPERTY)) {
995
996 struct icaldatetimeperiodtype rdate_period =
997 icalproperty_get_rdate(rdate);
998
999 /** RDATES can specify raw datetimes, periods, or dates.
1000 we only support raw datetimes for now..
1001
1002 @todo Add support for other types **/
1003
1004 if (icaltime_is_null_time(rdate_period.time))
1005 continue;
1006
1007 recurspan.start =
1008 icaltime_as_timet_with_zone(rdate_period.time,
1009 rdate_period.time.zone ?
1010 rdate_period.time.zone :
1011 icaltimezone_get_utc_timezone());
1012 recurspan.end = recurspan.start + dtduration;
1013
1014 /** save the iterator ICK! **/
1015 property_iterator = comp->property_iterator;
1016
1017 if (!icalproperty_recurrence_is_excluded(comp,
1018 &dtstart, &rdate_period.time)) {
1019 /** call callback action **/
1020 if (icaltime_span_overlaps(&recurspan, &limit_span))
1021 (*callback) (comp, &recurspan, callback_data);
1022 }
1023 comp->property_iterator = property_iterator;
1024 }
1025 }
1026
icalcomponent_check_restrictions(icalcomponent * comp)1027 int icalcomponent_check_restrictions(icalcomponent *comp)
1028 {
1029 icalerror_check_arg_rz(comp != 0, "comp");
1030 return icalrestriction_check(comp);
1031 }
1032
1033 /** @brief returns the number of errors encountered parsing the data
1034 *
1035 * This function counts the number times the X-LIC-ERROR occurs
1036 * in the data structure.
1037 */
1038
icalcomponent_count_errors(icalcomponent * component)1039 int icalcomponent_count_errors(icalcomponent *component)
1040 {
1041 int errors = 0;
1042 icalproperty *p;
1043 icalcomponent *c;
1044 pvl_elem itr;
1045
1046 icalerror_check_arg_rz((component != 0), "component");
1047
1048 for (itr = pvl_head(component->properties); itr != 0; itr = pvl_next(itr)) {
1049 p = (icalproperty *) pvl_data(itr);
1050
1051 if (icalproperty_isa(p) == ICAL_XLICERROR_PROPERTY) {
1052 errors++;
1053 }
1054 }
1055
1056 for (itr = pvl_head(component->components); itr != 0; itr = pvl_next(itr)) {
1057 c = (icalcomponent *) pvl_data(itr);
1058
1059 errors += icalcomponent_count_errors(c);
1060 }
1061
1062 return errors;
1063 }
1064
icalcomponent_strip_errors(icalcomponent * component)1065 void icalcomponent_strip_errors(icalcomponent *component)
1066 {
1067 icalproperty *p;
1068 icalcomponent *c;
1069 pvl_elem itr, next_itr;
1070
1071 icalerror_check_arg_rv((component != 0), "component");
1072
1073 for (itr = pvl_head(component->properties); itr != 0; itr = next_itr) {
1074 p = (icalproperty *) pvl_data(itr);
1075 next_itr = pvl_next(itr);
1076
1077 if (icalproperty_isa(p) == ICAL_XLICERROR_PROPERTY) {
1078 icalcomponent_remove_property(component, p);
1079 icalproperty_free(p);
1080 p = NULL;
1081 }
1082 }
1083
1084 for (itr = pvl_head(component->components); itr != 0; itr = pvl_next(itr)) {
1085 c = (icalcomponent *) pvl_data(itr);
1086 icalcomponent_strip_errors(c);
1087 }
1088 }
1089
1090 /* Hack. This will change the state of the iterators */
icalcomponent_convert_errors(icalcomponent * component)1091 void icalcomponent_convert_errors(icalcomponent *component)
1092 {
1093 icalproperty *p, *next_p;
1094 icalcomponent *c;
1095
1096 for (p = icalcomponent_get_first_property(component, ICAL_ANY_PROPERTY); p != 0; p = next_p) {
1097
1098 next_p = icalcomponent_get_next_property(component, ICAL_ANY_PROPERTY);
1099
1100 if (icalproperty_isa(p) == ICAL_XLICERROR_PROPERTY) {
1101 struct icalreqstattype rst;
1102 icalparameter *param =
1103 icalproperty_get_first_parameter(p, ICAL_XLICERRORTYPE_PARAMETER);
1104
1105 rst.code = ICAL_UNKNOWN_STATUS;
1106 rst.desc = 0;
1107
1108 switch (icalparameter_get_xlicerrortype(param)) {
1109
1110 case ICAL_XLICERRORTYPE_PARAMETERNAMEPARSEERROR:{
1111 rst.code = ICAL_3_2_INVPARAM_STATUS;
1112 break;
1113 }
1114 case ICAL_XLICERRORTYPE_PARAMETERVALUEPARSEERROR:{
1115 rst.code = ICAL_3_3_INVPARAMVAL_STATUS;
1116 break;
1117 }
1118 case ICAL_XLICERRORTYPE_PROPERTYPARSEERROR:{
1119 rst.code = ICAL_3_0_INVPROPNAME_STATUS;
1120 break;
1121 }
1122 case ICAL_XLICERRORTYPE_VALUEPARSEERROR:{
1123 rst.code = ICAL_3_1_INVPROPVAL_STATUS;
1124 break;
1125 }
1126 case ICAL_XLICERRORTYPE_COMPONENTPARSEERROR:{
1127 rst.code = ICAL_3_4_INVCOMP_STATUS;
1128 break;
1129 }
1130
1131 default:{
1132 break;
1133 }
1134 }
1135 if (rst.code != ICAL_UNKNOWN_STATUS) {
1136
1137 rst.debug = icalproperty_get_xlicerror(p);
1138 icalcomponent_add_property(component, icalproperty_new_requeststatus(rst));
1139
1140 icalcomponent_remove_property(component, p);
1141 icalproperty_free(p);
1142 p = NULL;
1143 }
1144 }
1145 }
1146
1147 for (c = icalcomponent_get_first_component(component, ICAL_ANY_COMPONENT);
1148 c != 0; c = icalcomponent_get_next_component(component, ICAL_ANY_COMPONENT)) {
1149
1150 icalcomponent_convert_errors(c);
1151 }
1152 }
1153
icalcomponent_get_parent(icalcomponent * component)1154 icalcomponent *icalcomponent_get_parent(icalcomponent *component)
1155 {
1156 return component->parent;
1157 }
1158
icalcomponent_set_parent(icalcomponent * component,icalcomponent * parent)1159 void icalcomponent_set_parent(icalcomponent *component, icalcomponent *parent)
1160 {
1161 component->parent = parent;
1162 }
1163
1164 static icalcompiter icalcompiter_null = { ICAL_NO_COMPONENT, 0 };
1165
1166 struct icalcomponent_kind_map
1167 {
1168 icalcomponent_kind kind;
1169 char name[20];
1170 };
1171
1172 static const struct icalcomponent_kind_map component_map[] = {
1173 {ICAL_VEVENT_COMPONENT, "VEVENT"},
1174 {ICAL_VTODO_COMPONENT, "VTODO"},
1175 {ICAL_VJOURNAL_COMPONENT, "VJOURNAL"},
1176 {ICAL_VCALENDAR_COMPONENT, "VCALENDAR"},
1177 {ICAL_VAGENDA_COMPONENT, "VAGENDA"},
1178 {ICAL_VFREEBUSY_COMPONENT, "VFREEBUSY"},
1179 {ICAL_VTIMEZONE_COMPONENT, "VTIMEZONE"},
1180 {ICAL_VALARM_COMPONENT, "VALARM"},
1181 {ICAL_XSTANDARD_COMPONENT, "STANDARD"}, /*These are part of RFC5545 */
1182 {ICAL_XDAYLIGHT_COMPONENT, "DAYLIGHT"}, /*but are not really components */
1183 {ICAL_X_COMPONENT, "X"},
1184 {ICAL_VSCHEDULE_COMPONENT, "SCHEDULE"},
1185
1186 /* CAP components */
1187 {ICAL_VCAR_COMPONENT, "VCAR"},
1188 {ICAL_VCOMMAND_COMPONENT, "VCOMMAND"},
1189 {ICAL_VQUERY_COMPONENT, "VQUERY"},
1190 {ICAL_VREPLY_COMPONENT, "VREPLY"},
1191
1192 /* libical private components */
1193 {ICAL_XLICINVALID_COMPONENT, "X-LIC-UNKNOWN"},
1194 {ICAL_XLICMIMEPART_COMPONENT, "X-LIC-MIME-PART"},
1195 {ICAL_ANY_COMPONENT, "ANY"},
1196 {ICAL_XROOT_COMPONENT, "XROOT"},
1197
1198 /* Calendar Availability components */
1199 {ICAL_VAVAILABILITY_COMPONENT, "VAVAILABILITY"},
1200 {ICAL_XAVAILABLE_COMPONENT, "AVAILABLE"},
1201
1202 /* Consensus Scheduling components */
1203 {ICAL_VPOLL_COMPONENT, "VPOLL"},
1204 {ICAL_VVOTER_COMPONENT, "VVOTER"},
1205 {ICAL_XVOTE_COMPONENT, "VOTE"},
1206
1207 /* VPATCH components */
1208 {ICAL_VPATCH_COMPONENT, "VPATCH"},
1209 {ICAL_XPATCH_COMPONENT, "PATCH"},
1210
1211 /* End of list */
1212 {ICAL_NO_COMPONENT, ""},
1213 };
1214
icalcomponent_kind_is_valid(const icalcomponent_kind kind)1215 int icalcomponent_kind_is_valid(const icalcomponent_kind kind)
1216 {
1217 int i = 0;
1218
1219 do {
1220 if (component_map[i].kind == kind) {
1221 return 1;
1222 }
1223 } while (component_map[i++].kind != ICAL_NO_COMPONENT);
1224
1225 return 0;
1226 }
1227
icalcomponent_kind_to_string(icalcomponent_kind kind)1228 const char *icalcomponent_kind_to_string(icalcomponent_kind kind)
1229 {
1230 int i;
1231
1232 for (i = 0; component_map[i].kind != ICAL_NO_COMPONENT; i++) {
1233 if (component_map[i].kind == kind) {
1234 return component_map[i].name;
1235 }
1236 }
1237
1238 return 0;
1239 }
1240
icalcomponent_string_to_kind(const char * string)1241 icalcomponent_kind icalcomponent_string_to_kind(const char *string)
1242 {
1243 int i;
1244
1245 if (string == 0) {
1246 return ICAL_NO_COMPONENT;
1247 }
1248
1249 for (i = 0; component_map[i].kind != ICAL_NO_COMPONENT; i++) {
1250 if (strncasecmp(string, component_map[i].name, strlen(component_map[i].name)) == 0) {
1251 return component_map[i].kind;
1252 }
1253 }
1254
1255 return ICAL_NO_COMPONENT;
1256 }
1257
icalcomponent_begin_component(icalcomponent * component,icalcomponent_kind kind)1258 icalcompiter icalcomponent_begin_component(icalcomponent *component, icalcomponent_kind kind)
1259 {
1260 icalcompiter itr;
1261 pvl_elem i;
1262
1263 itr.kind = kind;
1264 itr.iter = NULL;
1265
1266 icalerror_check_arg_re(component != 0, "component", icalcompiter_null);
1267
1268 for (i = pvl_head(component->components); i != 0; i = pvl_next(i)) {
1269
1270 icalcomponent *c = (icalcomponent *) pvl_data(i);
1271
1272 if (icalcomponent_isa(c) == kind || kind == ICAL_ANY_COMPONENT) {
1273
1274 itr.iter = i;
1275
1276 return itr;
1277 }
1278 }
1279
1280 return icalcompiter_null;
1281 }
1282
icalcomponent_end_component(icalcomponent * component,icalcomponent_kind kind)1283 icalcompiter icalcomponent_end_component(icalcomponent *component, icalcomponent_kind kind)
1284 {
1285 icalcompiter itr;
1286 pvl_elem i;
1287
1288 itr.kind = kind;
1289
1290 icalerror_check_arg_re(component != 0, "component", icalcompiter_null);
1291
1292 for (i = pvl_tail(component->components); i != 0; i = pvl_prior(i)) {
1293
1294 icalcomponent *c = (icalcomponent *) pvl_data(i);
1295
1296 if (icalcomponent_isa(c) == kind || kind == ICAL_ANY_COMPONENT) {
1297
1298 itr.iter = pvl_next(i);
1299
1300 return itr;
1301 }
1302 }
1303
1304 return icalcompiter_null;
1305 }
1306
icalcompiter_next(icalcompiter * i)1307 icalcomponent *icalcompiter_next(icalcompiter *i)
1308 {
1309 if (i->iter == 0) {
1310 return 0;
1311 }
1312
1313 icalerror_check_arg_rz((i != 0), "i");
1314
1315 for (i->iter = pvl_next(i->iter); i->iter != 0; i->iter = pvl_next(i->iter)) {
1316
1317 icalcomponent *c = (icalcomponent *) pvl_data(i->iter);
1318
1319 if (icalcomponent_isa(c) == i->kind || i->kind == ICAL_ANY_COMPONENT) {
1320
1321 return icalcompiter_deref(i);
1322 }
1323 }
1324
1325 return 0;
1326 }
1327
icalcompiter_prior(icalcompiter * i)1328 icalcomponent *icalcompiter_prior(icalcompiter *i)
1329 {
1330 if (i->iter == 0) {
1331 return 0;
1332 }
1333
1334 for (i->iter = pvl_prior(i->iter); i->iter != 0; i->iter = pvl_prior(i->iter)) {
1335
1336 icalcomponent *c = (icalcomponent *) pvl_data(i->iter);
1337
1338 if (icalcomponent_isa(c) == i->kind || i->kind == ICAL_ANY_COMPONENT) {
1339
1340 return icalcompiter_deref(i);
1341 }
1342 }
1343
1344 return 0;
1345 }
1346
icalcompiter_deref(icalcompiter * i)1347 icalcomponent *icalcompiter_deref(icalcompiter *i)
1348 {
1349 if (i->iter == 0) {
1350 return 0;
1351 }
1352
1353 return pvl_data(i->iter);
1354 }
1355
icalcomponent_get_inner(icalcomponent * comp)1356 icalcomponent *icalcomponent_get_inner(icalcomponent *comp)
1357 {
1358 if (icalcomponent_isa(comp) == ICAL_VCALENDAR_COMPONENT) {
1359 return icalcomponent_get_first_real_component(comp);
1360 } else {
1361 return comp;
1362 }
1363 }
1364
1365 /** @brief sets the METHOD property to the given method
1366 */
1367
icalcomponent_set_method(icalcomponent * comp,icalproperty_method method)1368 void icalcomponent_set_method(icalcomponent *comp, icalproperty_method method)
1369 {
1370 icalproperty *prop = icalcomponent_get_first_property(comp, ICAL_METHOD_PROPERTY);
1371
1372 if (prop == 0) {
1373 prop = icalproperty_new_method(method);
1374 icalcomponent_add_property(comp, prop);
1375 }
1376
1377 icalproperty_set_method(prop, method);
1378 }
1379
1380 /** @brief returns the METHOD property
1381 */
1382
icalcomponent_get_method(icalcomponent * comp)1383 icalproperty_method icalcomponent_get_method(icalcomponent *comp)
1384 {
1385 icalproperty *prop = icalcomponent_get_first_property(comp, ICAL_METHOD_PROPERTY);
1386
1387 if (prop == 0) {
1388 return ICAL_METHOD_NONE;
1389 }
1390
1391 return icalproperty_get_method(prop);
1392 }
1393
1394 #define ICALSETUPSET(p_kind) \
1395 icalcomponent *inner; \
1396 icalproperty *prop; \
1397 icalerror_check_arg_rv(comp != 0,"comp"); \
1398 inner = icalcomponent_get_inner(comp); \
1399 if (inner == 0) { \
1400 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); \
1401 return; \
1402 } \
1403 prop = icalcomponent_get_first_property(inner, p_kind);
1404
1405 /** @brief Set DTSTART property to given icaltime
1406 *
1407 * This method respects the icaltime type (DATE vs DATE-TIME) and
1408 * timezone (or lack thereof).
1409 */
icalcomponent_set_dtstart(icalcomponent * comp,struct icaltimetype v)1410 void icalcomponent_set_dtstart(icalcomponent *comp, struct icaltimetype v)
1411 {
1412 const char *tzid;
1413
1414 ICALSETUPSET(ICAL_DTSTART_PROPERTY);
1415
1416 if (prop == 0) {
1417 prop = icalproperty_new_dtstart(v);
1418 icalcomponent_add_property(inner, prop);
1419 } else {
1420 icalproperty_remove_parameter_by_kind(prop, ICAL_TZID_PARAMETER);
1421 }
1422
1423 icalproperty_set_dtstart(prop, v);
1424
1425 if ((tzid = icaltime_get_tzid(v)) != NULL && !icaltime_is_utc(v)) {
1426 icalproperty_add_parameter(prop, icalparameter_new_tzid(tzid));
1427 }
1428 }
1429
1430 /** @brief Get DTSTART property as an icaltime
1431 *
1432 * If DTSTART is a DATE-TIME with a timezone parameter and a
1433 * corresponding VTIMEZONE is present in the component, the
1434 * returned component will already be in the correct timezone;
1435 * otherwise the caller is responsible for converting it.
1436 *
1437 * FIXME this is useless until we can flag the failure
1438 */
icalcomponent_get_dtstart(icalcomponent * comp)1439 struct icaltimetype icalcomponent_get_dtstart(icalcomponent *comp)
1440 {
1441 icalcomponent *inner = icalcomponent_get_inner(comp);
1442 icalproperty *prop;
1443
1444 prop = icalcomponent_get_first_property(inner, ICAL_DTSTART_PROPERTY);
1445 if (prop == 0) {
1446 return icaltime_null_time();
1447 }
1448
1449 return icalproperty_get_datetime_with_component(prop, comp);
1450 }
1451
1452 /** @brief Get DTEND property as an icaltime
1453 *
1454 * If a DTEND property is not present but a DURATION is, we use
1455 * that to determine the proper end.
1456 *
1457 * If DTSTART is a DATE-TIME with a timezone parameter and a
1458 * corresponding VTIMEZONE is present in the component, the
1459 * returned component will already be in the correct timezone;
1460 * otherwise the caller is responsible for converting it.
1461 *
1462 * FIXME this is useless until we can flag the failure
1463 */
icalcomponent_get_dtend(icalcomponent * comp)1464 struct icaltimetype icalcomponent_get_dtend(icalcomponent *comp)
1465 {
1466 icalcomponent *inner = icalcomponent_get_inner(comp);
1467 icalproperty *end_prop = icalcomponent_get_first_property(inner, ICAL_DTEND_PROPERTY);
1468 icalproperty *dur_prop = icalcomponent_get_first_property(inner, ICAL_DURATION_PROPERTY);
1469 struct icaltimetype ret = icaltime_null_time();
1470
1471 if (end_prop != 0) {
1472 ret = icalproperty_get_datetime_with_component(end_prop, comp);
1473 } else if (dur_prop != 0) {
1474
1475 struct icaltimetype start = icalcomponent_get_dtstart(inner);
1476 struct icaldurationtype duration;
1477
1478 //extra check to prevent empty durations from crashing
1479 if (icalproperty_get_value(dur_prop)) {
1480 duration = icalproperty_get_duration(dur_prop);
1481 } else {
1482 duration = icaldurationtype_null_duration();
1483 }
1484
1485 ret = icaltime_add(start, duration);
1486 }
1487
1488 return ret;
1489 }
1490
1491 /** @brief Set DTEND property to given icaltime
1492 *
1493 * This method respects the icaltime type (DATE vs DATE-TIME) and
1494 * timezone (or lack thereof).
1495 *
1496 * This also checks that a DURATION property isn't already there,
1497 * and returns an error if it is. It's the caller's responsibility
1498 * to remove it.
1499 */
icalcomponent_set_dtend(icalcomponent * comp,struct icaltimetype v)1500 void icalcomponent_set_dtend(icalcomponent *comp, struct icaltimetype v)
1501 {
1502 const char *tzid;
1503
1504 ICALSETUPSET(ICAL_DTEND_PROPERTY);
1505
1506 if (icalcomponent_get_first_property(inner, ICAL_DURATION_PROPERTY) != NULL) {
1507 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1508 return;
1509 }
1510
1511 if (prop == 0) {
1512 prop = icalproperty_new_dtend(v);
1513 icalcomponent_add_property(inner, prop);
1514 } else {
1515 icalproperty_remove_parameter_by_kind(prop, ICAL_TZID_PARAMETER);
1516 }
1517
1518 icalproperty_set_dtend(prop, v);
1519
1520 if ((tzid = icaltime_get_tzid(v)) != NULL && !icaltime_is_utc(v)) {
1521 icalproperty_add_parameter(prop, icalparameter_new_tzid(tzid));
1522 }
1523 }
1524
1525 /** @brief Set DURATION property to given icalduration
1526 *
1527 * This method respects the icaltime type (DATE vs DATE-TIME) and
1528 * timezone (or lack thereof).
1529 *
1530 * This also checks that a DTEND property isn't already there,
1531 * and returns an error if it is. It's the caller's responsibility
1532 * to remove it.
1533 */
icalcomponent_set_duration(icalcomponent * comp,struct icaldurationtype v)1534 void icalcomponent_set_duration(icalcomponent *comp, struct icaldurationtype v)
1535 {
1536 ICALSETUPSET(ICAL_DURATION_PROPERTY);
1537
1538 if (icalcomponent_get_first_property(inner, ICAL_DTEND_PROPERTY) != NULL) {
1539 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1540 return;
1541 }
1542
1543 if (prop == 0) {
1544 prop = icalproperty_new_duration(v);
1545 icalcomponent_add_property(inner, prop);
1546 } else {
1547 icalproperty_set_duration(prop, v);
1548 }
1549 }
1550
1551 /** @brief Get DURATION property as an icalduration
1552 *
1553 * If a DURATION property is not present but a DTEND is, we use
1554 * that to determine the proper end.
1555 */
icalcomponent_get_duration(icalcomponent * comp)1556 struct icaldurationtype icalcomponent_get_duration(icalcomponent *comp)
1557 {
1558 icalcomponent *inner = icalcomponent_get_inner(comp);
1559
1560 icalproperty *end_prop = icalcomponent_get_first_property(inner, ICAL_DTEND_PROPERTY);
1561
1562 icalproperty *dur_prop = icalcomponent_get_first_property(inner, ICAL_DURATION_PROPERTY);
1563
1564 struct icaldurationtype ret = icaldurationtype_null_duration();
1565
1566 if (dur_prop != 0 && end_prop == 0) {
1567 ret = icalproperty_get_duration(dur_prop);
1568
1569 } else if (end_prop != 0 && dur_prop == 0) {
1570 /**
1571 * FIXME
1572 * We assume DTSTART and DTEND are not in different time zones.
1573 * Does the standard actually guarantee this?
1574 */
1575 struct icaltimetype start = icalcomponent_get_dtstart(inner);
1576 struct icaltimetype end = icalcomponent_get_dtend(inner);
1577
1578 ret = icaltime_subtract(end, start);
1579 } else {
1580 /* Error, both duration and dtend have been specified */
1581 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1582 }
1583 return ret;
1584 }
1585
icalcomponent_set_dtstamp(icalcomponent * comp,struct icaltimetype v)1586 void icalcomponent_set_dtstamp(icalcomponent *comp, struct icaltimetype v)
1587 {
1588 ICALSETUPSET(ICAL_DTSTAMP_PROPERTY);
1589
1590 if (prop == 0) {
1591 prop = icalproperty_new_dtstamp(v);
1592 icalcomponent_add_property(inner, prop);
1593 }
1594
1595 icalproperty_set_dtstamp(prop, v);
1596 }
1597
icalcomponent_get_dtstamp(icalcomponent * comp)1598 struct icaltimetype icalcomponent_get_dtstamp(icalcomponent *comp)
1599 {
1600 icalcomponent *inner = icalcomponent_get_inner(comp);
1601 icalproperty *prop = icalcomponent_get_first_property(inner, ICAL_DTSTAMP_PROPERTY);
1602
1603 if (prop == 0) {
1604 return icaltime_null_time();
1605 }
1606
1607 return icalproperty_get_dtstamp(prop);
1608 }
1609
icalcomponent_set_summary(icalcomponent * comp,const char * v)1610 void icalcomponent_set_summary(icalcomponent *comp, const char *v)
1611 {
1612 ICALSETUPSET(ICAL_SUMMARY_PROPERTY)
1613
1614 if (prop == 0) {
1615 prop = icalproperty_new_summary(v);
1616 icalcomponent_add_property(inner, prop);
1617 }
1618
1619 icalproperty_set_summary(prop, v);
1620 }
1621
icalcomponent_get_summary(icalcomponent * comp)1622 const char *icalcomponent_get_summary(icalcomponent *comp)
1623 {
1624 icalcomponent *inner;
1625 icalproperty *prop;
1626
1627 icalerror_check_arg_rz(comp != 0, "comp");
1628
1629 inner = icalcomponent_get_inner(comp);
1630
1631 if (inner == 0) {
1632 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1633 return 0;
1634 }
1635
1636 prop = icalcomponent_get_first_property(inner, ICAL_SUMMARY_PROPERTY);
1637
1638 if (prop == 0) {
1639 return 0;
1640 }
1641
1642 return icalproperty_get_summary(prop);
1643 }
1644
icalcomponent_set_comment(icalcomponent * comp,const char * v)1645 void icalcomponent_set_comment(icalcomponent *comp, const char *v)
1646 {
1647 ICALSETUPSET(ICAL_COMMENT_PROPERTY);
1648
1649 if (prop == 0) {
1650 prop = icalproperty_new_comment(v);
1651 icalcomponent_add_property(inner, prop);
1652 }
1653
1654 icalproperty_set_comment(prop, v);
1655 }
1656
icalcomponent_get_comment(icalcomponent * comp)1657 const char *icalcomponent_get_comment(icalcomponent *comp)
1658 {
1659 icalcomponent *inner;
1660 icalproperty *prop;
1661
1662 icalerror_check_arg_rz(comp != 0, "comp");
1663
1664 inner = icalcomponent_get_inner(comp);
1665
1666 if (inner == 0) {
1667 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1668 return 0;
1669 }
1670
1671 prop = icalcomponent_get_first_property(inner, ICAL_COMMENT_PROPERTY);
1672
1673 if (prop == 0) {
1674 return 0;
1675 }
1676
1677 return icalproperty_get_comment(prop);
1678 }
1679
icalcomponent_set_uid(icalcomponent * comp,const char * v)1680 void icalcomponent_set_uid(icalcomponent *comp, const char *v)
1681 {
1682 ICALSETUPSET(ICAL_UID_PROPERTY);
1683
1684 if (prop == 0) {
1685 prop = icalproperty_new_uid(v);
1686 icalcomponent_add_property(inner, prop);
1687 }
1688
1689 icalproperty_set_uid(prop, v);
1690 }
1691
icalcomponent_get_uid(icalcomponent * comp)1692 const char *icalcomponent_get_uid(icalcomponent *comp)
1693 {
1694 icalcomponent *inner;
1695 icalproperty *prop;
1696
1697 icalerror_check_arg_rz(comp != 0, "comp");
1698
1699 inner = icalcomponent_get_inner(comp);
1700
1701 if (inner == 0) {
1702 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1703 return 0;
1704 }
1705
1706 prop = icalcomponent_get_first_property(inner, ICAL_UID_PROPERTY);
1707
1708 if (prop == 0) {
1709 return 0;
1710 }
1711
1712 return icalproperty_get_uid(prop);
1713 }
1714
icalcomponent_set_recurrenceid(icalcomponent * comp,struct icaltimetype v)1715 void icalcomponent_set_recurrenceid(icalcomponent *comp, struct icaltimetype v)
1716 {
1717 ICALSETUPSET(ICAL_RECURRENCEID_PROPERTY);
1718
1719 if (prop == 0) {
1720 prop = icalproperty_new_recurrenceid(v);
1721 icalcomponent_add_property(inner, prop);
1722 }
1723
1724 icalproperty_set_recurrenceid(prop, v);
1725 }
1726
icalcomponent_get_recurrenceid(icalcomponent * comp)1727 struct icaltimetype icalcomponent_get_recurrenceid(icalcomponent *comp)
1728 {
1729 icalcomponent *inner;
1730 icalproperty *prop;
1731
1732 if (comp == 0) {
1733 icalerror_set_errno(ICAL_BADARG_ERROR);
1734 return icaltime_null_time();
1735 }
1736
1737 inner = icalcomponent_get_inner(comp);
1738
1739 if (inner == 0) {
1740 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1741 return icaltime_null_time();
1742 }
1743
1744 prop = icalcomponent_get_first_property(inner, ICAL_RECURRENCEID_PROPERTY);
1745
1746 if (prop == 0) {
1747 return icaltime_null_time();
1748 }
1749
1750 return icalproperty_get_recurrenceid(prop);
1751 }
1752
icalcomponent_set_description(icalcomponent * comp,const char * v)1753 void icalcomponent_set_description(icalcomponent *comp, const char *v)
1754 {
1755 ICALSETUPSET(ICAL_DESCRIPTION_PROPERTY);
1756
1757 if (prop == 0) {
1758 prop = icalproperty_new_description(v);
1759 icalcomponent_add_property(inner, prop);
1760 }
1761
1762 icalproperty_set_description(prop, v);
1763 }
1764
icalcomponent_get_description(icalcomponent * comp)1765 const char *icalcomponent_get_description(icalcomponent *comp)
1766 {
1767 icalcomponent *inner;
1768 icalproperty *prop;
1769
1770 icalerror_check_arg_rz(comp != 0, "comp");
1771
1772 inner = icalcomponent_get_inner(comp);
1773
1774 if (inner == 0) {
1775 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1776 return 0;
1777 }
1778
1779 prop = icalcomponent_get_first_property(inner, ICAL_DESCRIPTION_PROPERTY);
1780
1781 if (prop == 0) {
1782 return 0;
1783 }
1784
1785 return icalproperty_get_description(prop);
1786 }
1787
icalcomponent_set_location(icalcomponent * comp,const char * v)1788 void icalcomponent_set_location(icalcomponent *comp, const char *v)
1789 {
1790 ICALSETUPSET(ICAL_LOCATION_PROPERTY)
1791
1792 if (prop == 0) {
1793 prop = icalproperty_new_location(v);
1794 icalcomponent_add_property(inner, prop);
1795 }
1796
1797 icalproperty_set_location(prop, v);
1798 }
1799
icalcomponent_get_location(icalcomponent * comp)1800 const char *icalcomponent_get_location(icalcomponent *comp)
1801 {
1802 icalcomponent *inner;
1803 icalproperty *prop;
1804
1805 icalerror_check_arg_rz(comp != 0, "comp");
1806
1807 inner = icalcomponent_get_inner(comp);
1808
1809 if (inner == 0) {
1810 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1811 return 0;
1812 }
1813
1814 prop = icalcomponent_get_first_property(inner, ICAL_LOCATION_PROPERTY);
1815
1816 if (prop == 0) {
1817 return 0;
1818 }
1819
1820 return icalproperty_get_location(prop);
1821 }
1822
icalcomponent_set_sequence(icalcomponent * comp,int v)1823 void icalcomponent_set_sequence(icalcomponent *comp, int v)
1824 {
1825 ICALSETUPSET(ICAL_SEQUENCE_PROPERTY);
1826
1827 if (prop == 0) {
1828 prop = icalproperty_new_sequence(v);
1829 icalcomponent_add_property(inner, prop);
1830 }
1831
1832 icalproperty_set_sequence(prop, v);
1833 }
1834
icalcomponent_get_sequence(icalcomponent * comp)1835 int icalcomponent_get_sequence(icalcomponent *comp)
1836 {
1837 icalcomponent *inner;
1838 icalproperty *prop;
1839
1840 icalerror_check_arg_rz(comp != 0, "comp");
1841
1842 inner = icalcomponent_get_inner(comp);
1843
1844 if (inner == 0) {
1845 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1846 return 0;
1847 }
1848
1849 prop = icalcomponent_get_first_property(inner, ICAL_SEQUENCE_PROPERTY);
1850
1851 if (prop == 0) {
1852 return 0;
1853 }
1854
1855 return icalproperty_get_sequence(prop);
1856 }
1857
icalcomponent_set_status(icalcomponent * comp,enum icalproperty_status v)1858 void icalcomponent_set_status(icalcomponent *comp, enum icalproperty_status v)
1859 {
1860 ICALSETUPSET(ICAL_STATUS_PROPERTY);
1861
1862 if (prop == 0) {
1863 prop = icalproperty_new_status(v);
1864 icalcomponent_add_property(inner, prop);
1865 }
1866
1867 icalproperty_set_status(prop, v);
1868 }
1869
icalcomponent_get_status(icalcomponent * comp)1870 enum icalproperty_status icalcomponent_get_status(icalcomponent *comp)
1871 {
1872 icalcomponent *inner;
1873 icalproperty *prop;
1874
1875 icalerror_check_arg_rz(comp != 0, "comp");
1876
1877 inner = icalcomponent_get_inner(comp);
1878
1879 if (inner == 0) {
1880 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1881 return 0;
1882 }
1883
1884 prop = icalcomponent_get_first_property(inner, ICAL_STATUS_PROPERTY);
1885
1886 if (prop == 0) {
1887 return 0;
1888 }
1889
1890 return icalproperty_get_status(prop);
1891 }
1892
icalcomponent_new_vcalendar(void)1893 icalcomponent *icalcomponent_new_vcalendar(void)
1894 {
1895 return icalcomponent_new(ICAL_VCALENDAR_COMPONENT);
1896 }
1897
icalcomponent_new_vevent(void)1898 icalcomponent *icalcomponent_new_vevent(void)
1899 {
1900 return icalcomponent_new(ICAL_VEVENT_COMPONENT);
1901 }
1902
icalcomponent_new_vtodo(void)1903 icalcomponent *icalcomponent_new_vtodo(void)
1904 {
1905 return icalcomponent_new(ICAL_VTODO_COMPONENT);
1906 }
1907
icalcomponent_new_vjournal(void)1908 icalcomponent *icalcomponent_new_vjournal(void)
1909 {
1910 return icalcomponent_new(ICAL_VJOURNAL_COMPONENT);
1911 }
1912
icalcomponent_new_valarm(void)1913 icalcomponent *icalcomponent_new_valarm(void)
1914 {
1915 return icalcomponent_new(ICAL_VALARM_COMPONENT);
1916 }
1917
icalcomponent_new_vfreebusy(void)1918 icalcomponent *icalcomponent_new_vfreebusy(void)
1919 {
1920 return icalcomponent_new(ICAL_VFREEBUSY_COMPONENT);
1921 }
1922
icalcomponent_new_vtimezone(void)1923 icalcomponent *icalcomponent_new_vtimezone(void)
1924 {
1925 return icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
1926 }
1927
icalcomponent_new_xstandard(void)1928 icalcomponent *icalcomponent_new_xstandard(void)
1929 {
1930 return icalcomponent_new(ICAL_XSTANDARD_COMPONENT);
1931 }
1932
icalcomponent_new_xdaylight(void)1933 icalcomponent *icalcomponent_new_xdaylight(void)
1934 {
1935 return icalcomponent_new(ICAL_XDAYLIGHT_COMPONENT);
1936 }
1937
icalcomponent_new_vagenda(void)1938 icalcomponent *icalcomponent_new_vagenda(void)
1939 {
1940 return icalcomponent_new(ICAL_VAGENDA_COMPONENT);
1941 }
1942
icalcomponent_new_vquery(void)1943 icalcomponent *icalcomponent_new_vquery(void)
1944 {
1945 return icalcomponent_new(ICAL_VQUERY_COMPONENT);
1946 }
1947
icalcomponent_new_vreply(void)1948 icalcomponent *icalcomponent_new_vreply(void)
1949 {
1950 return icalcomponent_new(ICAL_VREPLY_COMPONENT);
1951 }
1952
icalcomponent_new_vavailability(void)1953 icalcomponent *icalcomponent_new_vavailability(void)
1954 {
1955 return icalcomponent_new(ICAL_VAVAILABILITY_COMPONENT);
1956 }
1957
icalcomponent_new_xavailable(void)1958 icalcomponent *icalcomponent_new_xavailable(void)
1959 {
1960 return icalcomponent_new(ICAL_XAVAILABLE_COMPONENT);
1961 }
1962
icalcomponent_new_vpoll(void)1963 icalcomponent *icalcomponent_new_vpoll(void)
1964 {
1965 return icalcomponent_new(ICAL_VPOLL_COMPONENT);
1966 }
1967
icalcomponent_new_vvoter(void)1968 icalcomponent *icalcomponent_new_vvoter(void)
1969 {
1970 return icalcomponent_new(ICAL_VVOTER_COMPONENT);
1971 }
1972
icalcomponent_new_xvote(void)1973 icalcomponent *icalcomponent_new_xvote(void)
1974 {
1975 return icalcomponent_new(ICAL_XVOTE_COMPONENT);
1976 }
1977
icalcomponent_new_vpatch(void)1978 icalcomponent *icalcomponent_new_vpatch(void)
1979 {
1980 return icalcomponent_new(ICAL_VPATCH_COMPONENT);
1981 }
1982
icalcomponent_new_xpatch(void)1983 icalcomponent *icalcomponent_new_xpatch(void)
1984 {
1985 return icalcomponent_new(ICAL_XPATCH_COMPONENT);
1986 }
1987
1988 /*
1989 * Timezone stuff.
1990 */
1991
1992 /**
1993 * This takes 2 VCALENDAR components and merges the second one into the first,
1994 * resolving any problems with conflicting TZIDs. comp_to_merge will no
1995 * longer exist after calling this function.
1996 */
icalcomponent_merge_component(icalcomponent * comp,icalcomponent * comp_to_merge)1997 void icalcomponent_merge_component(icalcomponent *comp, icalcomponent *comp_to_merge)
1998 {
1999 icalcomponent *subcomp, *next_subcomp;
2000 icalarray *tzids_to_rename;
2001 size_t i;
2002
2003 /* Check that both components are VCALENDAR components. */
2004 assert(icalcomponent_isa(comp) == ICAL_VCALENDAR_COMPONENT);
2005 assert(icalcomponent_isa(comp_to_merge) == ICAL_VCALENDAR_COMPONENT);
2006
2007 /* Step through each subcomponent of comp_to_merge, looking for VTIMEZONEs.
2008 For each VTIMEZONE found, check if we need to add it to comp and if we
2009 need to rename it and all TZID references to it. */
2010 tzids_to_rename = icalarray_new(sizeof(char *), 16);
2011 subcomp = icalcomponent_get_first_component(comp_to_merge, ICAL_VTIMEZONE_COMPONENT);
2012 while (subcomp) {
2013 next_subcomp = icalcomponent_get_next_component(comp_to_merge, ICAL_VTIMEZONE_COMPONENT);
2014 /* This will add the VTIMEZONE to comp, if necessary, and also update
2015 the array of TZIDs we need to rename. */
2016 icalcomponent_merge_vtimezone(comp, subcomp, tzids_to_rename);
2017 /* FIXME: Handle possible NEWFAILED error. */
2018
2019 subcomp = next_subcomp;
2020 }
2021
2022 /* If we need to do any renaming of TZIDs, do it now. */
2023 if (tzids_to_rename->num_elements != 0) {
2024 icalcomponent_rename_tzids(comp_to_merge, tzids_to_rename);
2025
2026 /* Now free the tzids_to_rename array. */
2027 for (i = 0; i < tzids_to_rename->num_elements; i++) {
2028 free(icalarray_element_at(tzids_to_rename, i));
2029 }
2030 }
2031 icalarray_free(tzids_to_rename);
2032 tzids_to_rename = 0;
2033 /* Now move all the components from comp_to_merge to comp, excluding
2034 VTIMEZONE components. */
2035 subcomp = icalcomponent_get_first_component(comp_to_merge, ICAL_ANY_COMPONENT);
2036 while (subcomp) {
2037 next_subcomp = icalcomponent_get_next_component(comp_to_merge, ICAL_ANY_COMPONENT);
2038 if (icalcomponent_isa(subcomp) != ICAL_VTIMEZONE_COMPONENT) {
2039 icalcomponent_remove_component(comp_to_merge, subcomp);
2040 icalcomponent_add_component(comp, subcomp);
2041 }
2042 subcomp = next_subcomp;
2043 }
2044
2045 /* Free comp_to_merge. We have moved most of the subcomponents over to
2046 comp now. */
2047 icalcomponent_free(comp_to_merge);
2048 }
2049
icalcomponent_merge_vtimezone(icalcomponent * comp,icalcomponent * vtimezone,icalarray * tzids_to_rename)2050 static void icalcomponent_merge_vtimezone(icalcomponent *comp,
2051 icalcomponent *vtimezone, icalarray *tzids_to_rename)
2052 {
2053 icalproperty *tzid_prop;
2054 const char *tzid;
2055 char *tzid_copy;
2056 icaltimezone *existing_vtimezone;
2057
2058 /* Get the TZID of the VTIMEZONE. */
2059 tzid_prop = icalcomponent_get_first_property(vtimezone, ICAL_TZID_PROPERTY);
2060 if (!tzid_prop)
2061 return;
2062
2063 tzid = icalproperty_get_tzid(tzid_prop);
2064 if (!tzid)
2065 return;
2066
2067 /* See if there is already a VTIMEZONE in comp with the same TZID. */
2068 existing_vtimezone = icalcomponent_get_timezone(comp, tzid);
2069
2070 /* If there is no existing VTIMEZONE with the same TZID, we can just move
2071 the VTIMEZONE to comp and return. */
2072 if (!existing_vtimezone) {
2073 icalcomponent_remove_component(icalcomponent_get_parent(vtimezone), vtimezone);
2074 icalcomponent_add_component(comp, vtimezone);
2075 return;
2076 }
2077
2078 /* If the TZID has a '/' prefix, then we don't have to worry about the
2079 clashing TZIDs, as they are supposed to be exactly the same VTIMEZONE. */
2080 if (tzid[0] == '/')
2081 return;
2082
2083 /* Now we have two VTIMEZONEs with the same TZID (which isn't a globally
2084 unique one), so we compare the VTIMEZONE components to see if they are
2085 the same. If they are, we don't need to do anything. We make a copy of
2086 the tzid, since the parameter may get modified in these calls. */
2087 tzid_copy = strdup(tzid);
2088 if (!tzid_copy) {
2089 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
2090 return;
2091 }
2092
2093 if (!icalcomponent_compare_vtimezones(comp, vtimezone)) {
2094 /* FIXME: Handle possible NEWFAILED error. */
2095
2096 /* Now we have two different VTIMEZONEs with the same TZID. */
2097 icalcomponent_handle_conflicting_vtimezones(comp, vtimezone, tzid_prop,
2098 tzid_copy, tzids_to_rename);
2099 }
2100 free(tzid_copy);
2101 }
2102
icalcomponent_handle_conflicting_vtimezones(icalcomponent * comp,icalcomponent * vtimezone,icalproperty * tzid_prop,const char * tzid,icalarray * tzids_to_rename)2103 static void icalcomponent_handle_conflicting_vtimezones(icalcomponent *comp,
2104 icalcomponent *vtimezone,
2105 icalproperty *tzid_prop,
2106 const char *tzid,
2107 icalarray *tzids_to_rename)
2108 {
2109 int suffix, max_suffix = 0;
2110 size_t i, num_elements, tzid_len;
2111 char *tzid_copy, *new_tzid, suffix_buf[32];
2112
2113 _unused(tzid_prop);
2114
2115 /* Find the length of the TZID without any trailing digits. */
2116 tzid_len = icalcomponent_get_tzid_prefix_len(tzid);
2117
2118 /* Step through each of the VTIMEZONEs in comp. We may already have the
2119 clashing VTIMEZONE in the calendar, but it may have been renamed
2120 (i.e. a unique number added on the end of the TZID, e.g. 'London2').
2121 So we compare the new VTIMEZONE with any VTIMEZONEs that have the
2122 same prefix (e.g. 'London'). If it matches any of those, we have to
2123 rename the TZIDs to that TZID, else we rename to a new TZID, using
2124 the biggest numeric suffix found + 1. */
2125 num_elements = comp->timezones ? comp->timezones->num_elements : 0;
2126 for (i = 0; i < num_elements; i++) {
2127 icaltimezone *zone;
2128 const char *existing_tzid;
2129 char *existing_tzid_copy;
2130 size_t existing_tzid_len;
2131
2132 zone = icalarray_element_at(comp->timezones, i);
2133 existing_tzid = icaltimezone_get_tzid(zone);
2134
2135 /* Find the length of the TZID without any trailing digits. */
2136 existing_tzid_len = icalcomponent_get_tzid_prefix_len(existing_tzid);
2137
2138 /* Check if we have the same prefix. */
2139 if (tzid_len == existing_tzid_len && !strncmp(tzid, existing_tzid, tzid_len)) {
2140 /* Compare the VTIMEZONEs. */
2141 if (icalcomponent_compare_vtimezones(icaltimezone_get_component(zone), vtimezone)) {
2142 /* The VTIMEZONEs match, so we can use the existing VTIMEZONE. But
2143 we have to rename TZIDs to this TZID. */
2144 tzid_copy = strdup(tzid);
2145 if (!tzid_copy) {
2146 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
2147 return;
2148 }
2149 existing_tzid_copy = strdup(existing_tzid);
2150 if (!existing_tzid_copy) {
2151 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
2152 free(tzid_copy);
2153 } else {
2154 icalarray_append(tzids_to_rename, tzid_copy);
2155 free(tzid_copy);
2156 icalarray_append(tzids_to_rename, existing_tzid_copy);
2157 free(existing_tzid_copy);
2158 }
2159 return;
2160 } else {
2161 /* FIXME: Handle possible NEWFAILED error. */
2162
2163 /* Convert the suffix to an integer and remember the maximum numeric
2164 suffix found. */
2165 suffix = atoi(existing_tzid + existing_tzid_len);
2166 if (max_suffix < suffix)
2167 max_suffix = suffix;
2168 }
2169 }
2170 }
2171
2172 /* We didn't find a VTIMEZONE that matched, so we have to rename the TZID,
2173 using the maximum numerical suffix found + 1. */
2174 tzid_copy = strdup(tzid);
2175 if (!tzid_copy) {
2176 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
2177 return;
2178 }
2179
2180 snprintf(suffix_buf, sizeof(suffix_buf), "%i", max_suffix + 1);
2181 new_tzid = malloc(tzid_len + strlen(suffix_buf) + 1);
2182 if (!new_tzid) {
2183 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
2184 free(tzid_copy);
2185 return;
2186 }
2187
2188 strncpy(new_tzid, tzid, tzid_len);
2189 strcpy(new_tzid + tzid_len, suffix_buf);
2190 icalarray_append(tzids_to_rename, tzid_copy);
2191 icalarray_append(tzids_to_rename, new_tzid);
2192 free(tzid_copy);
2193 free(new_tzid);
2194 }
2195
2196 /* Returns the length of the TZID, without any trailing digits. */
icalcomponent_get_tzid_prefix_len(const char * tzid)2197 static size_t icalcomponent_get_tzid_prefix_len(const char *tzid)
2198 {
2199 size_t len;
2200 const char *p;
2201
2202 len = strlen(tzid);
2203 p = tzid + len - 1;
2204 while (len > 0 && *p >= '0' && *p <= '9') {
2205 p--;
2206 len--;
2207 }
2208
2209 return len;
2210 }
2211
2212 /**
2213 * Renames all references to the given TZIDs to a new name. rename_table
2214 * contains pairs of strings - a current TZID, and the new TZID to rename it
2215 * to.
2216 */
icalcomponent_rename_tzids(icalcomponent * comp,icalarray * rename_table)2217 static void icalcomponent_rename_tzids(icalcomponent *comp, icalarray *rename_table)
2218 {
2219 icalcomponent_foreach_tzid(comp, icalcomponent_rename_tzids_callback, rename_table);
2220 }
2221
icalcomponent_rename_tzids_callback(icalparameter * param,void * data)2222 static void icalcomponent_rename_tzids_callback(icalparameter *param, void *data)
2223 {
2224 icalarray *rename_table = data;
2225 const char *tzid;
2226 size_t i;
2227
2228 tzid = icalparameter_get_tzid(param);
2229 if (!tzid)
2230 return;
2231
2232 /* Step through the rename table to see if the current TZID matches
2233 any of the ones we want to rename. */
2234 for (i = 0; i < rename_table->num_elements - 1; i += 2) {
2235 if (!strcmp(tzid, icalarray_element_at(rename_table, i))) {
2236 icalparameter_set_tzid(param, icalarray_element_at(rename_table, i + 1));
2237 break;
2238 }
2239 }
2240 }
2241
2242 /**
2243 * Calls the given function for each TZID parameter found in the component.
2244 */
icalcomponent_foreach_tzid(icalcomponent * comp,void (* callback)(icalparameter * param,void * data),void * callback_data)2245 void icalcomponent_foreach_tzid(icalcomponent *comp,
2246 void (*callback) (icalparameter *param, void *data),
2247 void *callback_data)
2248 {
2249 icalproperty *prop;
2250 icalproperty_kind kind;
2251 icalparameter *param;
2252 icalcomponent *subcomp;
2253
2254 /* First look for any TZID parameters used in this component itself. */
2255 prop = icalcomponent_get_first_property(comp, ICAL_ANY_PROPERTY);
2256 while (prop) {
2257 kind = icalproperty_isa(prop);
2258
2259 /* These are the only properties that can have a TZID. Note that
2260 COMPLETED, CREATED, DTSTAMP & LASTMODIFIED must be in UTC. */
2261 if (kind == ICAL_DTSTART_PROPERTY ||
2262 kind == ICAL_DTEND_PROPERTY ||
2263 kind == ICAL_DUE_PROPERTY ||
2264 kind == ICAL_EXDATE_PROPERTY ||
2265 kind == ICAL_RDATE_PROPERTY) {
2266 param = icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER);
2267 if (param)
2268 (*callback) (param, callback_data);
2269 }
2270
2271 prop = icalcomponent_get_next_property(comp, ICAL_ANY_PROPERTY);
2272 }
2273
2274 /* Now recursively check child components. */
2275 subcomp = icalcomponent_get_first_component(comp, ICAL_ANY_COMPONENT);
2276 while (subcomp) {
2277 icalcomponent_foreach_tzid(subcomp, callback, callback_data);
2278 subcomp = icalcomponent_get_next_component(comp, ICAL_ANY_COMPONENT);
2279 }
2280 }
2281
2282 /**
2283 * Returns the icaltimezone from the component corresponding to the given
2284 * TZID, or NULL if the component does not have a corresponding VTIMEZONE.
2285 */
icalcomponent_get_timezone(icalcomponent * comp,const char * tzid)2286 icaltimezone *icalcomponent_get_timezone(icalcomponent *comp, const char *tzid)
2287 {
2288 icaltimezone *zone;
2289 size_t lower, middle, upper;
2290 int cmp;
2291 const char *zone_tzid;
2292
2293 if (!comp->timezones)
2294 return NULL;
2295
2296 /* Sort the array if necessary (by the TZID string). */
2297 if (!comp->timezones_sorted) {
2298 icalarray_sort(comp->timezones, icalcomponent_compare_timezone_fn);
2299 comp->timezones_sorted = 1;
2300 }
2301
2302 /* Do a simple binary search. */
2303 lower = middle = 0;
2304 upper = comp->timezones->num_elements;
2305
2306 while (lower < upper) {
2307 middle = (lower + upper) >> 1;
2308 zone = icalarray_element_at(comp->timezones, middle);
2309 zone_tzid = icaltimezone_get_tzid(zone);
2310 if (zone_tzid != NULL) {
2311 cmp = strcmp(tzid, zone_tzid);
2312 if (cmp == 0) {
2313 return zone;
2314 } else if (cmp < 0) {
2315 upper = middle;
2316 } else {
2317 lower = middle + 1;
2318 }
2319 }
2320 }
2321
2322 return NULL;
2323 }
2324
2325 /**
2326 * A function to compare 2 icaltimezone elements, used for qsort().
2327 */
icalcomponent_compare_timezone_fn(const void * elem1,const void * elem2)2328 static int icalcomponent_compare_timezone_fn(const void *elem1, const void *elem2)
2329 {
2330 icaltimezone *zone1, *zone2;
2331 const char *zone1_tzid, *zone2_tzid;
2332
2333 zone1 = (icaltimezone *) elem1;
2334 zone2 = (icaltimezone *) elem2;
2335
2336 zone1_tzid = icaltimezone_get_tzid(zone1);
2337 zone2_tzid = icaltimezone_get_tzid(zone2);
2338
2339 return strcmp(zone1_tzid, zone2_tzid);
2340 }
2341
2342 /**
2343 * Compares 2 VTIMEZONE components to see if they match, ignoring their TZIDs.
2344 * It returns 1 if they match, 0 if they don't, or -1 on error.
2345 */
icalcomponent_compare_vtimezones(icalcomponent * vtimezone1,icalcomponent * vtimezone2)2346 static int icalcomponent_compare_vtimezones(icalcomponent *vtimezone1, icalcomponent *vtimezone2)
2347 {
2348 icalproperty *prop1, *prop2;
2349 const char *tzid1, *tzid2;
2350 char *tzid2_copy, *string1, *string2;
2351 int cmp;
2352
2353 /* Get the TZID property of the first VTIMEZONE. */
2354 prop1 = icalcomponent_get_first_property(vtimezone1, ICAL_TZID_PROPERTY);
2355 if (!prop1)
2356 return -1;
2357
2358 tzid1 = icalproperty_get_tzid(prop1);
2359 if (!tzid1)
2360 return -1;
2361
2362 /* Get the TZID property of the second VTIMEZONE. */
2363 prop2 = icalcomponent_get_first_property(vtimezone2, ICAL_TZID_PROPERTY);
2364 if (!prop2)
2365 return -1;
2366
2367 tzid2 = icalproperty_get_tzid(prop2);
2368 if (!tzid2)
2369 return -1;
2370
2371 /* Copy the second TZID, and set the property to the same as the first
2372 TZID, since we don't care if these match of not. */
2373 tzid2_copy = strdup(tzid2);
2374 if (!tzid2_copy) {
2375 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
2376 return 0;
2377 }
2378
2379 icalproperty_set_tzid(prop2, tzid1);
2380
2381 /* Now convert both VTIMEZONEs to strings and compare them. */
2382 string1 = icalcomponent_as_ical_string_r(vtimezone1);
2383 if (!string1) {
2384 free(tzid2_copy);
2385 return -1;
2386 }
2387
2388 string2 = icalcomponent_as_ical_string_r(vtimezone2);
2389 if (!string2) {
2390 free(string1);
2391 free(tzid2_copy);
2392 return -1;
2393 }
2394
2395 cmp = strcmp(string1, string2);
2396
2397 free(string1);
2398 free(string2);
2399
2400 /* Now reset the second TZID. */
2401 icalproperty_set_tzid(prop2, tzid2_copy);
2402 free(tzid2_copy);
2403
2404 return (cmp == 0) ? 1 : 0;
2405 }
2406
2407 /**
2408 * @brief set the RELCALID property of a component.
2409 *
2410 * @param comp Valid calendar component.
2411 * @param v Relcalid URL value
2412 */
2413
icalcomponent_set_relcalid(icalcomponent * comp,const char * v)2414 void icalcomponent_set_relcalid(icalcomponent *comp, const char *v)
2415 {
2416 ICALSETUPSET(ICAL_RELCALID_PROPERTY);
2417
2418 if (prop == 0) {
2419 prop = icalproperty_new_relcalid(v);
2420 icalcomponent_add_property(inner, prop);
2421 }
2422
2423 icalproperty_set_relcalid(prop, v);
2424 }
2425
2426 /**
2427 * @brief get the RELCALID property of a component.
2428 *
2429 * @param comp Valid calendar component.
2430 */
2431
icalcomponent_get_relcalid(icalcomponent * comp)2432 const char *icalcomponent_get_relcalid(icalcomponent *comp)
2433 {
2434 icalcomponent *inner;
2435 icalproperty *prop;
2436
2437 icalerror_check_arg_rz(comp != 0, "comp");
2438
2439 inner = icalcomponent_get_inner(comp);
2440
2441 if (inner == 0) {
2442 return 0;
2443 }
2444
2445 prop = icalcomponent_get_first_property(inner, ICAL_RELCALID_PROPERTY);
2446
2447 if (prop == 0) {
2448 return 0;
2449 }
2450
2451 return icalproperty_get_relcalid(prop);
2452 }
2453
2454 /** @brief Return the time a TODO task is DUE.
2455 *
2456 * @param comp Valid calendar component.
2457 *
2458 * Uses the DUE: property if it exists, otherwise we calculate the DUE
2459 * value by adding the task's duration to the DTSTART time
2460 */
2461
icalcomponent_get_due(icalcomponent * comp)2462 struct icaltimetype icalcomponent_get_due(icalcomponent *comp)
2463 {
2464 icalcomponent *inner = icalcomponent_get_inner(comp);
2465
2466 icalproperty *due_prop = icalcomponent_get_first_property(inner, ICAL_DUE_PROPERTY);
2467
2468 icalproperty *dur_prop = icalcomponent_get_first_property(inner, ICAL_DURATION_PROPERTY);
2469
2470 if (due_prop != 0) {
2471 return icalproperty_get_datetime_with_component(due_prop, comp);
2472 } else if (dur_prop != 0) {
2473
2474 struct icaltimetype start = icalcomponent_get_dtstart(inner);
2475 struct icaldurationtype duration = icalproperty_get_duration(dur_prop);
2476
2477 struct icaltimetype due = icaltime_add(start, duration);
2478
2479 return due;
2480 }
2481 return icaltime_null_time();
2482 }
2483
2484 /** @brief Set the due date of a VTODO task.
2485 *
2486 * @param comp Valid VTODO component.
2487 * @param v Valid due date time.
2488 *
2489 * - If no duration or due properties then set the DUE property.
2490 * - If a DUE property is already set, then reset it to the value v.
2491 * - If a DURATION property is already set, then calculate the new
2492 * duration based on the supplied value of v.
2493 */
2494
icalcomponent_set_due(icalcomponent * comp,struct icaltimetype v)2495 void icalcomponent_set_due(icalcomponent *comp, struct icaltimetype v)
2496 {
2497 const char *tzid;
2498
2499 icalcomponent *inner = icalcomponent_get_inner(comp);
2500
2501 icalproperty *due_prop = icalcomponent_get_first_property(inner, ICAL_DUE_PROPERTY);
2502
2503 icalproperty *dur_prop = icalcomponent_get_first_property(inner, ICAL_DURATION_PROPERTY);
2504
2505 if (due_prop == 0 && dur_prop == 0) {
2506 due_prop = icalproperty_new_due(v);
2507 icalcomponent_add_property(inner, due_prop);
2508 } else if (due_prop != 0) {
2509 icalproperty_set_due(due_prop, v);
2510 } else if (dur_prop != 0) {
2511 struct icaltimetype start = icalcomponent_get_dtstart(inner);
2512
2513 struct icaltimetype due = icalcomponent_get_due(inner);
2514
2515 struct icaldurationtype dur = icaltime_subtract(due, start);
2516
2517 icalproperty_set_duration(dur_prop, dur);
2518 }
2519
2520 if (due_prop && (tzid = icaltime_get_tzid(v)) != NULL && !icaltime_is_utc(v)) {
2521 icalproperty_set_parameter(due_prop, icalparameter_new_tzid(tzid));
2522 }
2523 }
2524
prop_compare(void * a,void * b)2525 static int prop_compare(void *a, void *b)
2526 {
2527 icalproperty *p1 = (icalproperty*) a;
2528 icalproperty *p2 = (icalproperty*) b;
2529 icalproperty_kind k1 = icalproperty_isa(p1);
2530 icalproperty_kind k2 = icalproperty_isa(p2);
2531 int r = k1 - k2;
2532
2533 if (r == 0) {
2534 if (k1 == ICAL_X_PROPERTY) {
2535 r = strcmp(icalproperty_get_x_name(p1),
2536 icalproperty_get_x_name(p2));
2537 }
2538
2539 if (r == 0) {
2540 r = strcmp(icalproperty_get_value_as_string(p1),
2541 icalproperty_get_value_as_string(p2));
2542 }
2543 }
2544
2545 return r;
2546 }
2547
comp_compare(void * a,void * b)2548 static int comp_compare(void *a, void *b)
2549 {
2550 icalcomponent *c1 = (icalcomponent*) a;
2551 icalcomponent *c2 = (icalcomponent*) b;
2552 icalcomponent_kind k1 = icalcomponent_isa(c1);
2553 icalcomponent_kind k2 = icalcomponent_isa(c2);
2554 int r = k1 - k2;
2555
2556 if (r == 0) {
2557 if (k1 == ICAL_X_COMPONENT) {
2558 r = strcmp(c1->x_name, c2->x_name);
2559 }
2560
2561 if (r == 0) {
2562 const char *u1 = icalcomponent_get_uid(c1);
2563 const char *u2 = icalcomponent_get_uid(c2);
2564
2565 if (u1 && u2) {
2566 r = strcmp(u1, u2);
2567
2568 if (r == 0) {
2569 r = icaltime_compare(icalcomponent_get_recurrenceid(c1),
2570 icalcomponent_get_recurrenceid(c2));
2571 }
2572 } else {
2573 icalproperty *p1, *p2;
2574
2575 switch (k1) {
2576 case ICAL_VALARM_COMPONENT:
2577 p1 = icalcomponent_get_first_property(c1,
2578 ICAL_TRIGGER_PROPERTY);
2579 p2 = icalcomponent_get_first_property(c2,
2580 ICAL_TRIGGER_PROPERTY);
2581 r = strcmp(icalproperty_get_value_as_string(p1),
2582 icalproperty_get_value_as_string(p2));
2583
2584 if (r == 0) {
2585 p1 = icalcomponent_get_first_property(c1,
2586 ICAL_ACTION_PROPERTY);
2587 p2 = icalcomponent_get_first_property(c2,
2588 ICAL_ACTION_PROPERTY);
2589 r = strcmp(icalproperty_get_value_as_string(p1),
2590 icalproperty_get_value_as_string(p2));
2591 }
2592 break;
2593
2594 case ICAL_VTIMEZONE_COMPONENT:
2595 p1 = icalcomponent_get_first_property(c1,
2596 ICAL_TZID_PROPERTY);
2597 p2 = icalcomponent_get_first_property(c2,
2598 ICAL_TZID_PROPERTY);
2599 r = strcmp(icalproperty_get_value_as_string(p1),
2600 icalproperty_get_value_as_string(p2));
2601 break;
2602
2603 case ICAL_XSTANDARD_COMPONENT:
2604 case ICAL_XDAYLIGHT_COMPONENT:
2605 p1 = icalcomponent_get_first_property(c1,
2606 ICAL_DTSTART_PROPERTY);
2607 p2 = icalcomponent_get_first_property(c2,
2608 ICAL_DTSTART_PROPERTY);
2609 r = strcmp(icalproperty_get_value_as_string(p1),
2610 icalproperty_get_value_as_string(p2));
2611 break;
2612
2613 case ICAL_VVOTER_COMPONENT:
2614 p1 = icalcomponent_get_first_property(c1,
2615 ICAL_VOTER_PROPERTY);
2616 p2 = icalcomponent_get_first_property(c2,
2617 ICAL_VOTER_PROPERTY);
2618 r = strcmp(icalproperty_get_value_as_string(p1),
2619 icalproperty_get_value_as_string(p2));
2620 break;
2621
2622 case ICAL_XVOTE_COMPONENT:
2623 p1 = icalcomponent_get_first_property(c1,
2624 ICAL_POLLITEMID_PROPERTY);
2625 p2 = icalcomponent_get_first_property(c2,
2626 ICAL_POLLITEMID_PROPERTY);
2627 r = strcmp(icalproperty_get_value_as_string(p1),
2628 icalproperty_get_value_as_string(p2));
2629 break;
2630
2631 default:
2632 /* XXX Anything better? */
2633 r = icaltime_compare(icalcomponent_get_dtstamp(c1),
2634 icalcomponent_get_dtstamp(c2));
2635 break;
2636 }
2637 }
2638 }
2639 /* Always sort VTIMEZONEs first */
2640 } else if (k1 == ICAL_VTIMEZONE_COMPONENT) {
2641 return -1;
2642 } else if (k2 == ICAL_VTIMEZONE_COMPONENT) {
2643 return 1;
2644 }
2645
2646 return r;
2647 }
2648
icalcomponent_normalize(icalcomponent * comp)2649 void icalcomponent_normalize(icalcomponent *comp)
2650 {
2651 pvl_list sorted_props = pvl_newlist();
2652 pvl_list sorted_comps = pvl_newlist();
2653 icalproperty *prop;
2654 icalcomponent *sub;
2655
2656 /* Normalize properties into sorted list */
2657 while ((prop = pvl_pop(comp->properties)) != 0) {
2658 int nparams, remove = 0;
2659
2660 icalproperty_normalize(prop);
2661
2662 nparams = icalproperty_count_parameters(prop);
2663
2664 /* Remove unparameterized properties having default values */
2665 if (nparams == 0) {
2666 switch (icalproperty_isa(prop)) {
2667 case ICAL_CALSCALE_PROPERTY:
2668 if (strcmp("GREGORIAN", icalproperty_get_calscale(prop)) == 0) {
2669 remove = 1;
2670 }
2671 break;
2672
2673 case ICAL_CLASS_PROPERTY:
2674 if (icalproperty_get_class(prop) == ICAL_CLASS_PUBLIC) {
2675 remove = 1;
2676 }
2677 break;
2678
2679 case ICAL_PRIORITY_PROPERTY:
2680 if (icalproperty_get_priority(prop) == 0) {
2681 remove = 1;
2682 }
2683 break;
2684
2685 case ICAL_TRANSP_PROPERTY:
2686 if (icalproperty_get_transp(prop) == ICAL_TRANSP_OPAQUE) {
2687 remove = 1;
2688 }
2689 break;
2690
2691 case ICAL_REPEAT_PROPERTY:
2692 if (icalproperty_get_repeat(prop) == 0) {
2693 remove = 1;
2694 }
2695 break;
2696
2697 case ICAL_SEQUENCE_PROPERTY:
2698 if (icalproperty_get_sequence(prop) == 0) {
2699 remove = 1;
2700 }
2701 break;
2702
2703 default:
2704 break;
2705 }
2706 }
2707
2708 if (remove) {
2709 icalproperty_set_parent(prop, 0); // MUST NOT have a parent to free
2710 icalproperty_free(prop);
2711 } else {
2712 pvl_insert_ordered(sorted_props, prop_compare, prop);
2713 }
2714 }
2715
2716 pvl_free(comp->properties);
2717 comp->properties = sorted_props;
2718
2719 /* Normalize sub-components into sorted list */
2720 while ((sub = pvl_pop(comp->components)) != 0) {
2721 icalcomponent_normalize(sub);
2722 pvl_insert_ordered(sorted_comps, comp_compare, sub);
2723 }
2724
2725 pvl_free(comp->components);
2726 comp->components = sorted_comps;
2727 }
2728