Libical API Documentation 4.0 STABLE VERSION [Visit the v3.0 documentation]
Loading...
Searching...
No Matches
vcardcomponent.c
Go to the documentation of this file.
1/*======================================================================
2 FILE: vcardcomponent.c
3 CREATOR: Ken Murchison 24 Aug 2022 <murch@fastmailteam.com>
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 "vcardcomponent.h"
19#include "icalpvl_p.h"
20#include "vcardparser.h"
21#include "vcardproperty_p.h"
22#include "vcardrestriction.h"
23#include "vcardvalue.h"
24#include "icalerror_p.h"
25#include "icalerror.h"
26#include "icalmemory.h"
27#include "icaltypes_p.h"
28
29#include <assert.h>
30#include <stdlib.h>
31#include <limits.h>
32#include <ctype.h>
33
34struct vcardcomponent_impl {
35 icalstructuretype id;
37 vcardproperty *versionp;
38 icalpvl_list properties;
39 icalpvl_elem property_iterator;
40 icalpvl_list components;
41 icalpvl_elem component_iterator;
42 struct vcardcomponent_impl *parent;
43};
44
45static void vcardcomponent_add_children(vcardcomponent *impl, va_list args)
46{
47 void *vp;
48
49 while ((vp = va_arg(args, void *)) != 0) {
50 icalassert(vcardproperty_isa_property(vp) != 0);
51
52 vcardcomponent_add_property(impl, (vcardproperty *)vp);
53 }
54}
55
56static vcardcomponent *vcardcomponent_new_impl(vcardcomponent_kind kind)
57{
58 vcardcomponent *comp;
59
61 return NULL;
62 }
63
64 if ((comp = (vcardcomponent *)icalmemory_new_buffer(sizeof(vcardcomponent))) == 0) {
66 return 0;
67 }
68
69 memset(comp, 0, sizeof(vcardcomponent));
70
71 comp->id = ICAL_STRUCTURE_TYPE_COMPONENT;
72 comp->kind = kind;
73 comp->properties = icalpvl_newlist();
74 comp->components = icalpvl_newlist();
75
76 return comp;
77}
78
80{
81 return vcardcomponent_new_impl(kind);
82}
83
84#if defined(__clang__)
85#pragma clang diagnostic push
86#pragma clang diagnostic ignored "-Wvarargs"
87#endif
89{
90 va_list args;
91
92 vcardcomponent *impl = vcardcomponent_new_impl(kind);
93
94 if (impl == 0) {
95 return 0;
96 }
97
98 va_start(args, kind);
99 vcardcomponent_add_children(impl, args);
100 va_end(args);
101
102 return impl;
103}
104#if defined(__clang__)
105#pragma clang diagnostic pop
106#endif
107
108vcardcomponent *vcardcomponent_new_from_string(const char *str)
109{
111}
112
113vcardcomponent *vcardcomponent_clone(const vcardcomponent *old)
114{
115 vcardcomponent *clone;
116 const vcardproperty *p;
117 icalpvl_elem itr;
118
119 icalerror_check_arg_rz((old != 0), "component");
120
121 clone = vcardcomponent_new_impl(old->kind);
122
123 if (clone == 0) {
124 return 0;
125 }
126
127 for (itr = icalpvl_head(old->properties); itr != 0; itr = icalpvl_next(itr)) {
128 p = (vcardproperty *)icalpvl_data(itr);
130 }
131
132 return clone;
133}
134
135void vcardcomponent_free(vcardcomponent *c)
136{
137 vcardcomponent *comp;
138
139 icalerror_check_arg_rv((c != 0), "component");
140
141 if (c->parent != 0) {
142 return;
143 }
144
145 if (c->properties != 0) {
146 vcardproperty *prop;
147 while ((prop = icalpvl_pop(c->properties)) != 0) {
149 vcardproperty_free(prop);
150 }
151 icalpvl_free(c->properties);
152 }
153
154 while ((comp = icalpvl_data(icalpvl_head(c->components))) != 0) {
157 }
158
159 icalpvl_free(c->components);
160
161 c->kind = VCARD_NO_COMPONENT;
162 c->properties = 0;
163 c->property_iterator = 0;
164 c->components = 0;
165 c->component_iterator = 0;
166 c->id = ICAL_STRUCTURE_TYPE_COMPONENT_EMPTY;
167
169}
170
171char *vcardcomponent_as_vcard_string(vcardcomponent *comp)
172{
173 char *buf;
174
176 if (buf) {
178 }
179 return buf;
180}
181
182char *vcardcomponent_as_vcard_string_r(vcardcomponent *comp)
183{
184 char *buf;
185 char *tmp_buf;
186 size_t buf_size = 1024;
187 char *buf_ptr = 0;
188 icalpvl_elem itr;
189
190 /* RFC6350 explicitly says that the newline is *ALWAYS* a \r\n (CRLF)!!!! */
191 const char newline[] = "\r\n";
192
193 vcardcomponent *c;
194 vcardproperty *p;
196
197 const char *kind_string = NULL;
198
199 icalerror_check_arg_rz((comp != 0), "component");
200 icalerror_check_arg_rz((kind != VCARD_NO_COMPONENT),
201 "component kind is VCARD_NO_COMPONENT");
202
203 kind_string = vcardcomponent_kind_to_string(kind);
204
205 icalerror_check_arg_rz((kind_string != 0), "Unknown kind of component");
206
207 buf = icalmemory_new_buffer(buf_size);
208 if (buf == NULL) {
209 return NULL;
210 }
211
212 buf_ptr = buf;
213
215 icalmemory_append_string(&buf, &buf_ptr, &buf_size, "BEGIN:");
216 icalmemory_append_string(&buf, &buf_ptr, &buf_size, kind_string);
217 icalmemory_append_string(&buf, &buf_ptr, &buf_size, newline);
218
219 for (itr = icalpvl_head(comp->properties); itr != 0; itr = icalpvl_next(itr)) {
220 p = (vcardproperty *)icalpvl_data(itr);
221
222 icalerror_assert((p != 0), "Got a null property");
224
225 icalmemory_append_string(&buf, &buf_ptr, &buf_size, tmp_buf);
226 icalmemory_free_buffer(tmp_buf);
227 }
228 }
229
230 for (itr = icalpvl_head(comp->components); itr != 0; itr = icalpvl_next(itr)) {
231 c = (vcardcomponent *)icalpvl_data(itr);
232
234 if (tmp_buf != NULL) {
235 icalmemory_append_string(&buf, &buf_ptr, &buf_size, tmp_buf);
236 icalmemory_free_buffer(tmp_buf);
237 }
238 }
239
241 icalmemory_append_string(&buf, &buf_ptr, &buf_size, "END:");
242 icalmemory_append_string(&buf, &buf_ptr, &buf_size, kind_string);
243 icalmemory_append_string(&buf, &buf_ptr, &buf_size, newline);
244 }
245
246 return buf;
247}
248
249bool vcardcomponent_is_valid(const vcardcomponent *component)
250{
251 if (component) {
252 if ((component->id == ICAL_STRUCTURE_TYPE_COMPONENT) && (component->kind != VCARD_NO_COMPONENT)) {
253 return true;
254 }
255 }
256 return false;
257}
258
259vcardcomponent_kind vcardcomponent_isa(const vcardcomponent *component)
260{
261 icalerror_check_arg_rx((component != 0), "component", VCARD_NO_COMPONENT);
262
263 return component->kind;
264}
265
266bool vcardcomponent_isa_component(const void *component)
267{
268 const vcardcomponent *impl = component;
269
270 icalerror_check_arg_rz((component != 0), "component");
271
272 return (impl->id == ICAL_STRUCTURE_TYPE_COMPONENT);
273}
274
275void vcardcomponent_add_property(vcardcomponent *comp, vcardproperty *property)
276{
277 icalerror_check_arg_rv((comp != 0), "component");
278 icalerror_check_arg_rv((property != 0), "property");
279
280 icalerror_assert((!vcardproperty_get_parent(property)),
281 "The property has already been added to a vcard. "
282 "Remove the property with vcardcomponent_remove_property "
283 "before calling vcardcomponent_add_property");
284
285 vcardproperty_set_parent(property, comp);
286
287 icalpvl_push(comp->properties, property);
288
289 if (vcardproperty_isa(property) == VCARD_VERSION_PROPERTY) {
290 comp->versionp = property;
291 }
292}
293
294void vcardcomponent_remove_property(vcardcomponent *comp, vcardproperty *property)
295{
296 icalpvl_elem itr, next_itr;
297
298 icalerror_check_arg_rv((comp != 0), "component");
299 icalerror_check_arg_rv((property != 0), "property");
300
301 if (vcardproperty_get_parent(property) == 0) {
302 return;
303 }
304
305 if (vcardproperty_isa(property) == VCARD_VERSION_PROPERTY) {
306 comp->versionp = 0;
307 }
308
309 for (itr = icalpvl_head(comp->properties); itr != 0; itr = next_itr) {
310 next_itr = icalpvl_next(itr);
311
312 if (icalpvl_data(itr) == (void *)property) {
313 if (comp->property_iterator == itr) {
314 comp->property_iterator = icalpvl_next(itr);
315 }
316
317 (void)icalpvl_remove(comp->properties, itr);
318 vcardproperty_set_parent(property, 0);
319 }
320 }
321}
322
323int vcardcomponent_count_properties(vcardcomponent *comp,
325 int ignore_alts)
326{
327 int count = 0;
328 icalpvl_elem itr;
329 vcardstrarray *altids = NULL;
330
331 icalerror_check_arg_rz((comp != 0), "component");
332
333 if (ignore_alts) {
334 altids = vcardstrarray_new(2);
335 }
336
337 for (itr = icalpvl_head(comp->properties); itr != 0; itr = icalpvl_next(itr)) {
338 vcardproperty *prop = (vcardproperty *)icalpvl_data(itr);
339
340 if (kind == VCARD_ANY_PROPERTY || kind == vcardproperty_isa(prop)) {
341 if (ignore_alts) {
342 /* Like-properties having the same ALTID only get counted once */
343 vcardparameter *param =
346 if (param) {
347 const char *altid = vcardparameter_get_altid(param);
348
349 if (vcardstrarray_find(altids, altid) >= vcardstrarray_size(altids)) {
350 continue;
351 }
352
353 vcardstrarray_append(altids, altid);
354 }
355 }
356 count++;
357 }
358 }
359
360 if (ignore_alts) {
361 vcardstrarray_free(altids);
362 }
363
364 return count;
365}
366
367vcardproperty *vcardcomponent_get_current_property(vcardcomponent *comp)
368{
369 icalerror_check_arg_rz((comp != 0), "card");
370
371 if (comp->property_iterator == 0) {
372 return 0;
373 }
374
375 return (vcardproperty *)icalpvl_data(comp->property_iterator);
376}
377
378vcardproperty *vcardcomponent_get_first_property(vcardcomponent *c,
380{
381 icalerror_check_arg_rz((c != 0), "card");
382
383 for (c->property_iterator = icalpvl_head(c->properties);
384 c->property_iterator != 0;
385 c->property_iterator = icalpvl_next(c->property_iterator)) {
386 vcardproperty *p = (vcardproperty *)icalpvl_data(c->property_iterator);
387
389 return p;
390 }
391 }
392 return 0;
393}
394
395vcardproperty *vcardcomponent_get_next_property(vcardcomponent *c,
397{
398 icalerror_check_arg_rz((c != 0), "card");
399
400 if (c->property_iterator == 0) {
401 return 0;
402 }
403
404 for (c->property_iterator = icalpvl_next(c->property_iterator);
405 c->property_iterator != 0;
406 c->property_iterator = icalpvl_next(c->property_iterator)) {
407 vcardproperty *p = (vcardproperty *)icalpvl_data(c->property_iterator);
408
410 return p;
411 }
412 }
413
414 return 0;
415}
416
417vcardproperty **vcardcomponent_get_properties(vcardcomponent *comp,
419
420void vcardcomponent_add_component(vcardcomponent *parent, vcardcomponent *child)
421{
422 icalerror_check_arg_rv((parent != 0), "parent");
423 icalerror_check_arg_rv((child != 0), "child");
424
425 if (child->parent != 0) {
427 }
428
429 child->parent = parent;
430
431 icalpvl_push(parent->components, child);
432}
433
434void vcardcomponent_remove_component(vcardcomponent *parent,
435 vcardcomponent *child)
436{
437 icalpvl_elem itr, next_itr;
438
439 icalerror_check_arg_rv((parent != 0), "parent");
440 icalerror_check_arg_rv((child != 0), "child");
441
442 for (itr = icalpvl_head(parent->components); itr != 0; itr = next_itr) {
443 next_itr = icalpvl_next(itr);
444
445 if (icalpvl_data(itr) == (void *)child) {
446 if (parent->component_iterator == itr) {
447 /* Don't let the current iterator become invalid */
448
449 /* HACK. The semantics for this are troubling. */
450 parent->component_iterator = icalpvl_next(parent->component_iterator);
451 }
452 (void)icalpvl_remove(parent->components, itr);
453 child->parent = 0;
454 break;
455 }
456 }
457}
458
459int vcardcomponent_count_components(vcardcomponent *component,
461{
462 int count = 0;
463 icalpvl_elem itr;
464
465 icalerror_check_arg_rz((component != 0), "component");
466
467 for (itr = icalpvl_head(component->components); itr != 0; itr = icalpvl_next(itr)) {
468 if (kind == VCARD_ANY_COMPONENT ||
469 kind == vcardcomponent_isa((vcardcomponent *)icalpvl_data(itr))) {
470 count++;
471 }
472 }
473
474 return count;
475}
476
477vcardcomponent *vcardcomponent_get_current_component(vcardcomponent *component)
478{
479 icalerror_check_arg_rz((component != 0), "component");
480
481 if (component->component_iterator == 0) {
482 return 0;
483 }
484
485 return (vcardcomponent *)icalpvl_data(component->component_iterator);
486}
487
488vcardcomponent *vcardcomponent_get_first_component(vcardcomponent *c,
490{
491 icalerror_check_arg_rz((c != 0), "component");
492
493 for (c->component_iterator = icalpvl_head(c->components);
494 c->component_iterator != 0;
495 c->component_iterator = icalpvl_next(c->component_iterator)) {
496 vcardcomponent *p = (vcardcomponent *)icalpvl_data(c->component_iterator);
497
499 return p;
500 }
501 }
502
503 return 0;
504}
505
506vcardcomponent *vcardcomponent_get_next_component(vcardcomponent *c,
508{
509 icalerror_check_arg_rz((c != 0), "component");
510
511 if (c->component_iterator == 0) {
512 return 0;
513 }
514
515 for (c->component_iterator = icalpvl_next(c->component_iterator);
516 c->component_iterator != 0;
517 c->component_iterator = icalpvl_next(c->component_iterator)) {
518 vcardcomponent *p = (vcardcomponent *)icalpvl_data(c->component_iterator);
519
521 return p;
522 }
523 }
524
525 return 0;
526}
527
528int vcardcomponent_check_restrictions(vcardcomponent *comp)
529{
530 icalerror_check_arg_rz(comp != 0, "comp");
531 return (int)vcardrestriction_check(comp);
532}
533
534int vcardcomponent_count_errors(vcardcomponent *comp)
535{
536 int errors = 0;
537 icalpvl_elem itr;
538
539 icalerror_check_arg_rz((comp != 0), "card");
540
541 for (itr = icalpvl_head(comp->properties); itr != 0; itr = icalpvl_next(itr)) {
542 const vcardproperty *p = (vcardproperty *)icalpvl_data(itr);
544 errors++;
545 }
546 }
547
548 return errors;
549}
550
551void vcardcomponent_strip_errors(vcardcomponent *comp)
552{
553 icalpvl_elem itr, next_itr;
554
555 icalerror_check_arg_rv((comp != 0), "comp");
556
557 for (itr = icalpvl_head(comp->properties); itr != 0; itr = next_itr) {
558 vcardproperty *p = (vcardproperty *)icalpvl_data(itr);
559 next_itr = icalpvl_next(itr);
560
564 }
565 }
566}
567
568struct vcardcomponent_kind_map {
570 char name[20];
571};
572
573static const struct vcardcomponent_kind_map component_map[] = {
574 {VCARD_XROOT_COMPONENT, "XROOT"},
575 {VCARD_VCARD_COMPONENT, "VCARD"},
576
577 /* End of list */
578 {VCARD_NO_COMPONENT, ""},
579};
580
582{
583 int i = 0;
584
585 do {
586 if (component_map[i].kind == kind) {
587 return true;
588 }
589 } while (component_map[i++].kind != VCARD_NO_COMPONENT);
590
591 return false;
592}
593
595{
596 int i;
597
598 for (i = 0; component_map[i].kind != VCARD_NO_COMPONENT; i++) {
599 if (component_map[i].kind == kind) {
600 return component_map[i].name;
601 }
602 }
603
604 return 0;
605}
606
608{
609 int i;
610
611 if (string == 0) {
612 return VCARD_NO_COMPONENT;
613 }
614
615 for (i = 0; component_map[i].kind != VCARD_NO_COMPONENT; i++) {
616 if (strncasecmp(string, component_map[i].name, strlen(component_map[i].name)) == 0) {
617 return component_map[i].kind;
618 }
619 }
620
621 return VCARD_NO_COMPONENT;
622}
623
624static int strcmpsafe(const char *a, const char *b)
625{
626 return strcmp((a == NULL ? "" : a),
627 (b == NULL ? "" : b));
628}
629
630static int prop_compare(void *a, void *b)
631{
632 const vcardproperty *p1 = (vcardproperty *)a;
633 const vcardproperty *p2 = (vcardproperty *)b;
636 int r = (int)k1 - (int)k2;
637
638 if (r == 0) {
639 if (k1 == VCARD_X_PROPERTY) {
640 r = strcmp(vcardproperty_get_x_name(p1),
642 }
643
644 if (r == 0) {
645 r = strcmpsafe(vcardproperty_get_value_as_string(p1),
647 }
648 }
649 /* Always sort VERSION first */
650 else if (k1 == VCARD_VERSION_PROPERTY) {
651 r = -1;
652 } else if (k2 == VCARD_VERSION_PROPERTY) {
653 r = 1;
654 }
655
656 return r;
657}
658
659static int prop_kind_compare(vcardproperty_kind kind,
660 vcardcomponent *c1, vcardcomponent *c2)
661{
662 const vcardproperty *p1 = vcardcomponent_get_first_property(c1, kind);
663 const vcardproperty *p2 = vcardcomponent_get_first_property(c2, kind);
664
665 if (p1 && p2) {
666 return strcmpsafe(vcardproperty_get_value_as_string(p1),
668 } else if (p1) {
669 return -1;
670 } else if (p2) {
671 return 1;
672 }
673
674 return 0;
675}
676
677static int comp_compare(void *a, void *b)
678{
679 vcardcomponent *c1 = (vcardcomponent *)a;
680 vcardcomponent *c2 = (vcardcomponent *)b;
683 int r = (int)k1 - (int)k2;
684
685 if (r == 0) {
686 if (k1 == VCARD_VCARD_COMPONENT) {
687 vcardproperty_kind prop_kinds[] = {
693 /* XXX What else should we compare? */
695
696 for (int i = 0; r == 0 && prop_kinds[i] != VCARD_NO_PROPERTY; i++) {
697 r = prop_kind_compare(prop_kinds[i], c1, c2);
698 }
699 }
700
701 if (r == 0) {
702 r = prop_kind_compare(VCARD_UID_PROPERTY, c1, c2);
703
704 if (r == 0) {
705 /* XXX Anything else to compare? */
706 }
707 }
708 }
709
710 return r;
711}
712
713void vcardcomponent_normalize(vcardcomponent *comp)
714{
715 icalpvl_list sorted_props = icalpvl_newlist();
716 icalpvl_list sorted_comps = icalpvl_newlist();
717 vcardproperty *prop;
718 vcardcomponent *sub;
719
720 /* Normalize properties into sorted list */
721 while ((prop = icalpvl_pop(comp->properties)) != 0) {
722 int nparams, remove = 0;
723
725
726 nparams = vcardproperty_count_parameters(prop);
727
728 /* Remove unparameterized properties having default values */
729 if (nparams == 0) {
730 switch (vcardproperty_isa(prop)) {
733 remove = 1;
734 }
735 break;
736
737 default:
738 break;
739 }
740 }
741
742 if (remove) {
743 vcardproperty_set_parent(prop, 0); // MUST NOT have a parent to free
744 vcardproperty_free(prop);
745 } else {
746 icalpvl_insert_ordered(sorted_props, prop_compare, prop);
747 }
748 }
749
750 icalpvl_free(comp->properties);
751 comp->properties = sorted_props;
752
753 /* Normalize sub-components into sorted list */
754 while ((sub = icalpvl_pop(comp->components)) != 0) {
756 icalpvl_insert_ordered(sorted_comps, comp_compare, sub);
757 }
758
759 icalpvl_free(comp->components);
760 comp->components = sorted_comps;
761}
762
763#define UUID_PREFIX "urn:uuid:"
764#define UUID_PREFIX_LEN 9
765
766static void comp_to_v4(vcardcomponent *impl)
767{
768 icalpvl_elem itr, next;
769
770 for (itr = icalpvl_head(impl->properties); itr != 0; itr = next) {
771 vcardproperty *prop = (vcardproperty *)icalpvl_data(itr);
773 vcardvalue *value = vcardproperty_get_value(prop);
774 vcardvalue_kind vkind = vcardvalue_isa(value);
775 vcardparameter *param;
776 vcardenumarray *types = NULL;
777
778 next = icalpvl_next(itr);
779
780 /* Remove TYPE=PREF and replace with PREF=1 (if no existing (PREF=) */
781 for (param = vcardproperty_get_first_parameter(prop,
783 param;
787 size_t i;
788
789 types = vcardparameter_get_type(param);
790 i = vcardenumarray_find(types, &pref);
791 if (i < vcardenumarray_size(types)) {
793 if (!vcardenumarray_size(types)) {
795 }
796
799 if (!param) {
800 param = vcardparameter_new_pref(1);
801 vcardproperty_add_parameter(prop, param);
802 }
803
804 break;
805 }
806 }
807
808 switch (pkind) {
811 break;
812
816 for (param = vcardproperty_get_first_parameter(prop,
818 param;
821 const char *name = vcardparameter_get_xname(param);
822
823 /* This appears in the wild for v3 date with missing year */
824 if (name && !strcasecmp(name, "X-APPLE-OMIT-YEAR")) {
826
827 dt.year = -1;
828 vcardproperty_set_bday(prop, dt);
830 break;
831 }
832 }
833 break;
834
836 if (vkind != VCARD_X_VALUE) {
837 vcardgeotype geo = vcardvalue_get_geo(value);
838
839 if (!geo.uri) {
840 /* Convert STRUCTURED value kind to geo: URI */
841 int n = snprintf(NULL, 0, "geo:%s,%s",
842 geo.coords.lat, geo.coords.lon);
843
844 size_t size = (size_t)n + 1;
845 char *buf = icalmemory_new_buffer(size);
846 snprintf(buf, size, "geo:%s,%s",
847 geo.coords.lat, geo.coords.lon);
848
849 geo.uri = buf;
850 geo.coords.lat[0] = '\0';
851 geo.coords.lon[0] = '\0';
852 vcardvalue_set_geo(value, geo);
854 }
855 }
856 break;
857
862 char *mediatype = NULL;
865 if (param) {
866 types = vcardparameter_get_type(param);
867 for (size_t i = 0; i < vcardenumarray_size(types); i++) {
868 const vcardenumarray_element *type =
870
871 if (type->xvalue) {
872 /* Copy and lowercase the mediatype */
873 char *c;
874
875 switch (pkind) {
878 mediatype = icalmemory_strdup("image/");
879 break;
881 mediatype = icalmemory_strdup("audio/");
882 break;
883 default:
884 mediatype = icalmemory_strdup("application/");
885 break;
886 }
887
888 size_t size = strlen(mediatype);
889 char *buf_ptr = mediatype + size;
890 icalmemory_append_string(&mediatype, &buf_ptr,
891 &size, type->xvalue);
892 for (c = mediatype; *c; c++) {
893 *c = (char)tolower((int)*c);
894 }
895
896 /* Remove this TYPE */
898 if (!vcardenumarray_size(types)) {
900 }
901 break;
902 }
903 }
904 }
905
906 const char *data = vcardvalue_get_uri(value);
907 if (data && !strchr(data, ':')) {
908 /* Convert base64-encoded TEXT value kind to data: URI */
909 size_t size = strlen(data) + 7; // "data:," + NUL
910
913 if (param) {
914 size += 7; // ";base64
915 }
916
917 char *buf = icalmemory_new_buffer(size);
918 char *buf_ptr = buf;
919
920 icalmemory_append_string(&buf, &buf_ptr, &size, "data:");
921 if (mediatype) {
922 icalmemory_append_string(&buf, &buf_ptr, &size, mediatype);
923 icalmemory_free_buffer(mediatype);
924 }
925 if (param) {
926 icalmemory_append_string(&buf, &buf_ptr, &size, ";base64");
928 }
929 icalmemory_append_char(&buf, &buf_ptr, &size, ',');
930 icalmemory_append_string(&buf, &buf_ptr, &size, data);
931
932 value->kind = VCARD_URI_VALUE;
933 vcardvalue_set_uri(value, buf);
934
936 } else if (mediatype) {
937 param = vcardparameter_new_mediatype(mediatype);
938 vcardproperty_add_parameter(prop, param);
939
940 icalmemory_free_buffer(mediatype);
941 }
942 break;
943 }
944
945 case VCARD_UID_PROPERTY: {
946 /* Does it look like a URI (the default)? */
947 const char *data = vcardvalue_get_text(value);
948 if (!data) {
949 break;
950 }
951 if (!strncasecmp(data, "urn:uuid:", 9) ||
952 !strncasecmp(data, "mailto:", 7) ||
953 !strncasecmp(data, "http://", 7) ||
954 !strncasecmp(data, "https://", 8) ||
955 !strncasecmp(data, "ldap://", 7) ||
956 !strncasecmp(data, "ldaps://", 8)) {
957 value->kind = VCARD_URI_VALUE;
960 } else {
961 /* Otherwise, treat it as TEXT */
962 value->kind = VCARD_TEXT_VALUE;
963 }
964 break;
965 }
966
967 case VCARD_X_PROPERTY: {
968 /* Rename X-ADDRESSBOOKSERVER-KIND, X-ADDRESSBOOKSERVER-MEMBER */
969 const char *xname = vcardproperty_get_x_name(prop);
970
971 if (!strncasecmp(xname, "X-ADDRESSBOOKSERVER-", 20)) {
973 const char *valstr = vcardvalue_as_vcard_string(value);
974 vcardproperty *new;
975 char *buf = NULL, *buf_ptr;
976 size_t buf_size;
977
978 switch (kind) {
980 /* strip urn:uuid: prefix and RFC6868-decode string */
981 if (!strncmp(valstr, UUID_PREFIX, UUID_PREFIX_LEN)) {
982 valstr += UUID_PREFIX_LEN;
983 }
984
985 buf_size = strlen(valstr) + 1;
986 buf_ptr = buf = icalmemory_new_buffer(buf_size);
987 icalmemory_append_decoded_string(&buf, &buf_ptr, &buf_size,
988 valstr);
989 valstr = buf;
990
991 _fallthrough();
992
994 new = vcardproperty_new(kind);
995 vcardproperty_set_value_from_string(new, valstr, "NO");
998 vcardproperty_free(prop);
999 break;
1000
1001 default:
1002 break;
1003 }
1004
1006 }
1007 break;
1008 }
1009
1010 default:
1011 break;
1012 }
1013 }
1014}
1015
1016struct pref_prop {
1017 vcardproperty *prop;
1018 int level;
1019};
1020
1021static void comp_to_v3(vcardcomponent *impl)
1022{
1023 struct pref_prop *pref_props[VCARD_NO_PROPERTY] = {0};
1025 vcardproperty_kind pkind;
1026 icalpvl_elem itr, next;
1027
1028 for (itr = icalpvl_head(impl->properties); itr != 0; itr = next) {
1029 vcardproperty *prop = (vcardproperty *)icalpvl_data(itr);
1030 vcardparameter *val_param =
1032 vcardvalue *value = vcardproperty_get_value(prop);
1033 vcardvalue_kind vkind = vcardvalue_isa(value);
1034 vcardparameter *param;
1035 char *subtype = NULL;
1036 const char *mediatype, *uri, *xname = NULL;
1037
1038 next = icalpvl_next(itr);
1039
1040 /* Find prop with lowest PREF= for each set of like properties */
1041 pkind = vcardproperty_isa(prop);
1043 if (param && pkind != VCARD_X_PROPERTY) {
1044 int level = vcardparameter_get_pref(param);
1045 struct pref_prop *pp = pref_props[pkind];
1046
1047 if (!pp) {
1048 pp = icalmemory_new_buffer(sizeof(struct pref_prop));
1049 pp->prop = prop;
1050 pp->level = level;
1051 pref_props[pkind] = pp;
1052 } else if (level < pp->level) {
1053 pp->prop = prop;
1054 pp->level = level;
1055 }
1056 }
1057
1058 /* Replace MEDIATYPE with TYPE=<subtype> */
1061 if (param) {
1062 mediatype = vcardparameter_get_mediatype(param);
1063 subtype = (char *)strchr(mediatype, '/');
1064 if (subtype) {
1065 /* Copy and uppercase the subtype */
1066 char *c;
1067
1068 subtype = icalmemory_strdup(subtype + 1);
1069 for (c = subtype; *c; c++) {
1070 *c = (char)toupper((int)*c);
1071 }
1072
1073 /* Add TYPE parameter */
1074 type.val = VCARD_TYPE_NONE;
1075 type.xvalue = subtype;
1077
1078 icalmemory_free_buffer(subtype);
1079 }
1080
1082 }
1083
1084 switch (pkind) {
1087 break;
1088
1093
1094 if (dt.year == -1) {
1095 /* This appears in the wild for v3 date with missing year */
1096 dt.year = 1604;
1098 "X-APPLE-OMIT-YEAR",
1099 "1604");
1100 }
1101 if (dt.hour != -1) {
1102 if (dt.second == -1) {
1103 dt.second = 0;
1104 if (dt.minute == -1) {
1105 dt.minute = 0;
1106 }
1107 }
1108 }
1109
1110 vcardproperty_set_bday(prop, dt);
1111 break;
1112 }
1113
1114 case VCARD_GEO_PROPERTY:
1115 if (vkind != VCARD_X_VALUE) {
1116 vcardgeotype geo = vcardvalue_get_geo(value);
1117
1118 if (geo.uri && !strncmp(geo.uri, "geo:", 4)) {
1119 /* Convert geo: URI to STRUCTURED value kind */
1120 char *buf = icalmemory_strdup(geo.uri);
1121 geo.uri = NULL;
1122 const char *lat = buf + 4;
1123 char *lon = strchr(buf + 4, ',');
1124 if (lon) {
1125 *lon++ = '\0';
1126 }
1127 if (lat && lon) {
1128 strncpy(geo.coords.lat, lat, VCARD_GEO_LEN - 1);
1129 geo.coords.lat[VCARD_GEO_LEN - 1] = '\0';
1130 strncpy(geo.coords.lon, lon, VCARD_GEO_LEN - 1);
1131 geo.coords.lon[VCARD_GEO_LEN - 1] = '\0';
1132 } else {
1133 geo.coords.lat[0] = '\0';
1134 geo.coords.lon[0] = '\0';
1135 }
1136
1137 vcardvalue_set_geo(value, geo);
1139 }
1140 }
1141 break;
1142
1143 case VCARD_KEY_PROPERTY:
1147 uri = vcardvalue_get_uri(value);
1148 if (uri && !strncmp("data:", uri, 5)) {
1149 /* Convert data: URI to base64-encoded TEXT value kind */
1150 char *base64, *data = NULL;
1151
1152 char *buf = icalmemory_strdup(uri);
1153 mediatype = buf + 5;
1154 base64 = (char *)strstr(mediatype, ";base64,");
1155
1156 if (base64) {
1158 vcardproperty_add_parameter(prop, param);
1159
1160 *base64 = '\0';
1161 data = base64 + 8;
1162 } else {
1163 data = (char *)strchr(mediatype, ',');
1164 if (data) {
1165 *data++ = '\0';
1166 }
1167 }
1168
1169 subtype = (char *)strchr(mediatype, '/');
1170 if (subtype) {
1171 /* Copy and uppercase the subtype */
1172 char *c;
1173
1174 for (c = ++subtype; *c; c++) {
1175 *c = (char)toupper((int)*c);
1176 }
1177
1178 /* Add TYPE parameter */
1179 type.val = VCARD_VERSION_NONE;
1180 type.xvalue = subtype;
1182 }
1183
1184 value->kind = VCARD_TEXT_VALUE;
1185 vcardvalue_set_text(value, data ? data : "");
1187 }
1188 break;
1189
1191 /* Rename KIND, MEMBER */
1192 xname = "X-ADDRESSBOOKSERVER-KIND";
1193
1194 _fallthrough();
1195
1196 case VCARD_MEMBER_PROPERTY: {
1197 char *buf = NULL;
1198
1199 const char *xval = vcardvalue_as_vcard_string(value);
1200 if (!xname) {
1201 size_t buf_size = strlen(UUID_PREFIX) + strlen(xval) + 1;
1202 char *buf_ptr = buf = icalmemory_new_buffer(buf_size);
1203
1204 /* RFC6868-encode the uid and prefix with urn:uuid: */
1205 icalmemory_append_string(&buf, &buf_ptr, &buf_size, UUID_PREFIX);
1206 icalmemory_append_encoded_string(&buf, &buf_ptr, &buf_size, xval);
1207 xname = "X-ADDRESSBOOKSERVER-MEMBER";
1208 xval = buf;
1209 }
1210
1211 vcardproperty *xprop = vcardproperty_new_x(xval);
1212 vcardproperty_set_x_name(xprop, xname);
1213 vcardcomponent_add_property(impl, xprop);
1215 vcardproperty_free(prop);
1217 break;
1218 }
1219
1220 case VCARD_TEL_PROPERTY:
1221 uri = vcardvalue_get_uri(value);
1222 if (uri && !strncmp(uri, "tel:", 4)) {
1223 /* Convert tel: URI to TEXT value kind */
1224 char *buf = icalmemory_strdup(uri + 4);
1225
1226 value->kind = VCARD_TEXT_VALUE;
1227 vcardvalue_set_text(value, buf);
1228
1229 if (val_param) {
1231 }
1232
1234 }
1235 break;
1236
1237 case VCARD_UID_PROPERTY:
1238 /* Treat all values as TEXT (the default) */
1239 if (!vcardvalue_get_text(value)) {
1240 break;
1241 }
1242 value->kind = VCARD_TEXT_VALUE;
1243
1244 if (val_param) {
1246 }
1247 break;
1248
1249 default:
1250 break;
1251 }
1252 }
1253
1254 /* Add TYPE=PREF for each most preferred property */
1255 for (pkind = 0; pkind < VCARD_NO_PROPERTY; ++pkind) {
1256 struct pref_prop *pp = pref_props[pkind];
1257
1258 if (pp) {
1259 type.val = VCARD_TYPE_PREF;
1260 type.xvalue = NULL;
1261
1262 vcardproperty_add_type_parameter(pp->prop, &type);
1264 }
1265 }
1266}
1267
1268void vcardcomponent_transform(vcardcomponent *impl,
1269 vcardproperty_version version)
1270{
1271 icalpvl_elem itr;
1273
1274 icalerror_check_arg_rv((impl != 0), "component");
1275 icalerror_check_arg_rv((kind != VCARD_NO_COMPONENT),
1276 "component kind is VCARD_NO_COMPONENT");
1277
1278 if (kind == VCARD_VCARD_COMPONENT) {
1279 if (version == VCARD_VERSION_40) {
1280 comp_to_v4(impl);
1281 } else {
1282 comp_to_v3(impl);
1283 }
1284 }
1285
1286 for (itr = icalpvl_head(impl->components); itr != 0; itr = icalpvl_next(itr)) {
1287 vcardcomponent *c = (vcardcomponent *)icalpvl_data(itr);
1288 vcardcomponent_transform(c, version);
1289 }
1290}
1291
1292/******************** Convenience routines **********************/
1293
1295{
1296 icalerror_check_arg_rx(comp != 0, "comp", VCARD_VERSION_NONE);
1297
1298 if (comp->versionp == 0) {
1299 comp->versionp =
1301
1302 if (comp->versionp == 0) {
1303 return VCARD_VERSION_NONE;
1304 }
1305 }
1306
1307 return vcardproperty_get_version(comp->versionp);
1308}
1309
1310const char *vcardcomponent_get_uid(vcardcomponent *comp)
1311{
1312 vcardproperty *prop;
1313
1314 icalerror_check_arg_rz(comp != 0, "comp");
1315
1317
1318 if (prop == 0) {
1319 return 0;
1320 }
1321
1322 return vcardproperty_get_uid(prop);
1323}
1324
1325const char *vcardcomponent_get_fn(vcardcomponent *comp)
1326{
1327 vcardproperty *prop;
1328
1329 icalerror_check_arg_rz(comp != 0, "comp");
1330
1332
1333 if (prop == 0) {
1334 return 0;
1335 }
1336
1337 return vcardproperty_get_fn(prop);
1338}
enum icalrequeststatus kind
Definition icalenums.c:26
const char * str
Definition icalenums.c:29
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
Definition icalerror.c:90
Error handling for libical.
@ ICAL_NEWFAILED_ERROR
Definition icalerror.h:50
@ ICAL_USAGE_ERROR
Definition icalerror.h:71
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_decoded_string(char **buf, char **pos, size_t *buf_size, const char *string)
Definition icalmemory.c:521
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_encoded_string(char **buf, char **pos, size_t *buf_size, const char *string)
Definition icalmemory.c:478
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.
char lon[16]
Definition vcardtypes.h:25
struct vcardgeotype::@162201204342263205067330224176321027032342016274 coords
const char * uri
Definition vcardtypes.h:22
char lat[16]
Definition vcardtypes.h:24
void vcardcomponent_strip_errors(vcardcomponent *comp)
Removes all X-LIC-ERROR properties.
#define UUID_PREFIX
void vcardcomponent_remove_property(vcardcomponent *comp, vcardproperty *property)
#define UUID_PREFIX_LEN
void vcardcomponent_add_property(vcardcomponent *comp, vcardproperty *property)
void vcardcomponent_remove_component(vcardcomponent *parent, vcardcomponent *child)
bool vcardcomponent_is_valid(const vcardcomponent *component)
vcardcomponent_kind vcardcomponent_string_to_kind(const char *string)
enum vcardproperty_version vcardcomponent_get_version(vcardcomponent *comp)
vcardproperty * vcardcomponent_get_next_property(vcardcomponent *c, vcardproperty_kind kind)
bool vcardcomponent_kind_is_valid(const vcardcomponent_kind kind)
vcardcomponent * vcardcomponent_get_first_component(vcardcomponent *c, vcardcomponent_kind kind)
vcardcomponent_kind vcardcomponent_isa(const vcardcomponent *component)
int vcardcomponent_check_restrictions(vcardcomponent *comp)
void vcardcomponent_add_component(vcardcomponent *parent, vcardcomponent *child)
char * vcardcomponent_as_vcard_string_r(vcardcomponent *comp)
vcardcomponent * vcardcomponent_clone(const vcardcomponent *old)
Deeply clones an vcard. Returns a pointer to the memory for the newly cloned vcard.
const char * vcardcomponent_kind_to_string(vcardcomponent_kind kind)
vcardproperty ** vcardcomponent_get_properties(vcardcomponent *comp, vcardproperty_kind kind)
int vcardcomponent_count_errors(vcardcomponent *comp)
Returns the number of errors encountered parsing the data.
bool vcardcomponent_isa_component(const void *component)
const char * vcardcomponent_get_uid(vcardcomponent *comp)
int vcardcomponent_count_properties(vcardcomponent *comp, vcardproperty_kind kind, int ignore_alts)
void vcardcomponent_transform(vcardcomponent *impl, vcardproperty_version version)
void vcardcomponent_free(vcardcomponent *c)
char * vcardcomponent_as_vcard_string(vcardcomponent *comp)
vcardcomponent * vcardcomponent_vanew(vcardcomponent_kind kind,...)
Constructor.
vcardcomponent * vcardcomponent_get_current_component(vcardcomponent *component)
const char * vcardcomponent_get_fn(vcardcomponent *comp)
vcardcomponent * vcardcomponent_new(vcardcomponent_kind kind)
Constructor.
vcardcomponent * vcardcomponent_new_from_string(const char *str)
Constructor.
void vcardcomponent_normalize(vcardcomponent *comp)
Normalizes (reorders and sorts the properties) the specified vcard comp.
vcardcomponent * vcardcomponent_get_next_component(vcardcomponent *c, vcardcomponent_kind kind)
int vcardcomponent_count_components(vcardcomponent *component, vcardcomponent_kind kind)
vcardproperty * vcardcomponent_get_current_property(vcardcomponent *comp)
vcardproperty * vcardcomponent_get_first_property(vcardcomponent *c, vcardproperty_kind kind)
Defines the data structure representing vCard components.
vcardcomponent_kind
@ VCARD_NO_COMPONENT
@ VCARD_ANY_COMPONENT
@ VCARD_VCARD_COMPONENT
@ VCARD_XROOT_COMPONENT
vcardcomponent * vcardproperty_get_parent(const vcardproperty *property)
Returns the parent vcard for the specified property.
void vcardproperty_set_parent(vcardproperty *property, vcardcomponent *comp)
Sets the parent vcard for the specified vcardproperty property.
vcardparameter * vcardparameter_new_mediatype(const char *v)
const char * vcardparameter_get_mediatype(const vcardparameter *value)
struct vcardparameter_impl vcardparameter
vcardenumarray * vcardparameter_get_type(const vcardparameter *value)
int vcardparameter_get_pref(const vcardparameter *value)
vcardparameter * vcardparameter_new_encoding(vcardparameter_encoding v)
const char * vcardparameter_get_altid(const vcardparameter *value)
vcardparameter * vcardparameter_new_pref(int v)
@ VCARD_ENCODING_B
@ VCARD_ENCODING_PARAMETER
@ VCARD_TYPE_PARAMETER
@ VCARD_X_PARAMETER
@ VCARD_ALTID_PARAMETER
@ VCARD_PREF_PARAMETER
@ VCARD_VALUE_PARAMETER
@ VCARD_MEDIATYPE_PARAMETER
@ VCARD_TYPE_PREF
@ VCARD_TYPE_NONE
void vcardproperty_set_version(vcardproperty *prop, enum vcardproperty_version v)
const char * vcardproperty_get_fn(const vcardproperty *prop)
enum vcardproperty_version vcardproperty_get_version(const vcardproperty *prop)
enum vcardproperty_kindenum vcardproperty_get_kind(const vcardproperty *prop)
vcardtimetype vcardproperty_get_bday(const vcardproperty *prop)
vcardproperty_kind
@ VCARD_NO_PROPERTY
@ VCARD_X_PROPERTY
@ VCARD_TEL_PROPERTY
@ VCARD_XLICERROR_PROPERTY
@ VCARD_GEO_PROPERTY
@ VCARD_UID_PROPERTY
@ VCARD_VERSION_PROPERTY
@ VCARD_ANY_PROPERTY
@ VCARD_PHOTO_PROPERTY
@ VCARD_N_PROPERTY
@ VCARD_FN_PROPERTY
@ VCARD_LOGO_PROPERTY
@ VCARD_SOUND_PROPERTY
@ VCARD_KIND_PROPERTY
@ VCARD_DEATHDATE_PROPERTY
@ VCARD_NICKNAME_PROPERTY
@ VCARD_ANNIVERSARY_PROPERTY
@ VCARD_ORG_PROPERTY
@ VCARD_MEMBER_PROPERTY
@ VCARD_BDAY_PROPERTY
@ VCARD_KEY_PROPERTY
vcardproperty * vcardproperty_new_x(const char *v)
const char * vcardproperty_get_uid(const vcardproperty *prop)
void vcardproperty_set_bday(vcardproperty *prop, vcardtimetype v)
struct vcardvalue_impl vcardvalue
void vcardvalue_set_uri(vcardvalue *value, const char *v)
@ VCARD_KIND_INDIVIDUAL
void vcardvalue_set_geo(vcardvalue *value, vcardgeotype v)
vcardproperty_version
@ VCARD_VERSION_30
@ VCARD_VERSION_40
@ VCARD_VERSION_NONE
vcardvalue_kind
@ VCARD_URI_VALUE
@ VCARD_X_VALUE
@ VCARD_TEXT_VALUE
const char * vcardvalue_get_uri(const vcardvalue *value)
vcardgeotype vcardvalue_get_geo(const vcardvalue *value)
void vcardvalue_set_text(vcardvalue *value, const char *v)
const char * vcardvalue_get_text(const vcardvalue *value)
icalenumarray_element vcardenumarray_element
#define vcardenumarray_element_at(array, position)
#define vcardenumarray_size(array)
#define vcardenumarray_remove_element_at(array, position)
icalenumarray vcardenumarray
#define vcardenumarray_find(array, needle)
const char * vcardparameter_get_xname(const vcardparameter *param)
Returns the X-name of param.
vcardcomponent * vcardparser_parse_string(const char *str)
Line-oriented parsing vCard format.
vcardproperty * vcardproperty_new(vcardproperty_kind kind)
void vcardproperty_set_parameter_from_string(vcardproperty *prop, const char *name, const char *value)
void vcardproperty_set_x_name(vcardproperty *prop, const char *name)
void vcardproperty_free(vcardproperty *p)
void vcardproperty_set_value_from_string(vcardproperty *prop, const char *str, const char *type)
void vcardproperty_normalize(vcardproperty *prop)
const char * vcardproperty_get_value_as_string(const vcardproperty *prop)
void vcardproperty_remove_parameter_by_ref(vcardproperty *prop, vcardparameter *parameter)
Removes the specified parameter reference from the property.
const char * vcardproperty_get_x_name(const vcardproperty *prop)
int vcardproperty_count_parameters(const vcardproperty *prop)
bool vcardproperty_isa_property(void *property)
vcardparameter * vcardproperty_get_first_parameter(vcardproperty *p, vcardparameter_kind kind)
vcardvalue * vcardproperty_get_value(const vcardproperty *prop)
void vcardproperty_add_type_parameter(vcardproperty *prop, vcardenumarray_element *type)
vcardproperty * vcardproperty_clone(const vcardproperty *old)
Deeply clones an vcardproperty.
void vcardproperty_remove_parameter_by_kind(vcardproperty *prop, vcardparameter_kind kind)
Removes all parameters with the specified kind.
void vcardproperty_add_parameter(vcardproperty *p, vcardparameter *parameter)
char * vcardproperty_as_vcard_string_r(vcardproperty *prop)
vcardproperty_kind vcardproperty_isa(const vcardproperty *p)
vcardparameter * vcardproperty_get_next_parameter(vcardproperty *p, vcardparameter_kind kind)
vcardproperty_kind vcardproperty_string_to_kind(const char *string)
Functions to check if a vcardcomponent meets the restrictions imposed by the standard.
bool vcardrestriction_check(vcardcomponent *comp)
Checks if a given VCARD meets all the restrictions imposed by the standard.
#define vcardstrarray_find(array, needle)
#define vcardstrarray_size(array)
#define vcardstrarray_new(increment_size)
#define vcardstrarray_append(array, elem)
#define vcardstrarray_free(array)
icalstrarray vcardstrarray
#define VCARD_GEO_LEN
Definition vcardtypes.h:19
const char * vcardvalue_as_vcard_string(const vcardvalue *value)
Definition vcardvalue.c:876
vcardvalue_kind vcardvalue_isa(const vcardvalue *value)
Definition vcardvalue.c:982
Defines the data structure representing vCard values.