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