$darkmode
Libical API Documentation 4.0 STABLE VERSION Visit the v3.0 documentation
vcardvalue.c
Go to the documentation of this file.
1 /*======================================================================
2  FILE: vcardvalue.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 "vcardvalue.h"
19 #include "vcardvalueimpl.h"
20 #include "vcardcomponent.h"
21 #include "vcardproperty.h"
22 #include "vcardtextlist.h"
23 #include "icalerror_p.h"
24 #include "icalmemory.h"
25 
26 #include <ctype.h>
27 #include <locale.h>
28 #include <stdlib.h>
29 
30 #define TMP_BUF_SIZE 1024
31 
32 LIBICAL_VCARD_EXPORT struct vcardvalue_impl *vcardvalue_new_impl(vcardvalue_kind kind)
33 {
34  struct vcardvalue_impl *v;
35 
36  if (!vcardvalue_kind_is_valid(kind)) {
37  return NULL;
38  }
39 
40  if ((v = (struct vcardvalue_impl *)icalmemory_new_buffer(sizeof(struct vcardvalue_impl))) == 0) {
42  return 0;
43  }
44 
45  v->id = ICAL_STRUCTURE_TYPE_VALUE;
46  v->kind = kind;
47  v->size = 0;
48  v->parent = 0;
49  v->x_value = 0;
50  memset(&(v->data), 0, sizeof(v->data));
51 
52  return v;
53 }
54 
55 vcardvalue *vcardvalue_new(vcardvalue_kind kind)
56 {
57  return (vcardvalue *)vcardvalue_new_impl(kind);
58 }
59 
60 vcardvalue *vcardvalue_clone(const vcardvalue *old)
61 {
62  struct vcardvalue_impl *clone;
63 
64  clone = vcardvalue_new_impl(old->kind);
65 
66  if (clone == 0) {
67  return 0;
68  }
69 
70  clone->id = old->id;
71  clone->kind = old->kind;
72  clone->size = old->size;
73 
74  switch (clone->kind) {
75  case VCARD_TEXT_VALUE:
76  case VCARD_URI_VALUE:
77  case VCARD_LANGUAGETAG_VALUE: {
78  if (old->data.v_string != 0) {
79  clone->data.v_string = icalmemory_strdup(old->data.v_string);
80 
81  if (clone->data.v_string == 0) {
82  clone->parent = 0;
83  vcardvalue_free(clone);
84  return 0;
85  }
86  }
87  break;
88  }
89  case VCARD_X_VALUE: {
90  if (old->x_value != 0) {
91  clone->x_value = icalmemory_strdup(old->x_value);
92 
93  if (clone->x_value == 0) {
94  clone->parent = 0;
95  vcardvalue_free(clone);
96  return 0;
97  }
98  }
99 
100  break;
101  }
102 
103  case VCARD_STRUCTURED_VALUE:
104  if (old->data.v_structured != 0) {
105  clone->data.v_structured =
106  vcardstructured_clone(old->data.v_structured);
107  }
108  break;
109 
110  default: {
111  /* all of the other types are stored as values, not
112  pointers, so we can just copy the whole structure. */
113 
114  clone->data = old->data;
115  }
116  }
117 
118  return clone;
119 }
120 
121 char *vcardvalue_strdup_and_dequote_text(const char **str, const char *sep)
122 {
123  const char *p;
124  char *out = (char *)icalmemory_new_buffer(sizeof(char) * strlen(*str) + 1);
125  char *pout;
126 
127  if (out == 0) {
128  return 0;
129  }
130 
131  pout = out;
132 
133  /* Stop the loop when encountering a terminator/separator in the source string
134  or if a null has been written to the destination. This prevents
135  reading past the end of the source string if the last character
136  is a backslash. */
137  for (p = *str; *p != 0; p++) {
138  if (*p == '\\') {
139  p++;
140  switch (*p) {
141  case 0: {
142  p--; // step back to NUL to stop iteration
143  *pout = '\0';
144  break;
145  }
146  case 'n':
147  case 'N': {
148  *pout = '\n';
149  break;
150  }
151  case 't':
152  case 'T': {
153  *pout = '\t';
154  break;
155  }
156  case 'r':
157  case 'R': {
158  *pout = '\r';
159  break;
160  }
161  case 'b':
162  case 'B': {
163  *pout = '\b';
164  break;
165  }
166  case 'f':
167  case 'F': {
168  *pout = '\f';
169  break;
170  }
171  case ';':
172  case ',':
173  case '"':
174  case '\\': {
175  *pout = *p;
176  break;
177  }
178  default: {
179  *pout = ' ';
180  }
181  }
182  } else if (sep && strchr(sep, *p)) {
183  break;
184  } else {
185  *pout = *p;
186  }
187 
188  pout++;
189  }
190 
191  *str = p;
192 
193  *pout = '\0';
194 
195  return out;
196 }
197 
198 /*
199  * Returns a quoted copy of a string
200  * @todo This is not RFC5545 compliant.
201  * RFC 6350 allows:
202  * TEXT-CHAR = "\\" / "\," / "\n" / WSP / NON-ASCII / %x21-2B / %x2D-5B / %x5D-7E
203  * but we use the more restrictive (for ADR and N):
204  * component = "\\" / "\," / "\;" / "\n" / WSP / NON-ASCII /
205  %x21-2B / %x2D-3A / %x3C-5B / %x5D-7E
206  * As such, \b, \f, \r are not allowed, not even escaped
207  */
208 static char *vcardmemory_strdup_and_quote(char **str, char **str_p, size_t *buf_sz,
209  const char *unquoted_str, bool is_param)
210 {
211  const char *p;
212 
213  if (!*str) {
214  *buf_sz = strlen(unquoted_str) + 1;
215 
216  *str_p = *str = (char *)icalmemory_new_buffer(*buf_sz);
217  }
218 
219  /* cppcheck-suppress uninitvar */
220  if (*str_p == 0) {
221  return 0;
222  }
223 
224  for (p = unquoted_str; *p != 0; p++) {
225  switch (*p) {
226  case '\b':
227  case '\f':
228  case '\r':
229  /* ignore */
230  break;
231 
232  case '\\':
233  case ',':
234  case ';':
235  /* escape */
236  icalmemory_append_char(str, str_p, buf_sz, '\\');
237  icalmemory_append_char(str, str_p, buf_sz, *p);
238  break;
239 
240  case '\n':
241  /* If encoding a parameter value, embed literally
242  (parameter encoding is done elsewhere), otherwise escape */
243  icalmemory_append_string(str, str_p, buf_sz, is_param ? "\n" : "\\n");
244  break;
245 
246  default:
247  icalmemory_append_char(str, str_p, buf_sz, *p);
248  break;
249  }
250  }
251 
252  /* Assume the last character is not a '\0' and add one. We could
253  check *str_p != 0, but that would be an uninitialized memory
254  read. */
255 
256  icalmemory_append_char(str, str_p, buf_sz, '\0');
257  return *str;
258 }
259 
260 /*
261  * FIXME
262  *
263  * This is a bad API, as it forces callers to specify their own X type.
264  * This function should take care of this by itself.
265  */
266 static vcardvalue *vcardvalue_new_enum(vcardvalue_kind kind, int x_type, const char *str)
267 {
268  int e = vcardproperty_kind_and_string_to_enum((int)kind, str);
269  struct vcardvalue_impl *value;
270 
271  if (e != 0 && vcardproperty_enum_belongs_to_property(vcardproperty_value_kind_to_kind(kind), e)) {
272  value = vcardvalue_new_impl(kind);
273  value->data.v_enum = e;
274  } else {
275  /* Make it an X value */
276  value = vcardvalue_new_impl(kind);
277  value->data.v_enum = x_type;
278  vcardvalue_set_x(value, str);
279  }
280 
281  return value;
282 }
283 
290 static bool simple_str_to_doublestr(const char *from, char *result, int result_len, char **to)
291 {
292  const char *start = NULL;
293  char *end = NULL, *cur = (char *)from;
294 
295  struct lconv *loc_data = localeconv();
296  int i = 0, len;
297  double dtest;
298 
299  /*sanity checks */
300  if (!from || !result) {
301  return true;
302  }
303 
304  /*skip the white spaces at the beginning */
305  while (*cur && isspace((int)*cur)) {
306  cur++;
307  }
308 
309  start = cur;
310  /* copy the part that looks like a double into result.
311  * during the copy, we give ourselves a chance to convert the '.'
312  * into the decimal separator of the current locale.
313  */
314  while (*cur && (isdigit((int)*cur) || *cur == '.' || *cur == '+' || *cur == '-')) {
315  ++cur;
316  }
317  end = cur;
318  len = (int)(ptrdiff_t)(end - start);
319  if (len + 1 >= result_len) {
320  /* huh hoh, number is too big. truncate it */
321  len = result_len - 1;
322  }
323 
324  /* copy the float number string into result, and take
325  * care to have the (optional) decimal separator be the one
326  * of the current locale.
327  */
328  for (i = 0; i < len; ++i) {
329  if (start[i] == '.' &&
330  loc_data && loc_data->decimal_point && loc_data->decimal_point[0] && loc_data->decimal_point[0] != '.') {
331  /*replace '.' by the digit separator of the current locale */
332  result[i] = loc_data->decimal_point[0];
333  } else {
334  result[i] = start[i];
335  }
336  }
337  if (to) {
338  *to = end;
339  }
340 
341  /* now try to convert to a floating point number, to check for validity only */
342  if (sscanf(result, "%lf", &dtest) != 1) {
343  return true;
344  }
345  return false;
346 }
347 
348 static vcardvalue *vcardvalue_new_from_string_with_error(vcardvalue_kind kind,
349  const char *str,
350  vcardproperty **error)
351 {
352  struct vcardvalue_impl *value = 0;
353 
354  icalerror_check_arg_rz(str != 0, "str");
355 
356  if (error != 0) {
357  *error = 0;
358  }
359 
360  switch (kind) {
361  case VCARD_BOOLEAN_VALUE: {
362  if (!strcmp(str, "TRUE")) {
363  value = vcardvalue_new_boolean(1);
364  } else if (!strcmp(str, "FALSE")) {
365  value = vcardvalue_new_boolean(0);
366  } else if (error != 0) {
367  char temp[TMP_BUF_SIZE];
368  vcardparameter *errParam;
369 
370  snprintf(temp, sizeof(temp),
371  "Could not parse %s as a %s property",
372  str, vcardvalue_kind_to_string(kind));
373  errParam = vcardparameter_new_xlicerrortype(VCARD_XLICERRORTYPE_VALUEPARSEERROR);
374  *error = vcardproperty_vanew_xlicerror(temp, errParam, (void *)0);
375  }
376  break;
377  }
378 
379  case VCARD_VERSION_VALUE:
380  value = vcardvalue_new_enum(kind, (int)VCARD_VERSION_X, str);
381  break;
382 
383  case VCARD_KIND_VALUE:
384  value = vcardvalue_new_enum(kind, (int)VCARD_KIND_X, str);
385  break;
386 
387  case VCARD_GRAMGENDER_VALUE:
388  value = vcardvalue_new_enum(kind, (int)VCARD_GRAMGENDER_X, str);
389  break;
390 
391  case VCARD_INTEGER_VALUE:
392  value = vcardvalue_new_integer(atoi(str));
393  break;
394 
395  case VCARD_FLOAT_VALUE:
396  value = vcardvalue_new_float((float)atof(str));
397  break;
398 
399  case VCARD_UTCOFFSET_VALUE: {
400 #pragma GCC diagnostic push
401 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
402  /* "+" / "-" hh [ [":"] mm ] */
403  char sign[2] = "";
404  unsigned hour, min = 0;
405  int nchar = 0, len = (int)strlen(str);
406 
407  if (len > 3) {
408  const char *fmt;
409 
410  if (str[3] == ':') {
411  fmt = "%1[+-]%02u:%02u%n";
412  } else {
413  fmt = "%1[+-]%02u%02u%n";
414  }
415 
416  if (3 != sscanf(str, fmt, sign, &hour, &min, &nchar)) {
417  nchar = 0;
418  }
419  } else if (2 != sscanf(str, "%1[+-]%02u%n", sign, &hour, &nchar)) {
420  nchar = 0;
421  }
422 #pragma GCC diagnostic pop
423 
424  if (len && (len == nchar)) {
425  int utcoffset = (int)(hour * 3600 + min * 60);
426 
427  if (*sign == '-') {
428  utcoffset = -utcoffset;
429  }
430  value = vcardvalue_new_utcoffset(utcoffset);
431  } else if (error != 0) {
432  char temp[TMP_BUF_SIZE];
433  vcardparameter *errParam;
434 
435  snprintf(temp, sizeof(temp),
436  "Could not parse %s as a %s property",
437  str, vcardvalue_kind_to_string(kind));
438  errParam = vcardparameter_new_xlicerrortype(VCARD_XLICERRORTYPE_VALUEPARSEERROR);
439  *error = vcardproperty_vanew_xlicerror(temp, errParam, (void *)0);
440  }
441  break;
442  }
443 
444  case VCARD_TEXT_VALUE: {
445  char *dequoted_str = vcardvalue_strdup_and_dequote_text(&str, NULL);
446 
447  value = vcardvalue_new_text(dequoted_str);
448  icalmemory_free_buffer(dequoted_str);
449  break;
450  }
451 
452  case VCARD_TEXTLIST_VALUE: {
453  value = vcardvalue_new_textlist(vcardtextlist_new_from_string(str, ','));
454  break;
455  }
456 
457  case VCARD_STRUCTURED_VALUE: {
459  value = vcardvalue_new_structured(st);
461  break;
462  }
463 
464  case VCARD_GEO_VALUE: {
465  char *cur = NULL;
466  struct vcardgeotype geo = {0};
467  memset(geo.coords.lat, 0, VCARD_GEO_LEN);
468  memset(geo.coords.lon, 0, VCARD_GEO_LEN);
469 
470  if (simple_str_to_doublestr(str, geo.coords.lat, VCARD_GEO_LEN, &cur)) {
471  goto geo_parsing_error;
472  }
473  /* skip white spaces */
474  while (cur && isspace((int)*cur)) {
475  ++cur;
476  }
477 
478  /*there is a ';' between the latitude and longitude parts */
479  if (!cur || *cur != ';') {
480  goto geo_parsing_error;
481  }
482 
483  ++cur;
484 
485  /* skip white spaces */
486  while (cur && isspace((int)*cur)) {
487  ++cur;
488  }
489 
490  if (simple_str_to_doublestr(cur, geo.coords.lon, VCARD_GEO_LEN, &cur)) {
491  goto geo_parsing_error;
492  }
493  value = vcardvalue_new_geo(geo);
494  break;
495 
496  geo_parsing_error:
497  if (error != 0) {
498  char temp[TMP_BUF_SIZE];
499  snprintf(temp, sizeof(temp),
500  "Could not parse %s as a %s value",
501  str, vcardvalue_kind_to_string(kind));
502  vcardparameter *errParam =
503  vcardparameter_new_xlicerrortype(VCARD_XLICERRORTYPE_VALUEPARSEERROR);
504  *error = vcardproperty_vanew_xlicerror(temp, errParam, (void *)0);
505  }
506  } break;
507 
508  case VCARD_URI_VALUE:
509  value = vcardvalue_new_uri(str);
510  break;
511 
512  case VCARD_DATE_VALUE:
513  case VCARD_TIME_VALUE:
514  case VCARD_DATETIME_VALUE:
515  case VCARD_DATEANDORTIME_VALUE:
516  case VCARD_TIMESTAMP_VALUE: {
517  struct vcardtimetype tt;
518 
519  tt = vcardtime_from_string(str, kind == VCARD_TIME_VALUE);
520  if (!vcardtime_is_null_datetime(tt)) {
521  value = vcardvalue_new_impl(kind);
522  value->data.v_time = tt;
523 
524  vcardvalue_reset_kind(value);
525  }
526  break;
527  }
528 
529  case VCARD_LANGUAGETAG_VALUE:
530  value = vcardvalue_new_languagetag(str);
531  break;
532 
533  case VCARD_X_VALUE: {
534  value = vcardvalue_new_x(str);
535  } break;
536 
537  default: {
538  char temp[TMP_BUF_SIZE];
539  if (error != 0) {
540  snprintf(temp, TMP_BUF_SIZE, "Unknown type for \'%s\'", str);
541  vcardparameter *errParam = vcardparameter_new_xlicerrortype(VCARD_XLICERRORTYPE_VALUEPARSEERROR);
542  *error = vcardproperty_vanew_xlicerror(temp, errParam, (void *)0);
543  }
544 
545  snprintf(temp, TMP_BUF_SIZE,
546  "vcardvalue_new_from_string got an unknown value type (%s) for \'%s\'",
547  vcardvalue_kind_to_string(kind), str);
548  icalerror_warn(temp);
549  value = 0;
550  }
551  }
552 
553  if (error != 0 && *error == 0 && value == 0) {
554  char temp[TMP_BUF_SIZE];
555  vcardparameter *errParam;
556 
557  snprintf(temp, TMP_BUF_SIZE, "Failed to parse value: \'%s\'", str);
558 
559  /* coverity[resource_leak] */
560  errParam = vcardparameter_new_xlicerrortype(VCARD_XLICERRORTYPE_VALUEPARSEERROR);
561  *error = vcardproperty_vanew_xlicerror(temp, errParam, (void *)0);
562  }
563 
564  return value;
565 }
566 
567 vcardvalue *vcardvalue_new_from_string(vcardvalue_kind kind, const char *str)
568 {
569  return vcardvalue_new_from_string_with_error(kind, str, (vcardproperty **)0);
570 }
571 
572 void vcardvalue_free(vcardvalue *v)
573 {
574  icalerror_check_arg_rv((v != 0), "value");
575 
576  if (v->parent != 0) {
577  return;
578  }
579 
580  if (v->x_value != 0) {
581  icalmemory_free_buffer(v->x_value);
582  }
583 
584  switch (v->kind) {
585  case VCARD_TEXT_VALUE:
586  case VCARD_URI_VALUE:
587  case VCARD_LANGUAGETAG_VALUE: {
588  if (v->data.v_string != 0) {
589  icalmemory_free_buffer((void *)v->data.v_string);
590  v->data.v_string = 0;
591  }
592  break;
593  }
594 
595  case VCARD_TEXTLIST_VALUE: {
596  if (v->data.v_textlist != 0) {
597  vcardstrarray_free(v->data.v_textlist);
598  }
599  v->data.v_textlist = 0;
600  break;
601  }
602 
603  case VCARD_STRUCTURED_VALUE: {
604  vcardstructured_unref(v->data.v_structured);
605  v->data.v_structured = 0;
606  break;
607  }
608 
609  case VCARD_GEO_VALUE: {
610  v->data.v_geo.coords.lat[0] = '\0';
611  v->data.v_geo.coords.lon[0] = '\0';
612  break;
613  }
614 
615  default: {
616  /* Nothing to do */
617  }
618  }
619 
620  v->kind = VCARD_NO_VALUE;
621  v->size = 0;
622  v->parent = 0;
623  memset(&(v->data), 0, sizeof(v->data));
624  v->id = ICAL_STRUCTURE_TYPE_VALUE_EMPTY;
626 }
627 
628 bool vcardvalue_is_valid(const vcardvalue *value)
629 {
630  if (value == 0) {
631  return false;
632  }
633 
634  return true;
635 }
636 
637 static char *vcardvalue_boolean_as_vcard_string_r(const vcardvalue *value)
638 {
639  int data;
640  char *str;
641 
642  icalerror_check_arg_rz((value != 0), "value");
643  str = (char *)icalmemory_new_buffer(6);
644 
645  data = vcardvalue_get_integer(value);
646 
647  strncpy(str, data ? "TRUE" : "FALSE", 6);
648 
649  return str;
650 }
651 
652 #define MAX_INT_DIGITS 12 /* Enough for 2^32 + sign */
653 
654 static char *vcardvalue_int_as_vcard_string_r(const vcardvalue *value)
655 {
656  int data;
657  char *str;
658 
659  icalerror_check_arg_rz((value != 0), "value");
660  str = (char *)icalmemory_new_buffer(MAX_INT_DIGITS);
661 
662  data = vcardvalue_get_integer(value);
663 
664  snprintf(str, MAX_INT_DIGITS, "%d", data);
665 
666  return str;
667 }
668 
669 static char *vcardvalue_utcoffset_as_vcard_string_r(const vcardvalue *value,
670  vcardproperty_version version)
671 {
672  int data, h, m, s;
673  char sign;
674  char *str;
675  size_t size = 10;
676  const char *fmt;
677 
678  icalerror_check_arg_rz((value != 0), "value");
679 
680  str = (char *)icalmemory_new_buffer(size);
681  data = vcardvalue_get_utcoffset(value);
682 
683  if (abs(data) == data) {
684  sign = '+';
685  } else {
686  sign = '-';
687  }
688 
689  h = data / 3600;
690  m = (data - (h * 3600)) / 60;
691  s = (data - (h * 3600) - (m * 60));
692 
693  h = MIN(abs(h), 23);
694  m = MIN(abs(m), 59);
695  s = MIN(abs(s), 59);
696 #pragma GCC diagnostic push
697 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
698  if (s != 0) {
699  if (version == VCARD_VERSION_40) {
700  fmt = "%c%02d%02d%02d";
701  } else {
702  fmt = "%c%02d:%02d:%02d";
703  }
704  } else if (version == VCARD_VERSION_40) {
705  if (m != 0) {
706  fmt = "%c%02d%02d";
707  } else {
708  fmt = "%c%02d";
709  }
710  } else {
711  fmt = "%c%02d:%02d";
712  }
713 
714  snprintf(str, size, fmt, sign, h, m, s);
715 #pragma GCC diagnostic pop
716  return str;
717 }
718 
719 static char *vcardvalue_text_as_vcard_string_r(const vcardvalue *value)
720 {
721  char *str = NULL;
722  char *str_p = NULL;
723  size_t buf_sz;
724 
725  return vcardmemory_strdup_and_quote(&str, &str_p, &buf_sz,
726  value->data.v_string, 0);
727 }
728 
729 static char *vcardvalue_string_as_vcard_string_r(const vcardvalue *value)
730 {
731  const char *data;
732  char *str = 0;
733 
734  icalerror_check_arg_rz((value != 0), "value");
735  data = value->data.v_string;
736 
737  const size_t len_data = strlen(data) + 1;
738  str = (char *)icalmemory_new_buffer(len_data);
739 
740  strncpy(str, data, len_data);
741  str[len_data - 1] = '\0';
742 
743  return str;
744 }
745 
746 static void _vcardstrarray_as_vcard_string_r(char **str, char **str_p, size_t *buf_sz,
747  vcardstrarray *array, const char sep,
748  bool is_param)
749 {
750  if (!vcardstrarray_size(array)) {
751  (void)vcardmemory_strdup_and_quote(str, str_p, buf_sz, "", is_param);
752  return;
753  }
754 
755  size_t i;
756 
757  for (i = 0; i < vcardstrarray_size(array); i++) {
758  if (i) {
759  *str_p -= 1; // backup to \0
760  icalmemory_append_char(str, str_p, buf_sz, sep);
761  }
762 
763  (void)vcardmemory_strdup_and_quote(str, str_p, buf_sz,
764  vcardstrarray_element_at(array, i), is_param);
765  }
766 }
767 
768 char *vcardstrarray_as_vcard_string_r(const vcardstrarray *array, const char sep)
769 {
770  char *buf = NULL;
771  char *buf_ptr = NULL;
772  size_t buf_size;
773 
774  _vcardstrarray_as_vcard_string_r(&buf, &buf_ptr, &buf_size,
775  (vcardstrarray *)array, sep, 0);
776 
777  return buf;
778 }
779 
781 {
782  char *buf;
783  char *buf_ptr;
784  size_t buf_size = 1;
785  size_t i;
786 
787  buf_ptr = buf = (char *)icalmemory_new_buffer(buf_size);
788 
789  for (i = 0; i < vcardstructured_num_fields(st); i++) {
790  vcardstrarray *array = vcardstructured_field_at(st, i);
791 
792  if (i) {
793  if (buf_ptr > buf) {
794  buf_ptr -= 1; // backup to \0
795  }
796  icalmemory_append_char(&buf, &buf_ptr, &buf_size, ';');
797  }
798 
799  if (array) {
800  _vcardstrarray_as_vcard_string_r(&buf, &buf_ptr, &buf_size,
801  array, ',', is_param);
802  } else {
803  icalmemory_append_char(&buf, &buf_ptr, &buf_size, '\0');
804  }
805  }
806 
807  return buf;
808 }
809 
810 static char *vcardvalue_textlist_as_vcard_string_r(const vcardvalue *value,
811  const char sep)
812 {
813  icalerror_check_arg_rz((value != 0), "value");
814 
815  return vcardstrarray_as_vcard_string_r(value->data.v_textlist, sep);
816 }
817 
818 static char *vcardvalue_structured_as_vcard_string_r(const vcardvalue *value)
819 {
820  icalerror_check_arg_rz((value != 0), "value");
821 
822  return vcardstructured_as_vcard_string_r(value->data.v_structured, 0);
823 }
824 
825 static char *vcardvalue_float_as_vcard_string_r(const vcardvalue *value)
826 {
827  float data;
828  char *str;
829  char *old_locale;
830 
831  icalerror_check_arg_rz((value != 0), "value");
832  data = vcardvalue_get_float(value);
833 
834  /* bypass current locale in order to make
835  sure snprintf uses a '.' as a separator
836  set locate to 'C' and keep old locale */
837  old_locale = icalmemory_strdup(setlocale(LC_NUMERIC, NULL));
838  (void)setlocale(LC_NUMERIC, "C");
839 
840  str = (char *)icalmemory_new_buffer(40);
841 
842  snprintf(str, 40, "%f", data);
843 
844  /* restore saved locale */
845  (void)setlocale(LC_NUMERIC, old_locale);
846  icalmemory_free_buffer(old_locale);
847 
848  return str;
849 }
850 
851 static char *vcardvalue_geo_as_vcard_string_r(const vcardvalue *value)
852 {
853  icalerror_check_arg_rz((value != 0), "value");
854  size_t max_len = 2 * VCARD_GEO_LEN;
855  char *str = (char *)icalmemory_new_buffer(max_len);
856  snprintf(str, max_len, "%s;%s",
857  value->data.v_geo.coords.lat, value->data.v_geo.coords.lon);
858  str[max_len - 1] = '\0';
859  return str;
860 }
861 
862 const char *vcardvalue_as_vcard_string(const vcardvalue *value)
863 {
864  char *buf;
865 
866  buf = vcardvalue_as_vcard_string_r(value);
868  return buf;
869 }
870 
871 char *vcardvalue_as_vcard_string_r(const vcardvalue *value)
872 {
873  vcardproperty_version version = VCARD_VERSION_NONE;
874  bool is_structured;
875  unsigned flags = 0;
876 
877  if (value == 0) {
878  return 0;
879  }
880 
881  if (value->parent) {
882  vcardcomponent *comp = vcardproperty_get_parent(value->parent);
883  if (comp) {
884  version = vcardcomponent_get_version(comp);
885  }
886  }
887 
888  if (version == VCARD_VERSION_NONE) {
889  version = VCARD_VERSION_30;
890  }
891 
892  is_structured = vcardproperty_is_structured(vcardproperty_isa(value->parent));
893 
894  switch (value->kind) {
895  case VCARD_BOOLEAN_VALUE:
896  return vcardvalue_boolean_as_vcard_string_r(value);
897 
898  case VCARD_INTEGER_VALUE:
899  return vcardvalue_int_as_vcard_string_r(value);
900 
901  case VCARD_UTCOFFSET_VALUE:
902  return vcardvalue_utcoffset_as_vcard_string_r(value, version);
903 
904  case VCARD_TEXT_VALUE:
905  return vcardvalue_text_as_vcard_string_r(value);
906 
907  case VCARD_TEXTLIST_VALUE:
908  return vcardvalue_textlist_as_vcard_string_r(value,
909  is_structured ? ';' : ',');
910 
911  case VCARD_STRUCTURED_VALUE:
912  return vcardvalue_structured_as_vcard_string_r(value);
913 
914  case VCARD_GEO_VALUE:
915  return vcardvalue_geo_as_vcard_string_r(value);
916 
917  case VCARD_URI_VALUE:
918  case VCARD_LANGUAGETAG_VALUE:
919  return vcardvalue_string_as_vcard_string_r(value);
920 
921  case VCARD_TIME_VALUE:
922  flags |= VCARDTIME_BARE_TIME;
923  _fallthrough();
924 
925  case VCARD_DATE_VALUE:
926  _fallthrough();
927 
928  case VCARD_DATETIME_VALUE:
929  _fallthrough();
930 
931  case VCARD_DATEANDORTIME_VALUE:
932  _fallthrough();
933 
934  case VCARD_TIMESTAMP_VALUE:
935  if (version == VCARD_VERSION_40) {
936  flags |= VCARDTIME_AS_V4;
937  }
938 
939  return vcardtime_as_vcard_string_r(value->data.v_time, flags);
940 
941  case VCARD_FLOAT_VALUE:
942  return vcardvalue_float_as_vcard_string_r(value);
943 
944  case VCARD_KIND_VALUE:
945  case VCARD_VERSION_VALUE:
946  case VCARD_GRAMGENDER_VALUE:
947  if (value->x_value != 0) {
948  return icalmemory_strdup(value->x_value);
949  }
950 
951  return vcardproperty_enum_to_string_r(value->data.v_enum);
952 
953  case VCARD_X_VALUE:
954  if (value->x_value != 0) {
955  return icalmemory_strdup(value->x_value);
956  }
957  _fallthrough();
958 
959  case VCARD_NO_VALUE:
960  _fallthrough();
961 
962  default: {
963  return 0;
964  }
965  }
966 }
967 
968 vcardvalue_kind vcardvalue_isa(const vcardvalue *value)
969 {
970  if (value == 0) {
971  return VCARD_NO_VALUE;
972  }
973 
974  return value->kind;
975 }
976 
977 bool vcardvalue_isa_value(void *value)
978 {
979  const struct vcardvalue_impl *impl = (struct vcardvalue_impl *)value;
980 
981  icalerror_check_arg_rz((value != 0), "value");
982 
983  return (impl->id == ICAL_STRUCTURE_TYPE_VALUE);
984 }
985 
990 void vcardvalue_reset_kind(vcardvalue *value)
991 {
992  if (!value) {
993  return;
994  }
995  switch (value->kind) {
996  case VCARD_DATE_VALUE:
997  case VCARD_TIME_VALUE:
998  case VCARD_DATETIME_VALUE:
999  case VCARD_DATEANDORTIME_VALUE:
1000  case VCARD_TIMESTAMP_VALUE:
1001  if (vcardtime_is_timestamp(value->data.v_time)) {
1002  value->kind = VCARD_TIMESTAMP_VALUE;
1003  } else if (vcardtime_is_time(value->data.v_time)) {
1004  value->kind = VCARD_TIME_VALUE;
1005  } else if (vcardtime_is_date(value->data.v_time)) {
1006  value->kind = VCARD_DATE_VALUE;
1007  } else {
1008  value->kind = VCARD_DATETIME_VALUE;
1009  }
1010  break;
1011 
1012  default:
1013  break;
1014  }
1015 }
1016 
1017 void vcardvalue_set_parent(vcardvalue *value, vcardproperty *property)
1018 {
1019  icalerror_check_arg_rv((value != 0), "value");
1020 
1021  value->parent = property;
1022 }
1023 
1024 vcardproperty *vcardvalue_get_parent(const vcardvalue *value)
1025 {
1026  return value->parent;
1027 }
1028 
1029 /* The remaining interfaces are 'new', 'set' and 'get' for each of the value
1030  types */
struct vcardstructuredtype_impl vcardstructuredtype
Represents a decoded, structured text value.
Common memory management routines.
vcardproperty * vcardvalue_get_parent(const vcardvalue *value)
Definition: vcardvalue.c:1024
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 vcardvalue_set_parent(vcardvalue *value, vcardproperty *property)
Definition: vcardvalue.c:1017
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
Definition: icalerror.c:90
void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
Appends a character to a buffer.
Definition: icalmemory.c:406
Defines the data structure representing vCard properties.
void vcardstructured_unref(vcardstructuredtype *st)
Decrements the reference count of the vcardstructuredtype.
Defines the data structure representing vCard components.
Defines functions for creating vCard text lists.
size_t vcardstructured_num_fields(const vcardstructuredtype *st)
Returns the number of fields in a vcardstructuredtype.
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
char * vcardstructured_as_vcard_string_r(const vcardstructuredtype *st, bool is_param)
Formats a vcardstructuredtype as a vCard property or parameter value.
Definition: vcardvalue.c:780
Defines the data structure representing vCard values.
void vcardvalue_reset_kind(vcardvalue *value)
Definition: vcardvalue.c:990
vcardcomponent * vcardproperty_get_parent(const vcardproperty *property)
Returns the parent vcard for the specified property.
vcardstructuredtype * vcardstructured_new_from_string(const char *str)
Creates a new instance of vcardstructuredtype from a string.
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
Definition: icalmemory.c:158
vcardstructuredtype * vcardstructured_clone(const vcardstructuredtype *st)
Clones a vcardstructuredtype.
bool vcardvalue_kind_is_valid(const vcardvalue_kind kind)
vcardstrarray * vcardstructured_field_at(const vcardstructuredtype *st, size_t position)
Returns the field at the given position in a vcardstructuredtype.