3 /*======================================================================
5 CREATOR: eric 28 April 1999
7 $Id: icalproperty.c,v 1.44 2008-01-30 20:28:42 dothebart Exp $
10 (C) COPYRIGHT 2000, Eric Busboom <eric@softwarestudio.org>
11 http://www.softwarestudio.org
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of either:
16 The LGPL as published by the Free Software Foundation, version
17 2.1, available at: http://www.fsf.org/copyleft/lesser.html
21 The Mozilla Public License Version 1.0. You may obtain a copy of
22 the License at http://www.mozilla.org/MPL/
24 The original code is icalproperty.c
26 ======================================================================*/
32 #include "icalproperty.h"
33 #include "icalparameter.h"
34 #include "icalcomponent.h"
36 #include "icalenums.h"
37 #include "icalerror.h"
38 #include "icalmemory.h"
39 #include "icalparser.h"
41 #include <string.h> /* For icalmemory_strdup, rindex */
45 #include <stdio.h> /* for printf */
46 #include <stdarg.h> /* for va_list, va_start, etc. */
49 #define snprintf _snprintf
50 #define strcasecmp stricmp
53 /* Private routines for icalproperty */
54 void icalvalue_set_parent(icalvalue* value,
55 icalproperty* property);
56 icalproperty* icalvalue_get_parent(icalvalue* value);
58 void icalparameter_set_parent(icalparameter* param,
59 icalproperty* property);
60 icalproperty* icalparameter_get_parent(icalparameter* value);
63 void icalproperty_set_x_name(icalproperty* prop, const char* name);
65 struct icalproperty_impl
68 icalproperty_kind kind;
71 pvl_elem parameter_iterator;
73 icalcomponent *parent;
76 void icalproperty_add_parameters(icalproperty* prop, va_list args)
80 while((vp = va_arg(args, void*)) != 0) {
82 if (icalvalue_isa_value(vp) != 0 ){
83 } else if (icalparameter_isa_parameter(vp) != 0 ){
85 icalproperty_add_parameter((icalproperty*)prop,
88 icalerror_set_errno(ICAL_BADARG_ERROR);
96 icalproperty_new_impl(icalproperty_kind kind)
100 if (!icalproperty_kind_is_valid(kind))
103 if ( ( prop = (icalproperty*) malloc(sizeof(icalproperty))) == 0) {
104 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
108 strcpy(prop->id,"prop");
111 prop->parameters = pvl_newlist();
112 prop->parameter_iterator = 0;
122 icalproperty_new (icalproperty_kind kind)
124 if (kind == ICAL_NO_PROPERTY){
128 return (icalproperty*)icalproperty_new_impl(kind);
133 icalproperty_new_clone(icalproperty* old)
135 icalproperty *new = icalproperty_new_impl(old->kind);
138 icalerror_check_arg_rz((old!=0),"old");
139 icalerror_check_arg_rz((new!=0),"new");
141 if (old->value !=0) {
142 new->value = icalvalue_new_clone(old->value);
145 if (old->x_name != 0) {
147 new->x_name = icalmemory_strdup(old->x_name);
149 if (new->x_name == 0) {
150 icalproperty_free(new);
151 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
156 for(p=pvl_head(old->parameters);p != 0; p = pvl_next(p)){
157 icalparameter *param = icalparameter_new_clone(pvl_data(p));
160 icalproperty_free(new);
161 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
165 pvl_push(new->parameters,param);
173 icalproperty* icalproperty_new_from_string(const char* str)
176 size_t buf_size = 1024;
183 icalerror_check_arg_rz( (str!=0),"str");
185 buf = icalmemory_new_buffer(buf_size);
188 /* Is this a HACK or a crafty reuse of code? */
190 icalmemory_append_string(&buf, &buf_ptr, &buf_size, "BEGIN:VCALENDAR\r\n");
191 icalmemory_append_string(&buf, &buf_ptr, &buf_size, str);
192 icalmemory_append_string(&buf, &buf_ptr, &buf_size, "\r\n");
193 icalmemory_append_string(&buf, &buf_ptr, &buf_size, "END:VCALENDAR\r\n");
195 comp = icalparser_parse_string(buf);
198 icalerror_set_errno(ICAL_PARSE_ERROR);
203 errors = icalcomponent_count_errors(comp);
205 prop = icalcomponent_get_first_property(comp,ICAL_ANY_PROPERTY);
207 icalcomponent_remove_property(comp,prop);
209 icalcomponent_free(comp);
213 icalproperty_free(prop);
222 icalproperty_free (icalproperty* p)
224 icalparameter* param;
226 icalerror_check_arg_rv((p!=0),"prop");
228 #ifdef ICAL_FREE_ON_LIST_IS_ERROR
229 icalerror_assert( (p->parent ==0),"Tried to free a property that is still attached to a component. ");
238 icalvalue_set_parent(p->value,0);
239 icalvalue_free(p->value);
242 while( (param = pvl_pop(p->parameters)) != 0){
243 icalparameter_free(param);
246 pvl_free(p->parameters);
248 if (p->x_name != 0) {
252 p->kind = ICAL_NO_PROPERTY;
254 p->parameter_iterator = 0;
264 /* This returns where the start of the next line should be. chars_left does
265 not include the trailing '\0'. */
266 #define MAX_LINE_LEN 75
267 /*#define MAX_LINE_LEN 120*/
270 get_next_line_start (char *line_start, size_t chars_left)
274 /* If we have 74 chars or less left, we can output all of them.
275 we return a pointer to the '\0' at the end of the string. */
276 if (chars_left < MAX_LINE_LEN) {
277 return line_start + chars_left;
280 /* Now we jump to the last possible character of the line, and step back
281 trying to find a ';' ':' or ' '. If we find one, we return the character
283 pos = line_start + MAX_LINE_LEN - 2;
284 while (pos > line_start) {
285 if (*pos == ';' || *pos == ':' || *pos == ' ') {
290 /* Now try to split on a UTF-8 boundary defined as a 7-bit
291 value or as a byte with the two high-most bits set:
292 11xxxxxx. See http://czyborra.com/utf/ */
294 pos = line_start + MAX_LINE_LEN - 1;
295 while (pos > line_start) {
297 if ((*pos & 128) == 0)
300 /* utf8 escape byte */
301 if ((*pos & 192) == 192)
307 /* Give up, just break at 74 chars (the 75th char is the space at
308 the start of the line). */
310 return line_start + MAX_LINE_LEN - 1;
314 /** This splits the property into lines less than 75 octects long (as
315 * specified in RFC2445). It tries to split after a ';' if it can.
316 * It returns a tmp buffer. NOTE: I'm not sure if it matters if we
317 * split a line in the middle of a UTF-8 character. It probably won't
318 * look nice in a text editor.
321 fold_property_line (char *text)
323 size_t buf_size, len, chars_left;
324 char *buf, *buf_ptr, *line_start, *next_line_start;
328 /* Start with a buffer twice the size of our property line, so we almost
329 certainly won't overflow it. */
332 buf = icalmemory_new_buffer (buf_size);
335 /* Step through the text, finding each line to add to the output. */
343 /* This returns the first character for the next line. */
344 next_line_start = get_next_line_start (line_start, chars_left);
346 /* If this isn't the first line, we need to output a newline and space
349 icalmemory_append_string (&buf, &buf_ptr, &buf_size, "\r\n ");
353 /* This adds the line to our tmp buffer. We temporarily place a '\0'
354 in text, so we can copy the line in one go. */
355 ch = *next_line_start;
356 *next_line_start = '\0';
357 icalmemory_append_string (&buf, &buf_ptr, &buf_size, line_start);
358 *next_line_start = ch;
360 /* Now we move on to the next line. */
361 chars_left -= (next_line_start - line_start);
362 line_start = next_line_start;
369 /* Determine what VALUE parameter to include. The VALUE parameters
370 are ignored in the normal parameter printing ( the block after
371 this one, so we need to do it here */
373 icalproperty_get_value_kind(icalproperty *prop)
375 const char* kind_string = 0;
377 icalparameter *orig_val_param
378 = icalproperty_get_first_parameter(prop,ICAL_VALUE_PARAMETER);
380 icalvalue *value = icalproperty_get_value(prop);
382 icalvalue_kind orig_kind = ICAL_NO_VALUE;
384 icalvalue_kind this_kind = ICAL_NO_VALUE;
386 icalvalue_kind default_kind
387 = icalproperty_kind_to_value_kind(prop->kind);
390 orig_kind = icalparameter_value_to_value_kind( icalparameter_get_value(orig_val_param) );
394 this_kind = icalvalue_isa(value);
397 if ( orig_kind != ICAL_NO_VALUE ) {
398 kind_string = icalvalue_kind_to_string( orig_kind );
399 } else if(this_kind == default_kind &&
400 orig_kind != ICAL_NO_VALUE){
401 /* The kind is the default, so it does not need to be
402 included, but do it anyway, since it was explicit in
403 the property. But, use the default, not the one
404 specified in the property */
406 kind_string = icalvalue_kind_to_string(default_kind);
408 } else if (this_kind != default_kind && this_kind != ICAL_NO_VALUE){
409 /* Not the default, so it must be specified */
410 kind_string = icalvalue_kind_to_string(this_kind);
412 /* Don'tinclude the VALUE parameter at all */
419 icalproperty_as_ical_string (icalproperty* prop)
422 buf = icalproperty_as_ical_string_r(prop);
423 icalmemory_add_tmp_buffer(buf);
429 icalproperty_as_ical_string_r(icalproperty* prop)
431 icalparameter *param;
433 /* Create new buffer that we can append names, parameters and a
434 * value to, and reallocate as needed.
437 const char* property_name = 0;
438 size_t buf_size = 1024;
443 const char* kind_string = 0;
444 const char newline[] = "\r\n";
447 icalerror_check_arg_rz( (prop!=0),"prop");
449 buf = icalmemory_new_buffer(buf_size);
452 /* Append property name */
454 if (prop->kind == ICAL_X_PROPERTY && prop->x_name != 0){
455 property_name = prop->x_name;
457 property_name = icalproperty_kind_to_string(prop->kind);
460 if (property_name == 0 ) {
461 icalerror_warn("Got a property of an unknown kind.");
462 icalmemory_free_buffer(buf);
467 icalmemory_append_string(&buf, &buf_ptr, &buf_size, property_name);
469 kind_string = icalproperty_get_value_kind(prop);
471 icalmemory_append_string(&buf, &buf_ptr, &buf_size, ";VALUE=");
472 icalmemory_append_string(&buf, &buf_ptr, &buf_size, kind_string);
475 /* Append parameters */
476 for(param = icalproperty_get_first_parameter(prop,ICAL_ANY_PARAMETER);
478 param = icalproperty_get_next_parameter(prop,ICAL_ANY_PARAMETER)) {
480 icalparameter_kind kind = icalparameter_isa(param);
481 kind_string = icalparameter_as_ical_string_r(param);
483 if (kind_string == 0 ) {
484 icalerror_warn("Got a parameter of unknown kind for the following property");
486 icalerror_warn((property_name) ? property_name : "(NULL)");
490 if (kind==ICAL_VALUE_PARAMETER) {
491 free ((char *) kind_string);
495 icalmemory_append_string(&buf, &buf_ptr, &buf_size, ";");
496 icalmemory_append_string(&buf, &buf_ptr, &buf_size, kind_string);
497 free((char *)kind_string);
502 icalmemory_append_string(&buf, &buf_ptr, &buf_size, ":");
504 value = icalproperty_get_value(prop);
507 char *str = icalvalue_as_ical_string_r(value);
509 icalmemory_append_string(&buf, &buf_ptr, &buf_size, str);
510 #if ICAL_ALLOW_EMPTY_PROPERTIES == 0
512 icalmemory_append_string(&buf, &buf_ptr, &buf_size,"ERROR: No Value");
516 #if ICAL_ALLOW_EMPTY_PROPERTIES == 0
517 icalmemory_append_string(&buf, &buf_ptr, &buf_size,"ERROR: No Value");
521 icalmemory_append_string(&buf, &buf_ptr, &buf_size, newline);
523 /* We now use a function to fold the line properly every 75 characters.
524 That function also adds the newline for us. */
525 out_buf = fold_property_line (buf);
527 icalmemory_free_buffer(buf);
535 icalproperty_isa (icalproperty* p)
541 return ICAL_NO_PROPERTY;
545 icalproperty_isa_property (void* property)
547 icalproperty *impl = (icalproperty *) property;
549 icalerror_check_arg_rz( (property!=0), "property");
550 if (strcmp(impl->id,"prop") == 0) {
559 icalproperty_add_parameter (icalproperty* p,icalparameter* parameter)
561 icalerror_check_arg_rv( (p!=0),"prop");
562 icalerror_check_arg_rv( (parameter!=0),"parameter");
564 pvl_push(p->parameters, parameter);
569 icalproperty_set_parameter (icalproperty* prop,icalparameter* parameter)
571 icalparameter_kind kind;
573 icalerror_check_arg_rv( (prop!=0),"prop");
574 icalerror_check_arg_rv( (parameter!=0),"parameter");
576 kind = icalparameter_isa(parameter);
577 if (kind == ICAL_X_PARAMETER) {
578 icalproperty_remove_parameter_by_name(prop,
579 icalparameter_get_xname(parameter));
580 } else if (kind == ICAL_IANA_PARAMETER) {
581 icalproperty_remove_parameter_by_name(prop,
582 icalparameter_get_iana_name(parameter));
585 icalproperty_remove_parameter_by_kind(prop,kind);
587 icalproperty_add_parameter(prop,parameter);
590 void icalproperty_set_parameter_from_string(icalproperty* prop,
591 const char* name, const char* value)
594 icalparameter_kind kind;
595 icalparameter *param;
597 icalerror_check_arg_rv( (prop!=0),"prop");
598 icalerror_check_arg_rv( (name!=0),"name");
599 icalerror_check_arg_rv( (value!=0),"value");
601 kind = icalparameter_string_to_kind(name);
603 if(kind == ICAL_NO_PARAMETER){
604 icalerror_set_errno(ICAL_BADARG_ERROR);
608 param = icalparameter_new_from_value_string(kind,value);
611 icalerror_set_errno(ICAL_BADARG_ERROR);
615 if (kind == ICAL_X_PARAMETER) {
616 icalparameter_set_xname(param, name);
617 } else if (kind == ICAL_IANA_PARAMETER) {
618 icalparameter_set_iana_name(param, name);
621 icalproperty_set_parameter(prop,param);
625 const char* icalproperty_get_parameter_as_string(icalproperty* prop,
629 buf = icalproperty_get_parameter_as_string_r(prop, name);
630 icalmemory_add_tmp_buffer(buf);
635 char* icalproperty_get_parameter_as_string_r(icalproperty* prop,
638 icalparameter_kind kind;
639 icalparameter *param;
645 icalerror_check_arg_rz( (prop!=0),"prop");
646 icalerror_check_arg_rz( (name!=0),"name");
648 kind = icalparameter_string_to_kind(name);
650 if(kind == ICAL_NO_PARAMETER){
651 /* icalenum_string_to_parameter_kind will set icalerrno */
655 for(param = icalproperty_get_first_parameter(prop,kind);
657 param = icalproperty_get_next_parameter(prop,kind)) {
659 if (kind == ICAL_X_PARAMETER) {
660 if (strcmp(icalparameter_get_xname(param),name)==0) {
663 } else if (kind == ICAL_IANA_PARAMETER) {
664 if (strcmp(icalparameter_get_iana_name(param),name)==0) {
678 str = icalparameter_as_ical_string_r(param);
683 icalerror_set_errno(ICAL_INTERNAL_ERROR);
688 /* Strip the property name and the equal sign */
689 pv = icalmemory_strdup(t+1);
692 /* Is the string quoted? */
693 pvql = strchr(pv, '"');
695 return(pv); /* No quotes? Return it immediately. */
698 /* Strip everything up to the first quote */
699 str = icalmemory_strdup(pvql+1);
702 /* Search for the end quote */
703 pvqr = strrchr(str, '"');
705 icalerror_set_errno(ICAL_INTERNAL_ERROR);
714 /** @see icalproperty_remove_parameter_by_kind()
716 * @deprecated Please use icalproperty_remove_parameter_by_kind()
721 icalproperty_remove_parameter(icalproperty* prop, icalparameter_kind kind)
723 icalproperty_remove_parameter_by_kind(prop, kind);
727 /** @brief Remove all parameters with the specified kind.
729 * @param prop A valid icalproperty.
730 * @param kind The kind to remove (ex. ICAL_TZID_PARAMETER)
732 * See icalproperty_remove_parameter_by_name() and
733 * icalproperty_remove_parameter_by_ref() for alternate ways of
734 * removing parameters
738 icalproperty_remove_parameter_by_kind(icalproperty* prop, icalparameter_kind kind)
742 icalerror_check_arg_rv((prop!=0),"prop");
744 for(p=pvl_head(prop->parameters);p != 0; p = pvl_next(p)){
745 icalparameter* param = (icalparameter *)pvl_data (p);
746 if (icalparameter_isa(param) == kind) {
747 pvl_remove (prop->parameters, p);
748 icalparameter_free(param);
755 /** @brief Remove all parameters with the specified name.
757 * @param prop A valid icalproperty.
758 * @param name The name of the parameter to remove
760 * This function removes parameters with the given name. The name
761 * corresponds to either a built-in name (TZID, etc.) or the name of
762 * an extended parameter (X-FOO)
764 * See icalproperty_remove_parameter_by_kind() and
765 * icalproperty_remove_parameter_by_ref() for alternate ways of removing
771 icalproperty_remove_parameter_by_name(icalproperty* prop, const char *name)
775 icalerror_check_arg_rv((prop!=0),"prop");
777 for(p=pvl_head(prop->parameters);p != 0; p = pvl_next(p)){
778 icalparameter* param = (icalparameter *)pvl_data (p);
779 const char * kind_string;
781 if (icalparameter_isa(param) == ICAL_X_PARAMETER)
782 kind_string = icalparameter_get_xname(param);
783 else if (icalparameter_isa(param) == ICAL_IANA_PARAMETER)
784 kind_string = icalparameter_get_iana_name(param);
786 kind_string = icalparameter_kind_to_string(icalparameter_isa(param));
791 if (0 == strcmp(kind_string, name)) {
792 pvl_remove (prop->parameters, p);
793 icalparameter_free(param);
800 /** @brief Remove the specified parameter reference from the property.
802 * @param prop A valid icalproperty.
803 * @param parameter A reference to a specific icalparameter.
805 * This function removes the specified parameter reference from the
810 icalproperty_remove_parameter_by_ref(icalproperty* prop, icalparameter* parameter)
814 icalerror_check_arg_rv((prop!=0),"prop");
815 icalerror_check_arg_rv((parameter!=0),"parameter");
817 for (p=pvl_head(prop->parameters);p != 0; p = pvl_next(p)) {
818 icalparameter* p_param = (icalparameter *)pvl_data (p);
820 if (icalparameter_has_same_name(parameter, p_param)) {
821 pvl_remove (prop->parameters, p);
822 icalparameter_free(p_param);
830 icalproperty_count_parameters (const icalproperty* prop)
833 return pvl_count(prop->parameters);
836 icalerror_set_errno(ICAL_USAGE_ERROR);
842 icalproperty_get_first_parameter(icalproperty* p, icalparameter_kind kind)
844 icalerror_check_arg_rz( (p!=0),"prop");
846 p->parameter_iterator = pvl_head(p->parameters);
848 if (p->parameter_iterator == 0) {
852 for( p->parameter_iterator = pvl_head(p->parameters);
853 p->parameter_iterator !=0;
854 p->parameter_iterator = pvl_next(p->parameter_iterator)){
856 icalparameter *param = (icalparameter*)pvl_data(p->parameter_iterator);
858 if(icalparameter_isa(param) == kind || kind == ICAL_ANY_PARAMETER){
868 icalproperty_get_next_parameter (icalproperty* p, icalparameter_kind kind)
870 icalerror_check_arg_rz( (p!=0),"prop");
872 if (p->parameter_iterator == 0) {
876 for( p->parameter_iterator = pvl_next(p->parameter_iterator);
877 p->parameter_iterator !=0;
878 p->parameter_iterator = pvl_next(p->parameter_iterator)){
880 icalparameter *param = (icalparameter*)pvl_data(p->parameter_iterator);
882 if(icalparameter_isa(param) == kind || kind == ICAL_ANY_PARAMETER){
892 icalproperty_set_value (icalproperty* p, icalvalue* value)
894 icalerror_check_arg_rv((p !=0),"prop");
895 icalerror_check_arg_rv((value !=0),"value");
898 icalvalue_set_parent(p->value,0);
899 icalvalue_free(p->value);
905 icalvalue_set_parent(value,p);
909 void icalproperty_set_value_from_string(icalproperty* prop,const char* str,
912 icalvalue *oval,*nval;
913 icalvalue_kind kind = ICAL_NO_VALUE;
915 icalerror_check_arg_rv( (prop!=0),"prop");
916 icalerror_check_arg_rv( (str!=0),"str");
917 icalerror_check_arg_rv( (type!=0),"type");
919 if(strcmp(type,"NO")==0){
920 /* Get the type from the value the property already has, if it exists */
921 oval = icalproperty_get_value(prop);
923 /* Use the existing value kind */
924 kind = icalvalue_isa(oval);
926 /* Use the default kind for the property */
927 kind = icalproperty_kind_to_value_kind(icalproperty_isa(prop));
930 /* Use the given kind string */
931 kind = icalvalue_string_to_kind(type);
934 if(kind == ICAL_NO_VALUE){
935 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
939 icalerror_clear_errno();
940 nval = icalvalue_new_from_string(kind, str);
943 /* icalvalue_new_from_string sets errno */
944 assert(icalerrno != ICAL_NO_ERROR);
948 icalproperty_set_value(prop,nval);
954 icalproperty_get_value(const icalproperty* prop)
956 icalerror_check_arg_rz( (prop!=0),"prop");
961 const char* icalproperty_get_value_as_string(const icalproperty* prop)
964 buf = icalproperty_get_value_as_string_r(prop);
965 icalmemory_add_tmp_buffer(buf);
970 char* icalproperty_get_value_as_string_r(const icalproperty* prop)
974 icalerror_check_arg_rz( (prop!=0),"prop");
978 return icalvalue_as_ical_string_r(value);
982 void icalproperty_set_x_name(icalproperty* prop, const char* name)
984 icalerror_check_arg_rv( (name!=0),"name");
985 icalerror_check_arg_rv( (prop!=0),"prop");
987 if (prop->x_name != 0) {
991 prop->x_name = icalmemory_strdup(name);
993 if(prop->x_name == 0){
994 icalerror_set_errno(ICAL_NEWFAILED_ERROR);
999 const char* icalproperty_get_x_name(icalproperty* prop){
1000 icalerror_check_arg_rz( (prop!=0),"prop");
1002 return prop->x_name;
1005 const char* icalproperty_get_property_name(const icalproperty* prop)
1008 buf = icalproperty_get_property_name_r(prop);
1009 icalmemory_add_tmp_buffer(buf);
1014 char* icalproperty_get_property_name_r(const icalproperty* prop)
1017 const char* property_name = 0;
1018 size_t buf_size = 256;
1022 icalerror_check_arg_rz( (prop!=0),"prop");
1024 buf = icalmemory_new_buffer(buf_size);
1027 if (prop->kind == ICAL_X_PROPERTY && prop->x_name != 0){
1028 property_name = prop->x_name;
1030 property_name = icalproperty_kind_to_string(prop->kind);
1033 if (property_name == 0 ) {
1034 icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
1035 icalmemory_free_buffer(buf);
1039 /* _append_string will automatically grow the buffer if
1040 property_name is longer than the initial buffer size */
1041 icalmemory_append_string(&buf, &buf_ptr, &buf_size, property_name);
1050 void icalproperty_set_parent(icalproperty* property,
1051 icalcomponent* component)
1053 icalerror_check_arg_rv( (property!=0),"property");
1055 property->parent = component;
1058 icalcomponent* icalproperty_get_parent(const icalproperty* property)
1060 icalerror_check_arg_rz( (property!=0),"property");
1062 return property->parent;