$darkmode
Libical API Documentation 4.0 STABLE VERSION Visit the v3.0 documentation
vcardproperty.c
Go to the documentation of this file.
1 /*======================================================================
2  FILE: vcardproperty.c
3  CREATOR: Ken Murchison 24 Aug 2022
4 
5  SPDX-FileCopyrightText: 2022, Fastmail Pty. Ltd. (https://fastmail.com)
6  SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
7  ======================================================================*/
8 
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17 
18 #include "vcardproperty_p.h"
19 #include "vcardcomponent.h"
20 #include "vcardparser.h"
21 #include "vcardvalue.h"
22 #include "icalerror_p.h"
23 #include "icalmemory.h"
24 #include "icalproperty.h"
25 #include "icalpvl_p.h"
26 #include "icaltypes_p.h"
27 
28 #include <stdlib.h>
29 
30 struct vcardproperty_impl {
31  icalstructuretype id;
32  vcardproperty_kind kind;
33  char *x_name;
34  char *group;
35  icalpvl_list parameters;
36  icalpvl_elem parameter_iterator;
37  vcardvalue *value;
38  vcardcomponent *parent;
39 };
40 
41 LIBICAL_VCARD_EXPORT struct vcardproperty_impl *vcardproperty_new_impl(vcardproperty_kind kind)
42 {
43  vcardproperty *prop;
44 
45  if (!vcardproperty_kind_is_valid(kind)) {
46  return NULL;
47  }
48 
49  if ((prop = (vcardproperty *)icalmemory_new_buffer(sizeof(vcardproperty))) == 0) {
51  return 0;
52  }
53 
54  memset(prop, 0, sizeof(vcardproperty));
55 
56  prop->id = ICAL_STRUCTURE_TYPE_PROPERTY;
57  prop->kind = kind;
58  prop->parameters = icalpvl_newlist();
59 
60  return prop;
61 }
62 
63 void vcardproperty_add_parameters(vcardproperty *prop, va_list args)
64 {
65  void *vp;
66 
67  while ((vp = va_arg(args, void *)) != 0) {
68  if (vcardvalue_isa_value(vp) != 0) {
69  } else if (vcardparameter_isa_parameter(vp) != 0) {
70  vcardproperty_add_parameter((vcardproperty *)prop,
71  (vcardparameter *)vp);
72  } else {
74  }
75  }
76 }
77 
78 vcardproperty *vcardproperty_new(vcardproperty_kind kind)
79 {
80  if (kind == VCARD_NO_PROPERTY) {
81  return 0;
82  }
83 
84  return (vcardproperty *)vcardproperty_new_impl(kind);
85 }
86 
87 vcardproperty *vcardproperty_clone(const vcardproperty *old)
88 {
89  vcardproperty *clone;
90  icalpvl_elem p;
91 
92  icalerror_check_arg_rz((old != 0), "old");
93  clone = vcardproperty_new_impl(old->kind);
94  icalerror_check_arg_rz((clone != 0), "clone");
95 
96  if (old->value != 0) {
97  clone->value = vcardvalue_clone(old->value);
98  }
99 
100  if (old->x_name != 0) {
101  clone->x_name = icalmemory_strdup(old->x_name);
102 
103  if (clone->x_name == 0) {
104  vcardproperty_free(clone);
106  return 0;
107  }
108  }
109 
110  for (p = icalpvl_head(old->parameters); p != 0; p = icalpvl_next(p)) {
111  vcardparameter *param = vcardparameter_clone(icalpvl_data(p));
112 
113  if (param == 0) {
114  vcardproperty_free(clone);
116  return 0;
117  }
118 
119  icalpvl_push(clone->parameters, param);
120  }
121 
122  return clone;
123 }
124 
125 vcardproperty *vcardproperty_new_from_string(const char *str)
126 {
127  size_t buf_size = 1024;
128  char *buf;
129  char *buf_ptr;
130  vcardproperty *prop;
131  vcardcomponent *comp;
132  int errors = 0;
133 
134  icalerror_check_arg_rz((str != 0), "str");
135 
136  buf = icalmemory_new_buffer(buf_size);
137  buf_ptr = buf;
138 
139  /* Is this a HACK or a crafty reuse of code? */
140 
141  icalmemory_append_string(&buf, &buf_ptr, &buf_size, "BEGIN:VCARD\r\n");
142  icalmemory_append_string(&buf, &buf_ptr, &buf_size, str);
143  icalmemory_append_string(&buf, &buf_ptr, &buf_size, "\r\n");
144  icalmemory_append_string(&buf, &buf_ptr, &buf_size, "END:VCARD\r\n");
145 
146  comp = vcardparser_parse_string(buf);
147 
148  if (comp == 0) {
151  return 0;
152  }
153 
154  errors = vcardcomponent_count_errors(comp);
155 
156  prop = vcardcomponent_get_first_property(comp, VCARD_ANY_PROPERTY);
157 
158  vcardcomponent_remove_property(comp, prop);
159 
160  vcardcomponent_free(comp);
162 
163  if (errors > 0) {
164  vcardproperty_free(prop);
165  return 0;
166  } else {
167  return prop;
168  }
169 }
170 
171 void vcardproperty_free(vcardproperty *p)
172 {
173  vcardparameter *param;
174 
175  icalerror_check_arg_rv((p != 0), "prop");
176 
177  if (p->parent != 0) {
178  return;
179  }
180 
181  if (p->value != 0) {
182  vcardvalue_set_parent(p->value, 0);
183  vcardvalue_free(p->value);
184  }
185 
186  while ((param = icalpvl_pop(p->parameters)) != 0) {
187  vcardparameter_free(param);
188  }
189 
190  icalpvl_free(p->parameters);
191 
192  if (p->x_name != 0) {
193  icalmemory_free_buffer(p->x_name);
194  }
195 
196  if (p->group != 0) {
197  icalmemory_free_buffer(p->group);
198  }
199 
200  p->kind = VCARD_NO_PROPERTY;
201  p->parameters = 0;
202  p->parameter_iterator = 0;
203  p->value = 0;
204  p->x_name = 0;
205  p->id = ICAL_STRUCTURE_TYPE_PROPERTY_EMPTY;
206 
208 }
209 
210 /* This returns where the start of the next line should be. chars_left does
211  not include the trailing '\0'. */
212 static const size_t MAX_LINE_LEN = 75;
213 
214 static char *get_next_line_start(char *line_start, size_t chars_left)
215 {
216  char *pos;
217 
218  /* If we have 74 chars or less left, we can output all of them.
219  we return a pointer to the '\0' at the end of the string. */
220  if (chars_left < MAX_LINE_LEN) {
221  return line_start + chars_left;
222  }
223 
224  /* Now we jump to the last possible character of the line, and step back
225  trying to find a ';' ':' or ' '. If we find one, we return the character
226  after it. */
227  pos = line_start + MAX_LINE_LEN - 2;
228  while (pos > line_start) {
229  if (*pos == ';' || *pos == ':' || *pos == ' ') {
230  return pos + 1;
231  }
232  pos--;
233  }
234  /* Now try to split on a UTF-8 boundary defined as a 7-bit
235  value or as a byte with the two high-most bits set:
236  11xxxxxx. See https://czyborra.com/utf/ */
237 
238  pos = line_start + MAX_LINE_LEN - 1;
239  while (pos > line_start) {
240  /* plain ascii */
241  if ((*pos & 128) == 0) {
242  return pos;
243  }
244 
245  /* utf8 escape byte */
246  if ((*pos & 192) == 192) {
247  return pos;
248  }
249 
250  pos--;
251  }
252 
253  /* Give up, just break at 74 chars (the 75th char is the space at
254  the start of the line). */
255 
256  return line_start + MAX_LINE_LEN - 1;
257 }
258 
265 static char *fold_property_line(char *text)
266 {
267  size_t buf_size, len;
268  ssize_t chars_left;
269  char *buf, *buf_ptr, *line_start, *next_line_start;
270  int first_line;
271 
272  /* Start with a buffer twice the size of our property line, so we almost
273  certainly won't overflow it. */
274  len = strlen(text);
275  buf_size = len * 2;
276  buf = icalmemory_new_buffer(buf_size);
277  buf_ptr = buf;
278 
279  /* Step through the text, finding each line to add to the output. */
280  line_start = text;
281  chars_left = (ssize_t)len;
282  first_line = 1;
283  for (;;) {
284  if (chars_left <= 0) {
285  break;
286  }
287 
288  /* This returns the first character for the next line. */
289  next_line_start = get_next_line_start(line_start, (size_t)chars_left);
290 
291  /* If this isn't the first line, we need to output a newline and space
292  first. */
293  if (!first_line) {
294  icalmemory_append_string(&buf, &buf_ptr, &buf_size, "\r\n ");
295  }
296  first_line = 0;
297 
298  /* This adds the line to our tmp buffer. We temporarily place a '\0'
299  in text, so we can copy the line in one go. */
300  char ch = *next_line_start;
301  *next_line_start = '\0';
302  icalmemory_append_string(&buf, &buf_ptr, &buf_size, line_start);
303  *next_line_start = ch;
304 
305  /* Now we move on to the next line. */
306  chars_left -= (next_line_start - line_start);
307  line_start = next_line_start;
308  }
309 
310  icalmemory_append_string(&buf, &buf_ptr, &buf_size, "\r\n");
311 
312  return buf;
313 }
314 
315 /* Determine what VALUE parameter to include. The VALUE parameters
316  are ignored in the normal parameter printing ( the block after
317  this one, so we need to do it here */
318 static const char *vcardproperty_get_value_kind(vcardproperty *prop,
319  const vcardvalue *value)
320 {
321  const char *kind_string = NULL;
322  if (!prop) {
323  return kind_string;
324  }
325  vcardvalue_kind kind = VCARD_NO_VALUE;
326  vcardparameter *val_param =
327  vcardproperty_get_first_parameter(prop, VCARD_VALUE_PARAMETER);
328  vcardproperty_version version = vcardcomponent_get_version(prop->parent);
329 
330  if (version == VCARD_VERSION_NONE) {
331  version = VCARD_VERSION_30;
332  }
333 
334  if (val_param) {
335  kind = vcardparameter_value_to_value_kind(vcardparameter_get_value(val_param));
336  }
337 
338  if (kind == VCARD_NO_VALUE && vcardproperty_isa(prop) != VCARD_X_PROPERTY) {
339  kind = vcardvalue_isa(value);
340  }
341 
342  /* Try to omit VERSION-dependent default VALUE kinds */
343  switch (vcardproperty_isa(prop)) {
344  case VCARD_GEO_PROPERTY:
345  /* v3 default: VALUE=STRUCTURED
346  * v4 default: VALUE=URL
347  */
348  if (kind != VCARD_X_VALUE) {
349  kind = VCARD_NO_VALUE;
350  }
351  break;
352 
353  case VCARD_KEY_PROPERTY:
354  case VCARD_LOGO_PROPERTY:
355  case VCARD_PHOTO_PROPERTY:
356  case VCARD_SOUND_PROPERTY:
357  /* v3 default: VALUE=BINARY (ENCODING=b parameter)
358  * v4 default: VALUE=URL
359  */
360  if (version == VCARD_VERSION_40 ||
361  vcardproperty_get_first_parameter(prop, VCARD_ENCODING_PARAMETER)) {
362  kind = VCARD_NO_VALUE;
363  }
364  break;
365 
366  case VCARD_ANNIVERSARY_PROPERTY:
367  case VCARD_BDAY_PROPERTY:
368  case VCARD_DEATHDATE_PROPERTY:
369  /* TIME is v4 specific.
370  * All other types are self-evident.
371  */
372  if (version == VCARD_VERSION_40 &&
373  (kind == VCARD_TIME_VALUE || kind == VCARD_DATEANDORTIME_VALUE)) {
374  kind = VCARD_NO_VALUE;
375  } else if (kind == VCARD_DATE_VALUE || kind == VCARD_DATETIME_VALUE) {
376  kind = VCARD_NO_VALUE;
377  }
378  break;
379 
380  case VCARD_TZ_PROPERTY:
381  /* v3 default: VALUE=UTCOFFSET
382  * v4 default: VALUE=TEXT
383  */
384  if (version == VCARD_VERSION_40) {
385  if (kind == VCARD_TEXT_VALUE) {
386  kind = VCARD_NO_VALUE;
387  }
388  } else if (kind == VCARD_UTCOFFSET_VALUE) {
389  kind = VCARD_NO_VALUE;
390  }
391  break;
392 
393  case VCARD_UID_PROPERTY:
394  /* v3 default: VALUE=TEXT
395  * v4 default: VALUE=URI
396  */
397  if (version == VCARD_VERSION_40) {
398  if (kind == VCARD_URI_VALUE) {
399  kind = VCARD_NO_VALUE;
400  }
401  } else {
402  kind = VCARD_NO_VALUE;
403  }
404  break;
405 
406  default:
407  break;
408  }
409 
410  if (kind != VCARD_NO_VALUE &&
411  !vcardproperty_value_kind_is_default(prop->kind, kind)) {
412  /* Not the default, so it must be specified */
413  kind_string = vcardvalue_kind_to_string(kind);
414  } else {
415  /* Don't include the VALUE parameter at all */
416  }
417 
418  return kind_string;
419 }
420 
421 const char *vcardproperty_as_vcard_string(vcardproperty *prop)
422 {
423  char *buf;
424 
425  buf = vcardproperty_as_vcard_string_r(prop);
427  return buf;
428 }
429 
430 char *vcardproperty_as_vcard_string_r(vcardproperty *prop)
431 {
432  vcardparameter *param;
433 
434  /* Create new buffer that we can append names, parameters and a
435  * value to, and reallocate as needed.
436  */
437 
438  const char *property_name = 0;
439  size_t buf_size = 1024;
440  char *buf;
441  char *buf_ptr;
442  const vcardvalue *value;
443  char *out_buf;
444  const char *kind_string = 0;
445 
446  icalerror_check_arg_rz((prop != 0), "prop");
447 
448  buf = icalmemory_new_buffer(buf_size);
449  buf_ptr = buf;
450 
451  /* Append property name */
452 
453  if (prop->kind == VCARD_X_PROPERTY && prop->x_name != 0) {
454  property_name = prop->x_name;
455  } else {
456  property_name = vcardproperty_kind_to_string(prop->kind);
457  }
458 
459  if (property_name == 0) {
460  icalerror_warn("Got a property of an unknown kind.");
462  return 0;
463  }
464 
465  if (prop->group != 0) {
466  icalmemory_append_string(&buf, &buf_ptr, &buf_size, prop->group);
467  icalmemory_append_string(&buf, &buf_ptr, &buf_size, ".");
468  }
469 
470  icalmemory_append_string(&buf, &buf_ptr, &buf_size, property_name);
471 
472  value = vcardproperty_get_value(prop);
473  kind_string = vcardproperty_get_value_kind(prop, value);
474  if (kind_string != 0) {
475  icalmemory_append_string(&buf, &buf_ptr, &buf_size, ";VALUE=");
476  icalmemory_append_string(&buf, &buf_ptr, &buf_size, kind_string);
477  }
478 
479  /* Append parameters */
480  for (param = vcardproperty_get_first_parameter(prop, VCARD_ANY_PARAMETER);
481  param != 0; param = vcardproperty_get_next_parameter(prop, VCARD_ANY_PARAMETER)) {
482  vcardparameter_kind kind = vcardparameter_isa(param);
483 
484  kind_string = vcardparameter_as_vcard_string_r(param);
485 
486  if (kind_string == 0) {
487  icalerror_warn("Got a parameter of unknown kind for the following property");
488 
489  icalerror_warn((property_name) ? property_name : "(NULL)");
490  continue;
491  }
492 
493  if (kind == VCARD_VALUE_PARAMETER) {
494  icalmemory_free_buffer((char *)kind_string);
495  continue;
496  }
497 
498  icalmemory_append_string(&buf, &buf_ptr, &buf_size, ";");
499  icalmemory_append_string(&buf, &buf_ptr, &buf_size, kind_string);
500  icalmemory_free_buffer((char *)kind_string);
501  }
502 
503  /* Append value */
504 
505  icalmemory_append_string(&buf, &buf_ptr, &buf_size, ":");
506 
507  if (value != 0) {
508  char *str = vcardvalue_as_vcard_string_r(value);
509 
510  if (str != 0) {
511  icalmemory_append_string(&buf, &buf_ptr, &buf_size, str);
513  icalmemory_append_string(&buf, &buf_ptr, &buf_size, "ERROR: No Value");
514  }
517  icalmemory_append_string(&buf, &buf_ptr, &buf_size, "ERROR: No Value");
518  }
519 
520  /* We now use a function to fold the line properly every 75 characters.
521  That function also adds the newline for us. */
522  out_buf = fold_property_line(buf);
523 
525 
526  return out_buf;
527 }
528 
529 vcardproperty_kind vcardproperty_isa(const vcardproperty *p)
530 {
531  if (p != 0) {
532  return p->kind;
533  }
534 
535  return VCARD_NO_PROPERTY;
536 }
537 
538 bool vcardproperty_isa_property(void *property)
539 {
540  const vcardproperty *impl = (vcardproperty *)property;
541 
542  icalerror_check_arg_rz((property != 0), "property");
543  return (impl->id == ICAL_STRUCTURE_TYPE_PROPERTY);
544 }
545 
546 void vcardproperty_add_parameter(vcardproperty *p, vcardparameter *parameter)
547 {
548  icalerror_check_arg_rv((p != 0), "prop");
549  icalerror_check_arg_rv((parameter != 0), "parameter");
550 
551  icalpvl_push(p->parameters, parameter);
552 }
553 
554 void vcardproperty_set_parameter(vcardproperty *prop, vcardparameter *parameter)
555 {
556  vcardparameter_kind kind;
557 
558  icalerror_check_arg_rv((prop != 0), "prop");
559  icalerror_check_arg_rv((parameter != 0), "parameter");
560 
561  kind = vcardparameter_isa(parameter);
562  if (kind == VCARD_X_PARAMETER) {
564  } else if (kind == VCARD_IANA_PARAMETER) {
566  } else {
568  }
569 
570  vcardproperty_add_parameter(prop, parameter);
571 }
572 
573 void vcardproperty_set_parameter_from_string(vcardproperty *prop,
574  const char *name, const char *value)
575 {
576  vcardparameter_kind kind;
577  vcardparameter *param;
578 
579  icalerror_check_arg_rv((prop != 0), "prop");
580  icalerror_check_arg_rv((name != 0), "name");
581  icalerror_check_arg_rv((value != 0), "value");
582 
583  kind = vcardparameter_string_to_kind(name);
584 
585  if (kind == VCARD_NO_PARAMETER) {
587  return;
588  }
589 
590  param = vcardparameter_new_from_value_string(kind, value);
591 
592  if (param == 0) {
594  return;
595  }
596 
597  if (kind == VCARD_X_PARAMETER) {
598  vcardparameter_set_xname(param, name);
599  } else if (kind == VCARD_IANA_PARAMETER) {
600  vcardparameter_set_iana_name(param, name);
601  }
602 
603  vcardproperty_set_parameter(prop, param);
604 }
605 
606 const char *vcardproperty_get_parameter_as_string(vcardproperty *prop, const char *name)
607 {
608  char *buf;
609 
610  buf = vcardproperty_get_parameter_as_string_r(prop, name);
612  return buf;
613 }
614 
615 char *vcardproperty_get_parameter_as_string_r(vcardproperty *prop, const char *name)
616 {
617  vcardparameter_kind kind;
618  vcardparameter *param;
619  char *str;
620  char *pv, *t;
621  char *pvql;
622  char *pvqr;
623 
624  icalerror_check_arg_rz((prop != 0), "prop");
625  icalerror_check_arg_rz((name != 0), "name");
626 
627  kind = vcardparameter_string_to_kind(name);
628 
629  if (kind == VCARD_NO_PARAMETER) {
630  /* icalenum_string_to_parameter_kind will set icalerrno */
631  return 0;
632  }
633 
634  for (param = vcardproperty_get_first_parameter(prop, kind);
635  param != 0; param = vcardproperty_get_next_parameter(prop, kind)) {
636  if (kind == VCARD_X_PARAMETER) {
637  if (strcmp(vcardparameter_get_xname(param), name) == 0) {
638  break;
639  }
640  } else if (kind == VCARD_IANA_PARAMETER) {
641  if (strcmp(vcardparameter_get_iana_name(param), name) == 0) {
642  break;
643  }
644  } else {
645  break;
646  }
647  }
648 
649  if (param == 0) {
650  return 0;
651  }
652 
654 
655  t = strchr(str, '=');
656 
657  if (t == 0) {
660  return 0;
661  }
662 
663  /* Strip the property name and the equal sign */
664  pv = icalmemory_strdup(t + 1);
666 
667  /* Is the string quoted? */
668  pvql = strchr(pv, '"');
669  if (pvql == 0) {
670  return (pv); /* No quotes? Return it immediately. */
671  }
672 
673  /* Strip everything up to the first quote */
674  str = icalmemory_strdup(pvql + 1);
676 
677  /* Search for the end quote */
678  pvqr = strrchr(str, '"');
679  if (pvqr == 0) {
682  return 0;
683  }
684 
685  *pvqr = '\0';
686  return str;
687 }
688 
689 void vcardproperty_remove_parameter_by_kind(vcardproperty *prop, vcardparameter_kind kind)
690 {
691  icalpvl_elem p;
692 
693  icalerror_check_arg_rv((prop != 0), "prop");
694 
695  for (p = icalpvl_head(prop->parameters); p != 0; p = icalpvl_next(p)) {
696  vcardparameter *param = (vcardparameter *)icalpvl_data(p);
697 
698  if (vcardparameter_isa(param) == kind) {
699  (void)icalpvl_remove(prop->parameters, p);
700  vcardparameter_free(param);
701  break;
702  }
703  }
704 }
705 
706 void vcardproperty_remove_parameter_by_name(vcardproperty *prop, const char *name)
707 {
708  icalpvl_elem p;
709 
710  icalerror_check_arg_rv((prop != 0), "prop");
711 
712  for (p = icalpvl_head(prop->parameters); p != 0; p = icalpvl_next(p)) {
713  vcardparameter *param = (vcardparameter *)icalpvl_data(p);
714  const char *kind_string;
715 
716  if (vcardparameter_isa(param) == VCARD_X_PARAMETER) {
717  kind_string = vcardparameter_get_xname(param);
718  } else if (vcardparameter_isa(param) == VCARD_IANA_PARAMETER) {
719  kind_string = vcardparameter_get_iana_name(param);
720  } else {
722  }
723 
724  if (!kind_string) {
725  continue;
726  }
727 
728  if (0 == strcmp(kind_string, name)) {
729  (void)icalpvl_remove(prop->parameters, p);
730  vcardparameter_free(param);
731  break;
732  }
733  }
734 }
735 
736 void vcardproperty_remove_parameter_by_ref(vcardproperty *prop, vcardparameter *parameter)
737 {
738  icalpvl_elem p;
739 
740  icalerror_check_arg_rv((prop != 0), "prop");
741  icalerror_check_arg_rv((parameter != 0), "parameter");
742 
743  for (p = icalpvl_head(prop->parameters); p != 0; p = icalpvl_next(p)) {
744  vcardparameter *p_param = (vcardparameter *)icalpvl_data(p);
745 
746  if (parameter == p_param) {
747  (void)icalpvl_remove(prop->parameters, p);
748  vcardparameter_free(p_param);
749  break;
750  }
751  }
752 }
753 
754 int vcardproperty_count_parameters(const vcardproperty *prop)
755 {
756  if (prop != 0) {
757  return icalpvl_count(prop->parameters);
758  }
759 
761  return -1;
762 }
763 
764 vcardparameter *vcardproperty_get_first_parameter(vcardproperty *p, vcardparameter_kind kind)
765 {
766  icalerror_check_arg_rz((p != 0), "prop");
767 
768  p->parameter_iterator = icalpvl_head(p->parameters);
769 
770  if (p->parameter_iterator == 0) {
771  return 0;
772  }
773 
774  for (p->parameter_iterator = icalpvl_head(p->parameters);
775  p->parameter_iterator != 0; p->parameter_iterator = icalpvl_next(p->parameter_iterator)) {
776  vcardparameter *param = (vcardparameter *)icalpvl_data(p->parameter_iterator);
777 
778  if (vcardparameter_isa(param) == kind || kind == VCARD_ANY_PARAMETER) {
779  return param;
780  }
781  }
782 
783  return 0;
784 }
785 
786 vcardparameter *vcardproperty_get_next_parameter(vcardproperty *p, vcardparameter_kind kind)
787 {
788  icalerror_check_arg_rz((p != 0), "prop");
789 
790  if (p->parameter_iterator == 0) {
791  return 0;
792  }
793 
794  for (p->parameter_iterator = icalpvl_next(p->parameter_iterator);
795  p->parameter_iterator != 0; p->parameter_iterator = icalpvl_next(p->parameter_iterator)) {
796  vcardparameter *param = (vcardparameter *)icalpvl_data(p->parameter_iterator);
797 
798  if (vcardparameter_isa(param) == kind || kind == VCARD_ANY_PARAMETER) {
799  return param;
800  }
801  }
802 
803  return 0;
804 }
805 
806 void vcardproperty_set_value(vcardproperty *p, vcardvalue *value)
807 {
808  vcardvalue_kind kind;
809 
810  icalerror_check_arg_rv((p != 0), "prop");
811  icalerror_check_arg_rv((value != 0), "value");
812 
813  if (p->value != 0) {
814  vcardvalue_set_parent(p->value, 0);
815  vcardvalue_free(p->value);
816  p->value = 0;
817  }
818 
819  p->value = value;
820 
821  vcardvalue_set_parent(value, p);
822 
823  kind = vcardvalue_isa(value);
824 
825  if (kind == VCARD_DATE_VALUE || kind == VCARD_DATETIME_VALUE) {
826  vcardparameter *val_param;
827 
828  val_param = vcardproperty_get_first_parameter(p, VCARD_VALUE_PARAMETER);
829 
830  if (val_param &&
831  vcardparameter_value_to_value_kind(vcardparameter_get_value(val_param)) != kind) {
832  vcardproperty_remove_parameter_by_kind(p, VCARD_VALUE_PARAMETER);
833  }
834  }
835 }
836 
837 void vcardproperty_set_value_from_string(vcardproperty *prop, const char *str, const char *type)
838 {
839  vcardvalue *nval;
840  vcardvalue_kind kind = VCARD_NO_VALUE;
841 
842  icalerror_check_arg_rv((prop != 0), "prop");
843  icalerror_check_arg_rv((str != 0), "str");
844  icalerror_check_arg_rv((type != 0), "type");
845 
846  if (strcmp(type, "NO") == 0) {
847  /* Get the type from the value the property already has, if it exists */
848  const vcardvalue *oval = vcardproperty_get_value(prop);
849  if (oval != 0) {
850  /* Use the existing value kind */
851  kind = vcardvalue_isa(oval);
852  } else {
853  /* Use the default kind for the property */
854  kind = vcardproperty_kind_to_value_kind(vcardproperty_isa(prop));
855  }
856  } else {
857  /* Use the given kind string */
858  kind = vcardvalue_string_to_kind(type);
859  }
860 
861  if (kind == VCARD_NO_VALUE) {
863  return;
864  }
865 
867  nval = vcardvalue_new_from_string(kind, str);
868 
869  if (nval == 0) {
870  /* vcardvalue_new_from_string sets errno */
871  icalassert(icalerrno != ICAL_NO_ERROR);
872  return;
873  }
874 
875  vcardproperty_set_value(prop, nval);
876 }
877 
878 vcardvalue *vcardproperty_get_value(const vcardproperty *prop)
879 {
880  icalerror_check_arg_rz((prop != 0), "prop");
881 
882  return prop->value;
883 }
884 
885 const char *vcardproperty_get_value_as_string(const vcardproperty *prop)
886 {
887  char *buf;
888 
889  buf = vcardproperty_get_value_as_string_r(prop);
891  return buf;
892 }
893 
894 char *vcardproperty_get_value_as_string_r(const vcardproperty *prop)
895 {
896  const vcardvalue *value;
897 
898  icalerror_check_arg_rz((prop != 0), "prop");
899 
900  value = prop->value;
901 
902  return vcardvalue_as_vcard_string_r(value);
903 }
904 
905 void vcardproperty_set_x_name(vcardproperty *prop, const char *name)
906 {
907  icalerror_check_arg_rv((name != 0), "name");
908  icalerror_check_arg_rv((prop != 0), "prop");
909 
910  if (prop->x_name != 0) {
911  icalmemory_free_buffer(prop->x_name);
912  }
913 
914  prop->x_name = icalmemory_strdup(name);
915 
916  if (prop->x_name == 0) {
918  }
919 }
920 
921 const char *vcardproperty_get_x_name(const vcardproperty *prop)
922 {
923  icalerror_check_arg_rz((prop != 0), "prop");
924 
925  return prop->x_name;
926 }
927 
928 void vcardproperty_set_group(vcardproperty *prop, const char *group)
929 {
930  icalerror_check_arg_rv((group != 0), "group");
931  icalerror_check_arg_rv((prop != 0), "prop");
932 
933  if (prop->group != 0) {
934  icalmemory_free_buffer(prop->group);
935  }
936 
937  prop->group = icalmemory_strdup(group);
938 
939  if (prop->group == 0) {
941  }
942 }
943 
944 const char *vcardproperty_get_group(const vcardproperty *prop)
945 {
946  icalerror_check_arg_rz((prop != 0), "prop");
947 
948  return prop->group;
949 }
950 
951 const char *vcardproperty_get_property_name(const vcardproperty *prop)
952 {
953  char *buf;
954 
955  buf = vcardproperty_get_property_name_r(prop);
957  return buf;
958 }
959 
960 char *vcardproperty_get_property_name_r(const vcardproperty *prop)
961 {
962  const char *property_name = 0;
963  size_t buf_size = 256;
964  char *buf;
965  char *buf_ptr;
966 
967  icalerror_check_arg_rz((prop != 0), "prop");
968 
969  buf = icalmemory_new_buffer(buf_size);
970  buf_ptr = buf;
971 
972  if (prop->kind == VCARD_X_PROPERTY && prop->x_name != 0) {
973  property_name = prop->x_name;
974  } else {
975  property_name = vcardproperty_kind_to_string(prop->kind);
976  }
977 
978  if (property_name == 0) {
981  return 0;
982 
983  } else {
984  /* _append_string will automatically grow the buffer if
985  property_name is longer than the initial buffer size */
986  icalmemory_append_string(&buf, &buf_ptr, &buf_size, property_name);
987  }
988 
989  return buf;
990 }
991 
992 void vcardproperty_set_parent(vcardproperty *property, vcardcomponent *comp)
993 {
994  icalerror_check_arg_rv((property != 0), "property");
995 
996  property->parent = comp;
997 }
998 
999 vcardcomponent *vcardproperty_get_parent(const vcardproperty *property)
1000 {
1001  icalerror_check_arg_rz((property != 0), "property");
1002 
1003  return property->parent;
1004 }
1005 
1006 static int param_compare(void *a, void *b)
1007 {
1008  return strcmp(vcardparameter_as_vcard_string((vcardparameter *)a),
1009  vcardparameter_as_vcard_string((vcardparameter *)b));
1010 }
1011 
1012 void vcardproperty_normalize(vcardproperty *prop)
1013 {
1014  vcardproperty_kind prop_kind = vcardproperty_isa(prop);
1015  icalpvl_list sorted_params = icalpvl_newlist();
1016  vcardparameter *param;
1017 
1018  /* Normalize parameters into sorted list */
1019  while ((param = icalpvl_pop(prop->parameters)) != 0) {
1020  int remove = 0;
1021 
1022  /* Remove parameters having default values */
1023  switch (vcardparameter_isa(param)) {
1024  case VCARD_VALUE_PARAMETER:
1025  /* Skip VALUE parameters for default property value types */
1026  switch (prop_kind) {
1027  case VCARD_TEL_PROPERTY:
1028  case VCARD_TZ_PROPERTY:
1029  case VCARD_BIRTHPLACE_PROPERTY:
1030  case VCARD_DEATHPLACE_PROPERTY:
1031  if (vcardparameter_get_value(param) == VCARD_VALUE_TEXT) {
1032  remove = 1;
1033  }
1034  break;
1035 
1036  case VCARD_RELATED_PROPERTY:
1037  case VCARD_UID_PROPERTY:
1038  case VCARD_KEY_PROPERTY:
1039  if (vcardparameter_get_value(param) == VCARD_VALUE_URI) {
1040  remove = 1;
1041  }
1042  break;
1043 
1044  case VCARD_BDAY_PROPERTY:
1045  case VCARD_ANNIVERSARY_PROPERTY:
1046  case VCARD_DEATHDATE_PROPERTY:
1047  if (vcardparameter_get_value(param) == VCARD_VALUE_DATEANDORTIME) {
1048  remove = 1;
1049  }
1050  break;
1051 
1052  default:
1053  break;
1054  }
1055  break;
1056 
1057  case VCARD_CALSCALE_PARAMETER:
1058  if (vcardparameter_get_calscale(param) == VCARD_CALSCALE_GREGORIAN) {
1059  remove = 1;
1060  }
1061  break;
1062 
1063  case VCARD_PREF_PARAMETER:
1064  if (vcardparameter_get_pref(param) >= 100) {
1065  remove = 1;
1066  }
1067  break;
1068 
1069  case VCARD_TYPE_PARAMETER:
1070  if (prop_kind == VCARD_TEL_PROPERTY) {
1071  /* Is it just TYPE=VOICE ? */
1072  vcardenumarray_element voice = {VCARD_TYPE_VOICE, NULL};
1073 
1074  if (vcardenumarray_find(vcardparameter_get_type(param),
1075  &voice) == 0) {
1076  remove = 1;
1077  }
1078  }
1079 
1080  if (!remove) {
1081  vcardenumarray_sort(vcardparameter_get_type(param));
1082  }
1083  break;
1084 
1085  case VCARD_PID_PARAMETER:
1086  vcardstrarray_sort(vcardparameter_get_pid(param));
1087  break;
1088 
1089  case VCARD_SORTAS_PARAMETER:
1090  vcardstrarray_sort(vcardparameter_get_sortas(param));
1091  break;
1092 
1093  default:
1094  break;
1095  }
1096 
1097  if (remove) {
1098  vcardparameter_set_parent(param, 0); // MUST NOT have a parent to free
1099  vcardparameter_free(param);
1100  } else {
1101  icalpvl_insert_ordered(sorted_params, param_compare, param);
1102  }
1103  }
1104 
1105  icalpvl_free(prop->parameters);
1106  prop->parameters = sorted_params;
1107 
1108  switch (prop_kind) {
1109  case VCARD_CATEGORIES_PROPERTY:
1110  vcardstrarray_sort(vcardproperty_get_categories(prop));
1111  break;
1112 
1113  case VCARD_NICKNAME_PROPERTY:
1114  vcardstrarray_sort(vcardproperty_get_nickname(prop));
1115  break;
1116 
1117  default:
1118  break;
1119  }
1120 }
1121 
1122 void vcardproperty_add_type_parameter(vcardproperty *prop,
1123  vcardenumarray_element *type)
1124 {
1125  vcardenumarray *types;
1126  vcardparameter *param =
1127  vcardproperty_get_first_parameter(prop, VCARD_TYPE_PARAMETER);
1128 
1129  if (param) {
1130  types = vcardparameter_get_type(param);
1131  vcardenumarray_add(types, type);
1132  } else {
1133  param = vcardparameter_new(VCARD_TYPE_PARAMETER);
1134  types = vcardenumarray_new(0);
1135  vcardenumarray_add(types, type);
1136  vcardparameter_set_type(param, types);
1137  vcardproperty_add_parameter(prop, param);
1138  }
1139 }
Defines the data structure representing iCalendar properties.
Common memory management routines.
char * vcardparameter_as_vcard_string_r(vcardparameter *param)
Converts vcardparameter into an string representation according to RFC5445/RFC6868.
vcardparameter * vcardparameter_new(vcardparameter_kind kind)
Creates new vcardparameter object.
char * icalmemory_strdup(const char *s)
Creates a duplicate of a string.
Definition: icalmemory.c:242
void icalmemory_free_buffer(void *buf)
Releases a buffer.
Definition: icalmemory.c:355
void vcardproperty_remove_parameter_by_ref(vcardproperty *prop, vcardparameter *parameter)
Removes the specified parameter reference from the property.
const char * vcardparameter_kind_to_string(vcardparameter_kind kind)
Returns a string representing the given vcardparameter_kind.
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
Definition: icalerror.c:90
Line-oriented parsing vCard format.
void vcardparameter_set_xname(vcardparameter *param, const char *v)
Sets the X-name of param to v.
void icalerror_clear_errno(void)
Resets icalerrno to ICAL_NO_ERROR.
Definition: icalerror.c:85
vcardcomponent * vcardproperty_get_parent(const vcardproperty *property)
Returns the parent vcard for the specified property.
const char * vcardproperty_get_property_name(const vcardproperty *prop)
vcardproperty * vcardproperty_clone(const vcardproperty *old)
Deeply clones an vcardproperty.
Definition: vcardproperty.c:87
const char * vcardparameter_get_iana_name(const vcardparameter *param)
Returns the IANA name of param.
Defines the data structure representing vCard components.
void vcardproperty_remove_parameter_by_kind(vcardproperty *prop, vcardparameter_kind kind)
Removes all parameters with the specified kind.
vcardparameter * vcardparameter_clone(const vcardparameter *old)
Creates new vcardparameter as a clone of the given one.
void vcardproperty_remove_parameter_by_name(vcardproperty *prop, const char *name)
Removes all parameters with the specified name.
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
Definition: icalmemory.c:315
void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
Appends a string to a buffer.
Definition: icalmemory.c:365
void vcardparameter_set_iana_name(vcardparameter *param, const char *v)
Sets the IANA name of param to v.
char * vcardparameter_as_vcard_string(vcardparameter *param)
Converts vcardparameter into a string representation.
Defines the data structure representing vCard values.
bool vcardparameter_isa_parameter(void *parameter)
vcardparameter_kind vcardparameter_isa(const vcardparameter *parameter)
void vcardproperty_normalize(vcardproperty *prop)
const char * vcardparameter_get_xname(const vcardparameter *param)
Returns the X-name of param.
void vcardparameter_free(vcardparameter *param)
Frees an vcardparameter object.
int vcardcomponent_count_errors(vcardcomponent *comp)
Returns the number of errors encountered parsing the data.
vcardparameter * vcardparameter_new_from_value_string(vcardparameter_kind kind, const char *value)
Creates new vcardparameter of a given kind with a given value.
#define icalerrno
Access the current icalerrno value.
Definition: icalerror.h:133
bool icalproperty_get_allow_empty_properties(void)
Definition: icalproperty.c:72
void vcardproperty_set_parent(vcardproperty *property, vcardcomponent *comp)
Sets the parent vcard for the specified vcardproperty property.
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
Definition: icalmemory.c:158
void vcardvalue_set_parent(vcardvalue *value, vcardproperty *property)
Definition: vcardvalue.c:1017
void vcardparameter_set_parent(vcardparameter *param, vcardproperty *property)
vcardparameter_kind vcardparameter_string_to_kind(const char *string)
Returns the vcardparameter_kind for a given string.
bool vcardproperty_kind_is_valid(const vcardproperty_kind kind)