Libical API Documentation 4.0 STABLE VERSION Visit the v3.0 documentation
Loading...
Searching...
No Matches
icalcomponent.c
Go to the documentation of this file.
1/*======================================================================
2 FILE: icalcomponent.c
3 CREATOR: eric 28 April 1999
4
5 SPDX-FileCopyrightText: 2000, Eric Busboom <eric@civicknowledge.com>
6 SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
7======================================================================*/
8
13
14#ifdef HAVE_CONFIG_H
15#include <config.h>
16#endif
17
18#include "icalcomponent.h"
19#include "icalerror_p.h"
20#include "icalerror.h"
21#include "icallimits.h"
22#include "icalmemory.h"
23#include "icalparser.h"
24#include "icalpvl_p.h"
25#include "icalrestriction.h"
26#include "icaltime_p.h"
27#include "icaltimezone.h"
28
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 bool 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 if (!icalcomponent_merge_vtimezone(comp, subcomp, tzids_to_rename)) {
2226 break;
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 icalarray_free(tzids_to_rename);
2236 tzids_to_rename = 0;
2237 /* Now move all the components from comp_to_merge to comp, excluding
2238 VTIMEZONE components. */
2239 subcomp = icalcomponent_get_first_component(comp_to_merge, ICAL_ANY_COMPONENT);
2240 while (subcomp) {
2241 next_subcomp = icalcomponent_get_next_component(comp_to_merge, ICAL_ANY_COMPONENT);
2243 icalcomponent_remove_component(comp_to_merge, subcomp);
2244 icalcomponent_add_component(comp, subcomp);
2245 }
2246 subcomp = next_subcomp;
2247 }
2248
2249 /* Free comp_to_merge. We have moved most of the subcomponents over to
2250 comp now. */
2251 icalcomponent_free(comp_to_merge);
2252}
2253
2254static bool icalcomponent_merge_vtimezone(icalcomponent *comp,
2255 icalcomponent *vtimezone, icalarray *tzids_to_rename)
2256{
2257 icalproperty *tzid_prop;
2258 const char *tzid;
2259 char *tzid_copy;
2260 const icaltimezone *existing_vtimezone;
2261
2262 /* Get the TZID of the VTIMEZONE. */
2263 tzid_prop = icalcomponent_get_first_property(vtimezone, ICAL_TZID_PROPERTY);
2264 if (!tzid_prop) {
2265 return false;
2266 }
2267
2268 tzid = icalproperty_get_tzid(tzid_prop);
2269 if (!tzid) {
2270 return false;
2271 }
2272
2273 /* See if there is already a VTIMEZONE in comp with the same TZID. */
2274 existing_vtimezone = icalcomponent_get_timezone(comp, tzid);
2275
2276 /* If there is no existing VTIMEZONE with the same TZID, we can just move
2277 the VTIMEZONE to comp and return. */
2278 if (!existing_vtimezone) {
2279 icalcomponent_remove_component(icalcomponent_get_parent(vtimezone), vtimezone);
2280 icalcomponent_add_component(comp, vtimezone);
2281 return false;
2282 }
2283
2284 /* If the TZID has a '/' prefix, then we don't have to worry about the
2285 clashing TZIDs, as they are supposed to be exactly the same VTIMEZONE. */
2286 if (tzid[0] == '/') {
2287 return false;
2288 }
2289
2290 /* Now we have two VTIMEZONEs with the same TZID (which isn't a globally
2291 unique one), so we compare the VTIMEZONE components to see if they are
2292 the same. If they are, we don't need to do anything. We make a copy of
2293 the tzid, since the parameter may get modified in these calls. */
2294 tzid_copy = icalmemory_strdup(tzid);
2295 if (!tzid_copy) {
2297 return false;
2298 }
2299
2300 const int match = icalcomponent_compare_vtimezones(comp, vtimezone);
2301 if (match == 0) {
2302 /* Now we have two different VTIMEZONEs with the same TZID. */
2303 icalcomponent_handle_conflicting_vtimezones(comp, vtimezone, tzid_prop,
2304 tzid_copy, tzids_to_rename);
2305 }
2306 icalmemory_free_buffer(tzid_copy);
2307 if (match == -1) {
2308 icalarray_free(tzids_to_rename);
2309 return false;
2310 }
2311 return true;
2312}
2313
2314static void icalcomponent_handle_conflicting_vtimezones(icalcomponent *comp,
2315 icalcomponent *vtimezone,
2316 icalproperty *tzid_prop,
2317 const char *tzid,
2318 icalarray *tzids_to_rename)
2319{
2320 int max_suffix = 0;
2321 size_t i, num_elements, tzid_len;
2322 char *new_tzid, suffix_buf[32];
2323
2324 _unused(tzid_prop);
2325
2326 /* Find the length of the TZID without any trailing digits. */
2327 tzid_len = icalcomponent_get_tzid_prefix_len(tzid);
2328
2329 /* Step through each of the VTIMEZONEs in comp. We may already have the
2330 clashing VTIMEZONE in the calendar, but it may have been renamed
2331 (i.e. a unique number added on the end of the TZID, e.g. 'London2').
2332 So we compare the new VTIMEZONE with any VTIMEZONEs that have the
2333 same prefix (e.g. 'London'). If it matches any of those, we have to
2334 rename the TZIDs to that TZID, else we rename to a new TZID, using
2335 the biggest numeric suffix found + 1. */
2336 num_elements = comp->timezones ? comp->timezones->num_elements : 0;
2337 for (i = 0; i < num_elements; i++) {
2339 const char *existing_tzid;
2340 size_t existing_tzid_len;
2341
2342 zone = icalarray_element_at(comp->timezones, i);
2343 existing_tzid = icaltimezone_get_tzid(zone);
2344
2345 /* Find the length of the TZID without any trailing digits. */
2346 existing_tzid_len = icalcomponent_get_tzid_prefix_len(existing_tzid);
2347
2348 /* Check if we have the same prefix. */
2349 if (tzid_len == existing_tzid_len && (strncmp(tzid, existing_tzid, tzid_len) != 0)) {
2350 /* Compare the VTIMEZONEs. */
2351 if (icalcomponent_compare_vtimezones(icaltimezone_get_component(zone), vtimezone)) {
2352 /* The VTIMEZONEs match, so we can use the existing VTIMEZONE. But
2353 we have to rename TZIDs to this TZID. */
2354 char *tzid_copy = icalmemory_strdup(tzid);
2355 if (!tzid_copy) {
2357 return;
2358 }
2359 char *existing_tzid_copy = icalmemory_strdup(existing_tzid);
2360 if (!existing_tzid_copy) {
2362 icalmemory_free_buffer(tzid_copy);
2363 } else {
2364 icalarray_append(tzids_to_rename, tzid_copy);
2365 icalmemory_free_buffer(tzid_copy);
2366 icalarray_append(tzids_to_rename, existing_tzid_copy);
2367 icalmemory_free_buffer(existing_tzid_copy);
2368 }
2369 return;
2370 } else {
2371 /* FIXME: Handle possible NEWFAILED error. */
2372
2373 /* Convert the suffix to an integer and remember the maximum numeric
2374 suffix found. */
2375 int suffix = atoi(existing_tzid + existing_tzid_len);
2376 if (max_suffix < suffix) {
2377 max_suffix = suffix;
2378 }
2379 }
2380 }
2381 }
2382
2383 /* We didn't find a VTIMEZONE that matched, so we have to rename the TZID,
2384 using the maximum numerical suffix found + 1. */
2385 char *tzid_copy = icalmemory_strdup(tzid);
2386 if (!tzid_copy) {
2388 return;
2389 }
2390
2391 snprintf(suffix_buf, sizeof(suffix_buf), "%i", max_suffix + 1);
2392 const size_t len_new_tzid = tzid_len + strlen(suffix_buf) + 1;
2393 new_tzid = icalmemory_new_buffer(len_new_tzid);
2394 if (!new_tzid) {
2396 icalmemory_free_buffer(tzid_copy);
2397 return;
2398 }
2399 strncpy(new_tzid, tzid, tzid_len);
2400 new_tzid[tzid_len] = '\0';
2401 strncat(new_tzid, suffix_buf, len_new_tzid);
2402 new_tzid[len_new_tzid - 1] = '\0';
2403 icalarray_append(tzids_to_rename, tzid_copy);
2404 icalarray_append(tzids_to_rename, new_tzid);
2405 icalmemory_free_buffer(tzid_copy);
2406 icalmemory_free_buffer(new_tzid);
2407}
2408
2409/* Returns the length of the TZID, without any trailing digits. */
2410static size_t icalcomponent_get_tzid_prefix_len(const char *tzid)
2411{
2412 size_t len;
2413 const char *p;
2414
2415 len = strlen(tzid);
2416 p = tzid + len - 1;
2417 while (len > 0 && *p >= '0' && *p <= '9') {
2418 p--;
2419 len--;
2420 }
2421
2422 return len;
2423}
2424
2430static void icalcomponent_rename_tzids(icalcomponent *comp, icalarray *rename_table)
2431{
2432 icalcomponent_foreach_tzid(comp, icalcomponent_rename_tzids_callback, rename_table);
2433}
2434
2435static void icalcomponent_rename_tzids_callback(icalparameter *param, void *data)
2436{
2437 icalarray *rename_table = data;
2438 const char *tzid;
2439 size_t i;
2440
2441 tzid = icalparameter_get_tzid(param);
2442 if (!tzid) {
2443 return;
2444 }
2445
2446 /* Step through the rename table to see if the current TZID matches
2447 any of the ones we want to rename. */
2448 for (i = 0; i < rename_table->num_elements - 1; i += 2) {
2449 if (!strcmp(tzid, icalarray_element_at(rename_table, i))) {
2450 icalparameter_set_tzid(param, icalarray_element_at(rename_table, i + 1));
2451 break;
2452 }
2453 }
2454}
2455
2456void icalcomponent_foreach_tzid(icalcomponent *comp,
2457 void (*callback)(icalparameter *param, void *data),
2458 void *callback_data)
2459{
2460 icalproperty *prop;
2461 icalcomponent *subcomp;
2462
2463 /* First look for any TZID parameters used in this component itself. */
2464 prop = icalcomponent_get_first_property(comp, ICAL_ANY_PROPERTY);
2465 while (prop) {
2466 icalproperty_kind kind = icalproperty_isa(prop);
2467
2468 /* These are the only properties that can have a TZID. Note that
2469 COMPLETED, CREATED, DTSTAMP & LASTMODIFIED must be in UTC. */
2470 if (kind == ICAL_DTSTART_PROPERTY ||
2471 kind == ICAL_DTEND_PROPERTY ||
2472 kind == ICAL_DUE_PROPERTY ||
2473 kind == ICAL_EXDATE_PROPERTY ||
2474 kind == ICAL_RDATE_PROPERTY) {
2475 icalparameter *param = icalproperty_get_first_parameter(prop, ICAL_TZID_PARAMETER);
2476 if (param) {
2477 (*callback)(param, callback_data);
2478 }
2479 }
2480
2481 prop = icalcomponent_get_next_property(comp, ICAL_ANY_PROPERTY);
2482 }
2483
2484 /* Now recursively check child components. */
2486 while (subcomp) {
2487 icalcomponent_foreach_tzid(subcomp, callback, callback_data);
2489 }
2490}
2491
2492icaltimezone *icalcomponent_get_timezone(icalcomponent *comp, const char *tzid)
2493{
2495 size_t lower, upper;
2496
2497 if (!comp->timezones) {
2498 return NULL;
2499 }
2500
2501 /* Sort the array if necessary (by the TZID string). */
2502 if (!comp->timezones_sorted) {
2503 icalarray_sort(comp->timezones, icalcomponent_compare_timezone_fn);
2504 comp->timezones_sorted = 1;
2505 }
2506
2507 /* Do a simple binary search. */
2508 lower = 0;
2509 upper = comp->timezones->num_elements;
2510
2511 while (lower < upper) {
2512 size_t middle = (lower + upper) >> 1;
2513 zone = icalarray_element_at(comp->timezones, middle);
2514 const char *zone_tzid = icaltimezone_get_tzid(zone);
2515 if (zone_tzid != NULL) {
2516 int cmp = strcmp(tzid, zone_tzid);
2517 if (cmp == 0) {
2518 return zone;
2519 } else if (cmp < 0) {
2520 upper = middle;
2521 } else {
2522 lower = middle + 1;
2523 }
2524 }
2525 }
2526
2527 return NULL;
2528}
2529
2533static int icalcomponent_compare_timezone_fn(const void *elem1, const void *elem2)
2534{
2535 icaltimezone *zone1, *zone2;
2536 const char *zone1_tzid = 0, *zone2_tzid = 0;
2537
2538 zone1 = (icaltimezone *)elem1;
2539 zone2 = (icaltimezone *)elem2;
2540
2541 const bool zone1_is_valid = (zone1 && (zone1_tzid = icaltimezone_get_tzid(zone1)));
2542 const bool zone2_is_valid = (zone2 && (zone2_tzid = icaltimezone_get_tzid(zone2)));
2543
2544 if (zone1_is_valid && !zone2_is_valid) {
2545 return 1;
2546 }
2547 if (!zone1_is_valid) {
2548 if (zone2_is_valid) {
2549 return -1;
2550 } else {
2551 return 0;
2552 }
2553 }
2554
2555 return strcmp(zone1_tzid, zone2_tzid);
2556}
2557
2562static int icalcomponent_compare_vtimezones(icalcomponent *vtimezone1, icalcomponent *vtimezone2)
2563{
2564 icalproperty *prop1, *prop2;
2565 const char *tzid1, *tzid2;
2566 char *tzid2_copy, *string1, *string2;
2567 int cmp;
2568
2569 /* Get the TZID property of the first VTIMEZONE. */
2570 prop1 = icalcomponent_get_first_property(vtimezone1, ICAL_TZID_PROPERTY);
2571 if (!prop1) {
2572 return -1;
2573 }
2574
2575 tzid1 = icalproperty_get_tzid(prop1);
2576 if (!tzid1) {
2577 return -1;
2578 }
2579
2580 /* Get the TZID property of the second VTIMEZONE. */
2581 prop2 = icalcomponent_get_first_property(vtimezone2, ICAL_TZID_PROPERTY);
2582 if (!prop2) {
2583 return -1;
2584 }
2585
2586 tzid2 = icalproperty_get_tzid(prop2);
2587 if (!tzid2) {
2588 return -1;
2589 }
2590
2591 /* Copy the second TZID, and set the property to the same as the first
2592 TZID, since we don't care if these match of not. */
2593 tzid2_copy = icalmemory_strdup(tzid2);
2594 if (!tzid2_copy) {
2596 return 0;
2597 }
2598
2599 icalproperty_set_tzid(prop2, tzid1);
2600
2601 /* Now convert both VTIMEZONEs to strings and compare them. */
2602 string1 = icalcomponent_as_ical_string_r(vtimezone1);
2603 if (!string1) {
2604 icalmemory_free_buffer(tzid2_copy);
2605 return -1;
2606 }
2607
2608 string2 = icalcomponent_as_ical_string_r(vtimezone2);
2609 if (!string2) {
2610 icalmemory_free_buffer(string1);
2611 icalmemory_free_buffer(tzid2_copy);
2612 return -1;
2613 }
2614
2615 cmp = strcmp(string1, string2);
2616
2617 icalmemory_free_buffer(string1);
2618 icalmemory_free_buffer(string2);
2619
2620 /* Now reset the second TZID. */
2621 icalproperty_set_tzid(prop2, tzid2_copy);
2622 icalmemory_free_buffer(tzid2_copy);
2623
2624 return (cmp == 0) ? 1 : 0;
2625}
2626
2627void icalcomponent_set_relcalid(icalcomponent *comp, const char *v)
2628{
2629 ICALSETUPSET(ICAL_RELCALID_PROPERTY);
2630
2631 if (prop == 0) {
2632 prop = icalproperty_new_relcalid(v);
2633 icalcomponent_add_property(inner, prop);
2634 }
2635
2636 icalproperty_set_relcalid(prop, v);
2637}
2638
2639const char *icalcomponent_get_relcalid(icalcomponent *comp)
2640{
2641 icalcomponent *inner;
2642 icalproperty *prop;
2643
2644 icalerror_check_arg_rz(comp != 0, "comp");
2645
2646 inner = icalcomponent_get_inner(comp);
2647
2648 if (inner == 0) {
2649 return 0;
2650 }
2651
2652 prop = icalcomponent_get_first_property(inner, ICAL_RELCALID_PROPERTY);
2653
2654 if (prop == 0) {
2655 return 0;
2656 }
2657
2658 return icalproperty_get_relcalid(prop);
2659}
2660
2661struct icaltimetype icalcomponent_get_due(icalcomponent *comp)
2662{
2663 icalcomponent *inner = icalcomponent_get_inner(comp);
2664
2665 icalproperty *due_prop = icalcomponent_get_first_property(inner, ICAL_DUE_PROPERTY);
2666
2667 icalproperty *dur_prop = icalcomponent_get_first_property(inner, ICAL_DURATION_PROPERTY);
2668
2669 if (due_prop != 0) {
2670 return icalproperty_get_datetime_with_component(due_prop, comp);
2671 } else if (dur_prop != 0) {
2672 struct icaltimetype start = icalcomponent_get_dtstart(inner);
2673 struct icaldurationtype duration = icalproperty_get_duration(dur_prop);
2674
2675 struct icaltimetype due = icalduration_extend(start, duration);
2676
2677 return due;
2678 }
2679 return icaltime_null_time();
2680}
2681
2682void icalcomponent_set_due(icalcomponent *comp, struct icaltimetype v)
2683{
2684 const char *tzid;
2685
2686 icalcomponent *inner = icalcomponent_get_inner(comp);
2687
2688 icalproperty *due_prop = icalcomponent_get_first_property(inner, ICAL_DUE_PROPERTY);
2689
2690 icalproperty *dur_prop = icalcomponent_get_first_property(inner, ICAL_DURATION_PROPERTY);
2691
2692 if (due_prop == 0 && dur_prop == 0) {
2693 due_prop = icalproperty_new_due(v);
2694 icalcomponent_add_property(inner, due_prop);
2695 } else if (due_prop != 0) {
2696 icalproperty_set_due(due_prop, v);
2697 icalproperty_remove_parameter_by_kind(due_prop, ICAL_TZID_PARAMETER);
2698 } else if (dur_prop != 0) {
2699 struct icaltimetype start = icalcomponent_get_dtstart(inner);
2700
2701 struct icaltimetype due = icalcomponent_get_due(inner);
2702
2703 struct icaldurationtype dur = icalduration_from_times(due, start);
2704
2705 icalproperty_set_duration(dur_prop, dur);
2706 }
2707
2708 if (due_prop && (tzid = icaltime_get_tzid(v)) != NULL && !icaltime_is_utc(v)) {
2709 icalproperty_set_parameter(due_prop, icalparameter_new_tzid(tzid));
2710 }
2711}
2712
2713static int strcmpsafe(const char *a, const char *b)
2714{
2715 return strcmp((a == NULL ? "" : a),
2716 (b == NULL ? "" : b));
2717}
2718
2719static int prop_compare(void *a, void *b)
2720{
2721 const icalproperty *p1 = (icalproperty *)a;
2722 const icalproperty *p2 = (icalproperty *)b;
2723 icalproperty_kind k1 = icalproperty_isa(p1);
2724 icalproperty_kind k2 = icalproperty_isa(p2);
2725 int r = (int)(k1 - k2);
2726
2727 if (r == 0) {
2728 if (k1 == ICAL_X_PROPERTY) {
2729 r = strcmp(icalproperty_get_x_name(p1),
2731 }
2732
2733 if (r == 0) {
2734 r = strcmpsafe(icalproperty_get_value_as_string(p1),
2736 }
2737 }
2738
2739 return r;
2740}
2741
2742static inline int compare_nullptr(const void *a, const void *b)
2743{
2744 if (!a == !b) {
2745 return 0;
2746 }
2747
2748 // non-NULL sorts before NULL
2749 return a ? -1 : 1;
2750}
2751
2752static int comp_compare(void *a, void *b)
2753{
2754 icalcomponent *c1 = (icalcomponent *)a;
2755 icalcomponent *c2 = (icalcomponent *)b;
2758 int r = (int)(k1 - k2);
2759
2760 if (r == 0) {
2761 if ((k1 == ICAL_X_COMPONENT || k1 == ICAL_IANA_COMPONENT) &&
2762 (c1->x_name && c2->x_name)) {
2763 r = strcmp(c1->x_name, c2->x_name);
2764 }
2765
2766 if (r == 0) {
2767 const char *u1 = icalcomponent_get_uid(c1);
2768 const char *u2 = icalcomponent_get_uid(c2);
2769
2770 if (u1 && u2) {
2771 r = strcmp(u1, u2);
2772
2773 if (r == 0) {
2776 }
2777 } else {
2778 const icalproperty *p1, *p2;
2779
2780 switch (k1) {
2783 ICAL_TRIGGER_PROPERTY);
2785 ICAL_TRIGGER_PROPERTY);
2786 if (p1 && p2) {
2787 r = strcmp(icalproperty_get_value_as_string(p1),
2789 if (r == 0) {
2791 ICAL_ACTION_PROPERTY);
2793 ICAL_ACTION_PROPERTY);
2794 if (p1 && p2) {
2795 r = strcmp(icalproperty_get_value_as_string(p1),
2797 } else {
2798 r = compare_nullptr(p1, p2);
2799 }
2800 }
2801 } else {
2802 r = compare_nullptr(p1, p2);
2803 }
2804
2805 break;
2806
2809 ICAL_TZID_PROPERTY);
2811 ICAL_TZID_PROPERTY);
2812 if (p1 && p2) {
2813 r = strcmp(icalproperty_get_value_as_string(p1),
2815 } else {
2816 r = compare_nullptr(p1, p2);
2817 }
2818 break;
2819
2823 ICAL_DTSTART_PROPERTY);
2825 ICAL_DTSTART_PROPERTY);
2826
2827 if (p1 && p2) {
2828 r = strcmp(icalproperty_get_value_as_string(p1),
2830 } else {
2831 r = compare_nullptr(p1, p2);
2832 }
2833 break;
2834
2837 ICAL_VOTER_PROPERTY);
2839 ICAL_VOTER_PROPERTY);
2840
2841 if (p1 && p2) {
2842 r = strcmp(icalproperty_get_value_as_string(p1),
2844 } else {
2845 r = compare_nullptr(p1, p2);
2846 }
2847 break;
2848
2851 ICAL_POLLITEMID_PROPERTY);
2853 ICAL_POLLITEMID_PROPERTY);
2854
2855 if (p1 && p2) {
2856 r = strcmp(icalproperty_get_value_as_string(p1),
2858 } else {
2859 r = compare_nullptr(p1, p2);
2860 }
2861 break;
2862
2863 default:
2864 /* XXX Anything better? */
2867 break;
2868 }
2869 }
2870 }
2871 /* Always sort VTIMEZONEs first */
2872 } else if (k1 == ICAL_VTIMEZONE_COMPONENT) {
2873 return -1;
2874 } else if (k2 == ICAL_VTIMEZONE_COMPONENT) {
2875 return 1;
2876 }
2877
2878 return r;
2879}
2880
2881void icalcomponent_normalize(icalcomponent *comp)
2882{
2883 icalproperty *prop;
2884 icalcomponent *sub;
2885 icalpvl_list sorted_props;
2886 icalpvl_list sorted_comps;
2887 size_t cnt = 0; //track properties
2888
2889 icalerror_check_arg(comp != 0, "comp");
2890 if (!comp) {
2891 return;
2892 }
2893
2894 sorted_props = icalpvl_newlist();
2895 sorted_comps = icalpvl_newlist();
2896
2897 /* oss-fuzz sets the cpu timeout at 60 seconds.
2898 * In order to meet that requirement we need to cap the number of properties.
2899 */
2900 const size_t max_properties = icallimit_get(ICAL_LIMIT_PROPERTIES);
2901 /* Normalize properties into sorted list */
2902 while ((++cnt < max_properties) && ((prop = icalpvl_pop(comp->properties)) != 0)) {
2903 int nparams, remove = 0;
2904
2906
2907 nparams = icalproperty_count_parameters(prop);
2908
2909 /* Remove un-parameterized properties having default values */
2910 if (nparams == 0) {
2911 switch (icalproperty_isa(prop)) {
2912 case ICAL_CALSCALE_PROPERTY:
2913 if (strcmp("GREGORIAN", icalproperty_get_calscale(prop)) == 0) {
2914 remove = 1;
2915 }
2916 break;
2917
2918 case ICAL_CLASS_PROPERTY:
2919 if (icalproperty_get_class(prop) == ICAL_CLASS_PUBLIC) {
2920 remove = 1;
2921 }
2922 break;
2923
2924 case ICAL_PRIORITY_PROPERTY:
2925 if (icalproperty_get_priority(prop) == 0) {
2926 remove = 1;
2927 }
2928 break;
2929
2930 case ICAL_TRANSP_PROPERTY:
2931 if (icalproperty_get_transp(prop) == ICAL_TRANSP_OPAQUE) {
2932 remove = 1;
2933 }
2934 break;
2935
2936 case ICAL_REPEAT_PROPERTY:
2937 if (icalproperty_get_repeat(prop) == 0) {
2938 remove = 1;
2939 }
2940 break;
2941
2942 case ICAL_SEQUENCE_PROPERTY:
2943 if (icalproperty_get_sequence(prop) == 0) {
2944 remove = 1;
2945 }
2946 break;
2947
2948 default:
2949 break;
2950 }
2951 }
2952
2953 if (remove) {
2954 icalproperty_set_parent(prop, 0); // MUST NOT have a parent to free
2955 icalproperty_free(prop);
2956 } else {
2957 icalpvl_insert_ordered(sorted_props, prop_compare, prop);
2958 }
2959 }
2960
2961 /* Drain the remaining properties */
2962 if (cnt == max_properties) {
2963 while ((prop = icalpvl_pop(comp->properties)) != 0) {
2964 icalproperty_set_parent(prop, 0); // MUST NOT have a parent to free
2965 icalproperty_free(prop);
2966 }
2967 }
2968
2969 icalpvl_free(comp->properties);
2970 comp->properties = sorted_props;
2971
2972 /* Normalize sub-components into sorted list */
2973 while ((sub = icalpvl_pop(comp->components)) != 0) {
2975 icalpvl_insert_ordered(sorted_comps, comp_compare, sub);
2976 }
2977
2978 icalpvl_free(comp->components);
2979 comp->components = sorted_comps;
2980}
void * icalarray_element_at(icalarray *array, size_t position)
Access an array element.
Definition icalarray.c:135
void icalarray_free(icalarray *array)
Definition icalarray.c:104
void icalarray_sort(icalarray *array, int(*compare)(const void *, const void *))
Sorts the elements of an icalarray using the given comparison function.
Definition icalarray.c:182
void icalarray_append(icalarray *array, const void *element)
Appends an element to an array.
Definition icalarray.c:119
icalarray * icalarray_new(size_t element_size, size_t increment_size)
Definition icalarray.c:36
void icalarray_remove_element_at(icalarray *array, size_t position)
Removes a given element from an array.
Definition icalarray.c:148
void icalcomponent_remove_property_by_kind(icalcomponent *component, icalproperty_kind kind)
void icalcomponent_convert_errors(icalcomponent *component)
icalcomponent * icalcomponent_new_xstandard(void)
void icalcomponent_set_relcalid(icalcomponent *comp, const char *v)
bool icalcomponent_is_valid(const icalcomponent *component)
icalproperty * icalpropiter_deref(icalpropiter *i)
icalcomponent * icalcomponent_new_vagenda(void)
void icalcomponent_set_uid(icalcomponent *comp, const char *v)
icalcomponent_kind icalcomponent_string_to_kind(const char *string)
void icalcomponent_set_x_name(icalcomponent *comp, const char *name)
struct icaltimetype icalcomponent_get_dtend(icalcomponent *comp)
struct icaltimetype icalcomponent_get_dtstart(icalcomponent *comp)
icalcomponent * icalcomponent_new_vlocation(void)
icalproperty * icalcomponent_get_first_property(icalcomponent *c, icalproperty_kind kind)
bool icalcomponent_check_restrictions(icalcomponent *comp)
icalcomponent * icalcomponent_new_vpoll(void)
int icalcomponent_count_properties(icalcomponent *component, icalproperty_kind kind)
icalcomponent * icalcomponent_new_vavailability(void)
icalcomponent * icalcomponent_new_xpatch(void)
icalproperty_method icalcomponent_get_method(icalcomponent *comp)
bool icalcompiter_is_valid(const icalcompiter *i)
icalcomponent * icalcomponent_new_vreply(void)
void icalcomponent_set_recurrenceid(icalcomponent *comp, struct icaltimetype v)
icalcomponent * icalcomponent_new_xvote(void)
struct icaltimetype icalcomponent_get_recurrenceid(icalcomponent *comp)
icalcomponent * icalcompiter_next(icalcompiter *i)
void icalcomponent_set_location(icalcomponent *comp, const char *v)
const char * icalcomponent_get_description(icalcomponent *comp)
void icalcomponent_set_iana_name(icalcomponent *comp, const char *name)
icalcomponent * icalcomponent_get_next_component(icalcomponent *c, icalcomponent_kind kind)
icalcomponent * icalcomponent_get_first_component(icalcomponent *c, icalcomponent_kind kind)
void icalcomponent_set_dtstart(icalcomponent *comp, struct icaltimetype v)
void icalcomponent_set_dtstamp(icalcomponent *comp, struct icaltimetype v)
void icalcomponent_strip_errors(icalcomponent *component)
icalcomponent * icalcomponent_new_vcalendar(void)
void icalcomponent_normalize(icalcomponent *comp)
icalpropiter icalcomponent_begin_property(icalcomponent *component, icalproperty_kind kind)
icalcomponent * icalcomponent_get_current_component(icalcomponent *component)
void icalcomponent_remove_property(icalcomponent *component, icalproperty *property)
char * icalcomponent_as_ical_string(const icalcomponent *component)
const char * icalcomponent_kind_to_string(icalcomponent_kind kind)
enum icalproperty_status icalcomponent_get_status(icalcomponent *comp)
icalcomponent * icalcomponent_new_vjournal(void)
void icalcomponent_remove_component(icalcomponent *parent, icalcomponent *child)
icalcomponent * icalcomponent_new_vpatch(void)
icalcomponent * icalcomponent_new_vfreebusy(void)
icalproperty * icalpropiter_next(icalpropiter *i)
icalcomponent * icalcompiter_deref(icalcompiter *i)
icalcomponent * icalcomponent_vanew(icalcomponent_kind kind,...)
void icalcomponent_set_method(icalcomponent *comp, icalproperty_method method)
icalcomponent * icalcomponent_new(icalcomponent_kind kind)
void icalcomponent_set_summary(icalcomponent *comp, const char *v)
void icalcomponent_set_sequence(icalcomponent *comp, int v)
icalcomponent * icalcomponent_get_inner(icalcomponent *comp)
const char * icalcomponent_get_comment(icalcomponent *comp)
const char * icalcomponent_get_summary(icalcomponent *comp)
icaltime_span icalcomponent_get_span(icalcomponent *comp)
const char * icalcomponent_get_relcalid(icalcomponent *comp)
icalcomponent * icalcomponent_get_first_real_component(const icalcomponent *c)
icalcomponent * icalcomponent_new_vtimezone(void)
struct icaltimetype icalcomponent_get_due(icalcomponent *comp)
void icalcomponent_merge_component(icalcomponent *comp, icalcomponent *comp_to_merge)
bool icalpropiter_is_valid(const icalpropiter *i)
icalcomponent * icalcomponent_new_vresource(void)
bool icalproperty_recurrence_is_excluded(icalcomponent *comp, struct icaltimetype *dtstart, struct icaltimetype *recurtime)
Decides if a recurrence is acceptable.
const char * icalcomponent_get_x_name(const icalcomponent *comp)
int icalcomponent_count_errors(icalcomponent *component)
struct icaldurationtype icalcomponent_get_duration(icalcomponent *comp)
icalcomponent * icalcomponent_clone(const icalcomponent *old)
icalcomponent * icalcomponent_new_iana(const char *iana_name)
icalcomponent * icalcomponent_new_xavailable(void)
icalcomponent * icalcomponent_new_participant(void)
const char * icalcomponent_get_iana_name(const icalcomponent *comp)
icalproperty * icalcomponent_get_current_property(icalcomponent *component)
icaltimezone * icalcomponent_get_timezone(icalcomponent *comp, const char *tzid)
void icalcomponent_set_dtend(icalcomponent *comp, struct icaltimetype v)
const char * icalcomponent_get_uid(icalcomponent *comp)
int icalcomponent_get_sequence(icalcomponent *comp)
icalcompiter icalcomponent_end_component(icalcomponent *component, icalcomponent_kind kind)
const char * icalcomponent_get_component_name(const icalcomponent *comp)
icalcomponent * icalcomponent_new_xdaylight(void)
icalcomponent * icalcomponent_new_valarm(void)
icalcomponent * icalcomponent_new_x(const char *x_name)
void icalcomponent_add_property(icalcomponent *component, icalproperty *property)
void icalcomponent_set_duration(icalcomponent *comp, struct icaldurationtype v)
icalcompiter icalcomponent_begin_component(icalcomponent *component, icalcomponent_kind kind)
char * icalcomponent_as_ical_string_r(const icalcomponent *component)
icalcomponent_kind icalcomponent_isa(const icalcomponent *component)
icalcomponent * icalcompiter_prior(icalcompiter *i)
icalcomponent * icalcomponent_new_vtodo(void)
bool icalcomponent_kind_is_valid(const icalcomponent_kind kind)
char * icalcomponent_get_component_name_r(const icalcomponent *comp)
void icalcomponent_set_status(icalcomponent *comp, enum icalproperty_status v)
void icalcomponent_foreach_tzid(icalcomponent *comp, void(*callback)(icalparameter *param, void *data), void *callback_data)
const char * icalcomponent_get_location(icalcomponent *comp)
void icalcomponent_free(icalcomponent *c)
icalcomponent * icalcomponent_new_vevent(void)
void icalcomponent_set_due(icalcomponent *comp, struct icaltimetype v)
void icalcomponent_foreach_recurrence(icalcomponent *comp, struct icaltimetype start, struct icaltimetype end, void(*callback)(icalcomponent *comp, const struct icaltime_span *span, void *data), void *callback_data)
void icalcomponent_add_component(icalcomponent *parent, icalcomponent *child)
icalproperty * icalcomponent_get_next_property(icalcomponent *c, icalproperty_kind kind)
void icalcomponent_set_description(icalcomponent *comp, const char *v)
icalcomponent * icalcomponent_new_from_string(const char *str)
int icalcomponent_count_components(icalcomponent *component, icalcomponent_kind kind)
void icalcomponent_set_comment(icalcomponent *comp, const char *v)
icalcomponent * icalcomponent_new_vquery(void)
bool icalcomponent_isa_component(const void *component)
icalcomponent * icalcomponent_new_vvoter(void)
struct icaltimetype icalcomponent_get_dtstamp(icalcomponent *comp)
Defines the data structure for iCalendar components.
icalcomponent * icalproperty_get_parent(const icalproperty *property)
struct icaltimetype icalproperty_get_datetime_with_component(icalproperty *prop, icalcomponent *comp)
void icalproperty_set_parent(icalproperty *property, icalcomponent *component)
struct icaldurationtype icalduration_from_times(struct icaltimetype t1, struct icaltimetype t2)
Creates a duration from two icaltimetype endpoints.
struct icaltimetype icalduration_extend(struct icaltimetype t, struct icaldurationtype d)
Extends a time duration.
bool icaldurationtype_is_null_duration(struct icaldurationtype d)
Checks if a duration is a null duration.
struct icaldurationtype icaldurationtype_null_duration(void)
Creates a duration with zero length.
icalcomponent_kind
Definition icalenums.h:29
@ ICAL_X_COMPONENT
Definition icalenums.h:48
@ ICAL_XROOT_COMPONENT
Definition icalenums.h:32
@ ICAL_XDAYLIGHT_COMPONENT
Definition icalenums.h:47
@ ICAL_VREPLY_COMPONENT
Definition icalenums.h:51
@ ICAL_VLOCATION_COMPONENT
Definition icalenums.h:65
@ ICAL_XSTANDARD_COMPONENT
Definition icalenums.h:46
@ ICAL_NO_COMPONENT
Definition icalenums.h:30
@ ICAL_VFREEBUSY_COMPONENT
Definition icalenums.h:39
@ ICAL_VTODO_COMPONENT
Definition icalenums.h:35
@ ICAL_PARTICIPANT_COMPONENT
Definition icalenums.h:64
@ ICAL_VEVENT_COMPONENT
Definition icalenums.h:34
@ ICAL_VPATCH_COMPONENT
Definition icalenums.h:62
@ ICAL_VCALENDAR_COMPONENT
Definition icalenums.h:37
@ ICAL_VAVAILABILITY_COMPONENT
Definition icalenums.h:57
@ ICAL_IANA_COMPONENT
Definition icalenums.h:67
@ ICAL_VSCHEDULE_COMPONENT
Definition icalenums.h:49
@ ICAL_XVOTE_COMPONENT
Definition icalenums.h:61
@ ICAL_VAGENDA_COMPONENT
Definition icalenums.h:38
@ ICAL_ANY_COMPONENT
Definition icalenums.h:31
@ ICAL_VTIMEZONE_COMPONENT
Definition icalenums.h:45
@ ICAL_VJOURNAL_COMPONENT
Definition icalenums.h:36
@ ICAL_VVOTER_COMPONENT
Definition icalenums.h:60
@ ICAL_VCAR_COMPONENT
Definition icalenums.h:52
@ ICAL_VCOMMAND_COMPONENT
Definition icalenums.h:53
@ ICAL_VQUERY_COMPONENT
Definition icalenums.h:50
@ ICAL_XLICMIMEPART_COMPONENT
Definition icalenums.h:55
@ ICAL_XPATCH_COMPONENT
Definition icalenums.h:63
@ ICAL_VPOLL_COMPONENT
Definition icalenums.h:59
@ ICAL_XAVAILABLE_COMPONENT
Definition icalenums.h:58
@ ICAL_XLICINVALID_COMPONENT
Definition icalenums.h:54
@ ICAL_VRESOURCE_COMPONENT
Definition icalenums.h:66
@ ICAL_VALARM_COMPONENT
Definition icalenums.h:40
@ ICAL_3_2_INVPARAM_STATUS
Definition icalenums.h:95
@ ICAL_3_0_INVPROPNAME_STATUS
Definition icalenums.h:93
@ ICAL_3_1_INVPROPVAL_STATUS
Definition icalenums.h:94
@ ICAL_3_4_INVCOMP_STATUS
Definition icalenums.h:97
@ ICAL_3_3_INVPARAMVAL_STATUS
Definition icalenums.h:96
@ ICAL_UNKNOWN_STATUS
Definition icalenums.h:80
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
Definition icalerror.c:90
Error handling for libical.
@ ICAL_NEWFAILED_ERROR
Definition icalerror.h:50
@ ICAL_BADARG_ERROR
Definition icalerror.h:47
@ ICAL_MALFORMEDDATA_ERROR
Definition icalerror.h:59
@ ICAL_USAGE_ERROR
Definition icalerror.h:71
size_t icallimit_get(icallimits_kind kind)
Definition icallimits.c:29
Defines the interface for getting/setting internal library limits.
@ ICAL_LIMIT_PROPERTIES
Definition icallimits.h:30
void icalmemory_free_buffer(void *buf)
Releases a buffer.
Definition icalmemory.c:355
char * icalmemory_strdup(const char *s)
Creates a duplicate of a string.
Definition icalmemory.c:242
void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
Appends a string to a buffer.
Definition icalmemory.c:365
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
Definition icalmemory.c:315
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
Definition icalmemory.c:158
Common memory management routines.
icalcomponent * icalparser_parse_string(const char *str)
Parses a string and returns the parsed icalcomponent.
Line-oriented parsing.
icalvalue * icalproperty_get_value(const icalproperty *prop)
bool icalproperty_isa_property(void *property)
void icalproperty_free(icalproperty *p)
char * icalproperty_as_ical_string_r(icalproperty *prop)
icalproperty_kind icalproperty_isa(const icalproperty *p)
int icalproperty_count_parameters(const icalproperty *prop)
const char * icalproperty_get_value_as_string(const icalproperty *prop)
void icalproperty_remove_parameter_by_kind(icalproperty *prop, icalparameter_kind kind)
Removes all parameters with the specified kind.
icalparameter * icalproperty_get_first_parameter(icalproperty *p, icalparameter_kind kind)
void icalproperty_add_parameter(icalproperty *p, icalparameter *parameter)
void icalproperty_normalize(icalproperty *prop)
icalproperty * icalproperty_clone(const icalproperty *old)
const char * icalproperty_get_x_name(const icalproperty *prop)
void icalproperty_set_parameter(icalproperty *prop, icalparameter *parameter)
bool icalrecur_iterator_set_start(icalrecur_iterator *impl, struct icaltimetype start)
Definition icalrecur.c:4105
void icalrecur_iterator_free(icalrecur_iterator *impl)
Definition icalrecur.c:2451
icalrecur_iterator * icalrecur_iterator_new(struct icalrecurrencetype *rule, struct icaltimetype dtstart)
Definition icalrecur.c:2323
struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
Definition icalrecur.c:3694
Functions to check if an icalcomponent meets the restrictions imposed by the standard.
bool icalrestriction_check(icalcomponent *comp)
Checks if a given VCALENDAR meets all the restrictions imposed by the standard.
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