Libical API Documentation 4.0 STABLE VERSION Visit the v3.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 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
55vcardvalue *vcardvalue_new(vcardvalue_kind kind)
56{
57 return (vcardvalue *)vcardvalue_new_impl(kind);
58}
59
60vcardvalue *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
121char *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 */
208static 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 */
266static 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
290static 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
348static 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
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
567vcardvalue *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
572void 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
628bool vcardvalue_is_valid(const vcardvalue *value)
629{
630 if (value == 0) {
631 return false;
632 }
633
634 return true;
635}
636
637static 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
654static 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
669static 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
719static 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
729static 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
746static 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
768char *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
810static 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
818static 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
825static 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
851static 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
862const 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
871char *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
968vcardvalue_kind vcardvalue_isa(const vcardvalue *value)
969{
970 if (value == 0) {
971 return VCARD_NO_VALUE;
972 }
973
974 return value->kind;
975}
976
977bool 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
989
990void 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
1017void vcardvalue_set_parent(vcardvalue *value, vcardproperty *property)
1018{
1019 icalerror_check_arg_rv((value != 0), "value");
1020
1021 value->parent = property;
1022}
1023
1024vcardproperty *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 */
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:355
char * icalmemory_strdup(const char *s)
Creates a duplicate of a string.
Definition icalmemory.c:242
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 * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
Definition icalmemory.c:315
void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
Appends a character to a buffer.
Definition icalmemory.c:406
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
Definition icalmemory.c:158
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:990
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.
bool vcardvalue_kind_is_valid(const vcardvalue_kind kind)