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