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