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