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