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