Libical API Documentation 4.0 UNRELEASED Go to the stable 3.0 documentation
Loading...
Searching...
No Matches
vcardparser.c
Go to the documentation of this file.
1/*======================================================================
2 FILE: vcardparser.c
3 CREATOR: Ken Murchison 24 Aug 2022 <murch@fastmailteam.com>
4 CONTRIBUTOR: Bron Gondwana <brong@fastmailteam.com>
5
6 SPDX-FileCopyrightText: 2022, Fastmail Pty. Ltd. (https://fastmail.com)
7 SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
8 ======================================================================*/
9
14
15#ifdef HAVE_CONFIG_H
16#include <config.h>
17#endif
18
19#include "vcardparser.h"
20#include "vcardcomponent.h"
21#include "vcardparameter.h"
22#include "vcardproperty.h"
23#include "vcardtextlist.h"
24#include "vcardvalue.h"
25#include "icalmemory.h"
26
27#include <ctype.h>
28#include <stdio.h>
29#include <stdarg.h>
30#include <stdlib.h>
31#include <string.h>
32
33#define DEBUG 0
34
35static ICAL_GLOBAL_VAR vcard_xprop_value_kind_func xprop_value_kind_func = NULL;
36static ICAL_GLOBAL_VAR void *xprop_value_kind_data = NULL;
37
38enum parse_error
39{
40 PE_OK = 0,
41 PE_BACKQUOTE_EOF,
42 PE_BEGIN_PARAMS,
43 PE_PROP_MULTIGROUP,
44 PE_FINISHED_EARLY,
45 PE_KEY_EOF,
46 PE_KEY_EOL,
47 PE_MISMATCHED_CARD,
48 PE_NAME_EOF,
49 PE_NAME_EOL,
50 PE_NAME_INVALID,
51 PE_PARAMVALUE_EOF,
52 PE_PARAMVALUE_EOL,
53 PE_QSTRING_EOF,
54 PE_QSTRING_EOL,
55 PE_QSTRING_EOV,
56 PE_VALUE_INVALID,
57 PE_ILLEGAL_CHAR,
58 PE_NUMERR /* last */
59};
60
61struct buf {
62 char *s;
63 size_t len;
64 size_t alloc;
65};
66#define BUF_INITIALIZER \
67 { \
68 NULL, 0, 0}
69
70struct vcardparser_state {
71 struct buf buf;
72 struct buf errbuf;
73 const char *base;
74 const char *itemstart;
75 const char *p;
76 vcardproperty *version;
77
78 /* current items */
79 vcardcomponent *root;
80 vcardcomponent *comp;
81 vcardproperty *prop;
82 vcardparameter *param;
83 vcardvalue_kind value_kind;
84};
85
86struct vcardparser_errorpos {
87 int startpos;
88 int startline;
89 int startchar;
90 int errorpos;
91 int errorline;
92 int errorchar;
93};
94
95#define BUF_GROW 128
96
97void buf_init(struct buf *buf, size_t size)
98{
99 buf->len = 0;
100 buf->alloc = size;
101 buf->s = icalmemory_new_buffer(buf->alloc);
102}
103
104static size_t buf_len(const struct buf *buf)
105{
106 return buf->len;
107}
108
109static void buf_reset(struct buf *buf)
110{
111 buf->len = 0;
112}
113
114static void buf_free(struct buf *buf)
115{
116 if (buf->s) {
118 buf->s = NULL;
119 }
120
121 buf->len = buf->alloc = 0;
122}
123
124static void _buf_putc(struct buf *buf, char c)
125{
126 char *pos = buf->s + buf->len;
127
128 icalmemory_append_char(&buf->s, &pos, &buf->alloc, c);
129}
130
131static void buf_putc(struct buf *buf, char c)
132{
133 _buf_putc(buf, c);
134
135 buf->len++;
136}
137
138static const char *buf_cstring(struct buf *buf)
139{
140 _buf_putc(buf, '\0');
141
142 return buf->s;
143}
144
145static void buf_trim(struct buf *buf)
146{
147 /* trim whitespace
148 *
149 * XXX we can do this within the existing buffer
150 * because the new string length <= old string length
151 */
152 const char *s = buf_cstring(buf);
153
154 for (buf->len = 0; *s; s++) {
155 if (isspace((int)*s)) {
156 continue;
157 }
158 buf->s[buf->len++] = *s;
159 }
160}
161
162#pragma GCC diagnostic push
163#pragma GCC diagnostic ignored "-Wformat-nonliteral"
164static void buf_vprintf(struct buf *buf, const char *fmt, va_list args)
165{
166 va_list ap;
167 size_t size, n;
168
169 /* Copy args in case we have to try again */
170 va_copy(ap, args);
171
172 size = buf->alloc - buf->len;
173 n = (size_t)vsnprintf(buf->s + buf->len, size, fmt, args);
174
175 if (n >= size) {
176 /* Grow the buffer and try again */
177 size = n + BUF_GROW;
178 buf->alloc += size;
179 buf->s = icalmemory_resize_buffer(buf->s, buf->alloc);
180
181 n = (size_t)vsnprintf(buf->s + buf->len, size, fmt, ap);
182 }
183 va_end(ap);
184
185 buf->len += n;
186}
187#pragma GCC diagnostic pop
188
189#define NOTESTART() state->itemstart = state->p
190#define MAKE(X, Y) X = icalmemory_new_buffer(sizeof(struct Y))
191#define PUTC(C) buf_putc(&state->buf, C)
192#define INC(I) state->p += I
193#define IS_CTRL(ch) \
194 ((ch) > 0 && (ch) <= 0x1f && (ch) != '\r' && (ch) != '\n' && (ch) != '\t')
195#define HANDLECTRL(state) \
196 { \
197 if (IS_CTRL(*(state)->p)) { \
198 while (IS_CTRL(*(state)->p)) \
199 (state)->p++; \
200 } \
201 if ((*(state)->p) == 0) \
202 break; \
203 }
204
205static int _parse_param_name(struct vcardparser_state *state)
206{
207 vcardparameter_kind kind;
208 const char *name;
209
210 while (*state->p) {
211 /* Handle control characters and break for NUL char */
212 HANDLECTRL(state);
213
214 switch (*state->p) {
215 case '=':
216 name = buf_cstring(&state->buf);
218 state->param = vcardparameter_new(kind);
219 if (kind == VCARD_X_PARAMETER) {
220 vcardparameter_set_xname(state->param, name);
221 } else if (kind == VCARD_IANA_PARAMETER) {
222 vcardparameter_set_iana_name(state->param, name);
223 }
224 buf_reset(&state->buf);
225 INC(1);
226 return 0;
227
228 case ';': /* vCard 2.1 parameter with just a value */
229 case ':':
230 state->param = vcardparameter_new(VCARD_TYPE_PARAMETER);
231 /* no INC - we need to see this char up a layer */
232 return 0;
233
234 case '\r':
235 INC(1);
236 break; /* just skip */
237 case '\n':
238 if (state->p[1] != ' ' && state->p[1] != '\t') {
239 return PE_KEY_EOL;
240 }
241 INC(2);
242 break;
243
244 /* XXX - check exact legal set? */
245 default:
246 PUTC(*state->p);
247 INC(1);
248 break;
249 }
250 }
251
252 return PE_KEY_EOF;
253}
254
255/* just leaves it on the buffer */
256static int _parse_param_quoted(struct vcardparser_state *state,
257 bool is_structured, bool is_multivalued)
258{
259 NOTESTART();
260
261 while (*state->p) {
262 /* Handle control characters and break for NUL char */
263 HANDLECTRL(state);
264
265 switch (*state->p) {
266 case '"':
267 INC(1);
268 return 0;
269
270 /* normal backslash quoting - NOTE, not strictly RFC compliant,
271 * but I figure anyone who generates one PROBABLY meant to escape
272 * the next character because it's so common, and LABEL definitely
273 * allows \n, so we have to handle that anyway */
274 case '\\':
275 /* seen in the wild - \n split by line wrapping */
276 if (state->p[1] == '\r') {
277 INC(1);
278 }
279 if (state->p[1] == '\n') {
280 if (state->p[2] != ' ' && state->p[2] != '\t') {
281 return PE_QSTRING_EOL;
282 }
283 INC(2);
284 }
285 if (!state->p[1]) {
286 return PE_BACKQUOTE_EOF;
287 }
288 if (state->p[1] == 'n' || state->p[1] == 'N') {
289 PUTC('\n');
290 } else if (is_structured && strchr(";,", state->p[1])) {
291 // preserve escaped COMMA and SEMICOLON in structured value
292 PUTC(state->p[0]);
293 PUTC(state->p[1]);
294 } else {
295 PUTC(state->p[1]);
296 }
297 INC(2);
298 break;
299
300 /* special value quoting for doublequote and endline (RFC 6868) */
301 case '^':
302 if (state->p[1] == '\r') {
303 INC(1);
304 }
305 if (state->p[1] == '\n') {
306 if (state->p[2] != ' ' && state->p[2] != '\t') {
307 return PE_QSTRING_EOL;
308 }
309 INC(2);
310 }
311 if (state->p[1] == '\'') {
312 PUTC('"');
313 INC(2);
314 } else if (state->p[1] == 'n') { /* only lower case per the RFC */
315 PUTC('\n');
316 INC(2);
317 } else if (state->p[1] == '^') {
318 PUTC('^');
319 INC(2);
320 } else {
321 PUTC('^');
322 INC(1); /* treat next char normally */
323 }
324 break;
325
326 case '\r':
327 INC(1);
328 break; /* just skip */
329 case '\n':
330 if (state->p[1] != ' ' && state->p[1] != '\t') {
331 return PE_QSTRING_EOL;
332 }
333 INC(2);
334 break;
335
336 case ',':
337 if (is_multivalued && !is_structured) {
338 return PE_QSTRING_EOV;
339 }
340 /* or fall through, comma isn't special */
341 _fallthrough();
342
343 default:
344 PUTC(*state->p);
345 INC(1);
346 break;
347 }
348 }
349
350 return PE_QSTRING_EOF;
351}
352
353static int _parse_param_value(struct vcardparser_state *state)
354{
355 bool is_multivalued = vcardparameter_is_multivalued(state->param);
356 bool is_structured = vcardparameter_is_structured(state->param);
357 int r;
358
359 while (*state->p) {
360 /* Handle control characters and break for NUL char */
361 HANDLECTRL(state);
362
363 switch (*state->p) {
364 case '\\': /* normal backslash quoting */
365 /* seen in the wild - \n split by line wrapping */
366 if (state->p[1] == '\r') {
367 INC(1);
368 }
369 if (state->p[1] == '\n') {
370 if (state->p[2] != ' ' && state->p[2] != '\t') {
371 return PE_PARAMVALUE_EOL;
372 }
373 INC(2);
374 }
375 if (!state->p[1]) {
376 return PE_BACKQUOTE_EOF;
377 }
378 if (state->p[1] == 'n' || state->p[1] == 'N') {
379 PUTC('\n');
380 } else {
381 PUTC(state->p[1]);
382 }
383 INC(2);
384 break;
385
386 case '^': /* special value quoting for doublequote (RFC 6868) */
387 /* seen in the wild - \n split by line wrapping */
388 if (state->p[1] == '\r') {
389 INC(1);
390 }
391 if (state->p[1] == '\n') {
392 if (state->p[2] != ' ' && state->p[2] != '\t') {
393 return PE_PARAMVALUE_EOL;
394 }
395 INC(2);
396 }
397 if (state->p[1] == '\'') {
398 PUTC('"');
399 INC(2);
400 } else if (state->p[1] == 'n') {
401 PUTC('\n');
402 INC(2);
403 } else if (state->p[1] == '^') {
404 PUTC('^');
405 INC(2);
406 } else {
407 PUTC('^');
408 INC(1); /* treat next char normally */
409 }
410 break;
411
412 case '"':
413 INC(1);
414 while ((r = _parse_param_quoted(state,
415 is_structured,
416 is_multivalued)) == PE_QSTRING_EOV) {
417 vcardparameter_add_value_from_string(state->param,
418 buf_cstring(&state->buf));
419
420 buf_reset(&state->buf);
421 INC(1);
422 }
423 if (r) {
424 return r;
425 }
426 break;
427
428 case ':':
429 case ';':
430 /* done - end of parameter */
431 if (is_multivalued) {
432 vcardparameter_add_value_from_string(state->param,
433 buf_cstring(&state->buf));
434 } else {
435 vcardparameter_set_value_from_string(state->param,
436 buf_cstring(&state->buf));
437
438 /* if it is a VALUE parameter, set the value kind */
439 if (vcardparameter_isa(state->param) == VCARD_VALUE_PARAMETER) {
440 vcardvalue_kind kind =
441 vcardvalue_string_to_kind(buf_cstring(&state->buf));
442 if (kind != VCARD_NO_VALUE) {
443 state->value_kind = kind;
444 }
445 }
446 }
447
448 buf_reset(&state->buf);
449 /* no INC - we need to see this char up a layer */
450 return 0;
451
452 case '\r':
453 INC(1);
454 break; /* just skip */
455
456 case '\n':
457 if (state->p[1] != ' ' && state->p[1] != '\t') {
458 return PE_PARAMVALUE_EOL;
459 }
460 INC(2);
461 break;
462
463 case ',':
464 if (is_multivalued) {
465 vcardparameter_add_value_from_string(state->param,
466 buf_cstring(&state->buf));
467 buf_reset(&state->buf);
468 INC(1);
469 break;
470 }
471 /* or fall through, comma isn't special */
472 _fallthrough();
473
474 default:
475 PUTC(*state->p);
476 INC(1);
477 break;
478 }
479 }
480
481 return PE_PARAMVALUE_EOF;
482}
483
484static void _parse_error(struct vcardparser_state *state,
485 enum vcardparameter_xlicerrortype type,
486 const char *fmt, ...)
487{
488 va_list ap;
489
490 va_start(ap, fmt);
491 buf_reset(&state->errbuf);
492 buf_vprintf(&state->errbuf, fmt, ap);
493 va_end(ap);
494
495 if (state->prop) {
496 vcardproperty_free(state->prop);
497 }
498 state->version = NULL;
499
500 /* coverity[resource_leak] */
501 vcardparameter *errParam = vcardparameter_new_xlicerrortype(type);
502 state->prop = vcardproperty_vanew_xlicerror(buf_cstring(&state->errbuf),
503 errParam,
504 (void *)0);
505 buf_reset(&state->buf);
506}
507
508static int _parse_prop_params(struct vcardparser_state *state)
509{
510 vcardproperty_kind prop_kind = vcardproperty_isa(state->prop);
511 const char *group = vcardproperty_get_group(state->prop);
512
513 do {
514 int r;
515
516 INC(1); /* skip ';' */
517
518 NOTESTART();
519
520 /* get the name */
521 r = _parse_param_name(state);
522 if (r) {
523 _parse_error(state,
524 VCARD_XLICERRORTYPE_PARAMETERNAMEPARSEERROR,
525 "%s '%s' in %s%s%s property. Removing entire property",
526 vcardparser_errstr(r), buf_cstring(&state->buf),
527 group ? group : "", group ? "." : "",
528 vcardproperty_kind_to_string(prop_kind));
529 return r;
530 }
531
532 vcardproperty_add_parameter(state->prop, state->param);
533
534 /* now get the value */
535 r = _parse_param_value(state);
536 if (r) {
537 vcardparameter_kind param_kind = vcardparameter_isa(state->param);
538
539 _parse_error(state,
540 VCARD_XLICERRORTYPE_PARAMETERVALUEPARSEERROR,
541 "%s for %s in %s%s%s property. Removing entire property",
542 vcardparser_errstr(r),
544 group ? group : "", group ? "." : "",
545 vcardproperty_kind_to_string(prop_kind));
546 return r;
547 }
548
549 } while (*state->p == ';'); /* another parameter to parse */
550
551 return 0;
552}
553
554static int _parse_prop_name(struct vcardparser_state *state)
555{
556 const char *name;
557 const char *group = NULL;
558 vcardproperty_kind kind;
559 vcardproperty_version version = VCARD_VERSION_NONE;
560 int r = 0;
561
562 NOTESTART();
563
564 while (*state->p) {
565 /* Handle control characters and break for NUL char */
566 HANDLECTRL(state);
567
568 switch (*state->p) {
569 case ':':
570 case ';':
571 name = buf_cstring(&state->buf);
572 kind = vcardproperty_string_to_kind(name);
573
574 state->prop = vcardproperty_new(kind);
575 if (!state->prop) {
576 return PE_NAME_INVALID;
577 }
578
579 if (kind == VCARD_X_PROPERTY) {
580 vcardproperty_set_x_name(state->prop, name);
581 }
582
583 if (group) {
584 vcardproperty_set_group(state->prop, group);
585 }
586
587 if (state->version) {
588 version = vcardproperty_get_version(state->version);
589 }
590
591 /* set default value kind */
592 switch (kind) {
593 case VCARD_GEO_PROPERTY:
594 state->value_kind = version == VCARD_VERSION_40 ? VCARD_URI_VALUE : VCARD_GEO_VALUE;
595 break;
596
597 case VCARD_KEY_PROPERTY:
598 case VCARD_LOGO_PROPERTY:
599 case VCARD_PHOTO_PROPERTY:
600 case VCARD_SOUND_PROPERTY:
601 state->value_kind = version == VCARD_VERSION_40 ? VCARD_URI_VALUE : VCARD_TEXT_VALUE;
602 break;
603
604 case VCARD_TZ_PROPERTY:
605 state->value_kind = version == VCARD_VERSION_40 ? VCARD_TEXT_VALUE : VCARD_UTCOFFSET_VALUE;
606 break;
607
608 case VCARD_UID_PROPERTY:
609 state->value_kind = version == VCARD_VERSION_40 ? VCARD_URI_VALUE : VCARD_TEXT_VALUE;
610 break;
611
612 case VCARD_X_PROPERTY:
613 state->value_kind =
614 xprop_value_kind_func ? xprop_value_kind_func(name, xprop_value_kind_data)
615 : VCARD_X_VALUE;
616 break;
617
618 default:
619 state->value_kind = vcardproperty_kind_to_value_kind(kind);
620 break;
621 }
622
623 buf_reset(&state->buf);
624
625 /* no INC - we need to see this char up a layer */
626 return r;
627
628 case '.':
629 if (group) {
630 size_t tmpLen = strlen(group) + buf_len(&state->buf) + 2;
631 char *tmp = icalmemory_tmp_buffer(tmpLen);
632 snprintf(tmp, tmpLen - 1, "%s.%s", group, buf_cstring(&state->buf));
633 group = tmp;
634 r = PE_PROP_MULTIGROUP;
635 } else {
636 group = icalmemory_tmp_copy(buf_cstring(&state->buf));
637 }
638 buf_reset(&state->buf);
639 INC(1);
640 break;
641
642 case '\r':
643 INC(1);
644 break; /* just skip */
645 case '\n':
646 if (state->p[1] == ' ' || state->p[1] == '\t') { /* wrapped line */
647 INC(2);
648 } else if (!state->buf.len) {
649 /* no key yet? blank intermediate lines are OK */
650 INC(1);
651 } else {
652 return PE_NAME_EOL;
653 }
654 break;
655
656 default:
657 PUTC(*state->p);
658 INC(1);
659 break;
660 }
661 }
662
663 return PE_NAME_EOF;
664}
665
666static int _parse_prop_value(struct vcardparser_state *state)
667{
668 vcardproperty_kind prop_kind = vcardproperty_isa(state->prop);
669 vcardvalue *value = NULL;
670
671 NOTESTART();
672
673 while (*state->p) {
674 /* Handle control characters and break for NUL char */
675 HANDLECTRL(state);
676
677 switch (*state->p) {
678 case '\\':
679 /* seen in the wild - \n split by line wrapping */
680 while (state->p[1] && strchr("\r\n", state->p[1])) {
681 if (state->p[1] == '\r') {
682 INC(1);
683 }
684 if (state->p[1] == '\n') {
685 if (state->p[2] != ' ' && state->p[2] != '\t') {
686 /* ignore unescaped backslash at end of line */
687 INC(2);
688 goto out;
689 }
690 INC(2);
691 }
692 }
693 if (state->p[1]) {
694 /* preserve escape sequences */
695 PUTC('\\');
696 PUTC(state->p[1]);
697 INC(2);
698 } else {
699 /* ignore unescaped backslash at end of line */
700 INC(1);
701 goto out;
702 }
703 break;
704 case '\r':
705 INC(1);
706 break; /* just skip */
707 case '\n':
708 if (state->p[1] == ' ' || state->p[1] == '\t') { /* wrapped line */
709 INC(2);
710 break;
711 }
712 /* otherwise it's the end of the value */
713 INC(1);
714 goto out;
715
716 default:
717 PUTC(*state->p);
718 INC(1);
719 break;
720 }
721 }
722
723out:
724 /* reaching the end of the file isn't a failure here,
725 * it's just another type of end-of-value */
726
727 /* repair critical property values */
728 if (prop_kind == VCARD_VERSION_PROPERTY) {
729 buf_trim(&state->buf);
730 state->version = state->prop;
731 }
732
733 if (state->value_kind == VCARD_TEXTLIST_VALUE) {
734 char sep = vcardproperty_is_structured(prop_kind) ? ';' : ',';
735 vcardstrarray *textlist =
736 vcardtextlist_new_from_string(buf_cstring(&state->buf), sep);
737 if (textlist) {
738 value = vcardvalue_new_textlist(textlist);
739 }
740 } else {
741 value = vcardvalue_new_from_string(state->value_kind,
742 buf_cstring(&state->buf));
743 }
744
745 if (!value) {
746 return PE_VALUE_INVALID;
747 }
748
749 vcardproperty_set_value(state->prop, value);
750 buf_reset(&state->buf);
751
752 return 0;
753}
754
755static void _parse_eatline(struct vcardparser_state *state)
756{
757 while (*state->p) {
758 /* Handle control characters and break for NUL char */
759 HANDLECTRL(state);
760
761 switch (*state->p) {
762 case '\n':
763 if (state->p[1] == ' ' || state->p[1] == '\t') { /* wrapped line */
764 INC(2);
765 break;
766 }
767 /* otherwise it's the end of the line */
768 INC(1);
769 return;
770
771 default:
772 INC(1);
773 break;
774 }
775 }
776}
777
778static void _parse_prop(struct vcardparser_state *state)
779{
780 int r = _parse_prop_name(state);
781 if (r) {
782 if (r == PE_PROP_MULTIGROUP) {
783 vcardproperty_kind prop_kind = vcardproperty_isa(state->prop);
784
785 _parse_error(state,
786 VCARD_XLICERRORTYPE_PROPERTYPARSEERROR,
787 "%s '%s.%s'. Removing entire property",
788 vcardparser_errstr(r),
789 vcardproperty_get_group(state->prop),
790 vcardproperty_kind_to_string(prop_kind));
791 _parse_eatline(state);
792 } else if (r == PE_NAME_INVALID) {
793 _parse_error(state,
794 VCARD_XLICERRORTYPE_PROPERTYPARSEERROR,
795 "%s '%s'. Removing entire property",
796 vcardparser_errstr(r), buf_cstring(&state->buf));
797 _parse_eatline(state);
798 } else {
799 _parse_error(state,
800 VCARD_XLICERRORTYPE_PROPERTYPARSEERROR,
801 "%s '%s'. Ignoring property",
802 vcardparser_errstr(r), buf_cstring(&state->buf));
803 }
804 return;
805 }
806
807 if (*state->p == ';') {
808 r = _parse_prop_params(state);
809 if (r) {
810 /* errors handled in _parse_prop_params() */
811 return;
812 }
813 }
814
815 INC(1); /* skip ':' */
816 r = _parse_prop_value(state);
817 if (r) {
818 vcardproperty_kind prop_kind = vcardproperty_isa(state->prop);
819 const char *group = vcardproperty_get_group(state->prop);
820
821 if (r == PE_VALUE_INVALID) {
822 _parse_error(state,
823 VCARD_XLICERRORTYPE_VALUEPARSEERROR,
824 "Error parsing '%s' as %s value in %s%s%s property."
825 " Removing entire property",
826 buf_cstring(&state->buf),
827 vcardvalue_kind_to_string(state->value_kind),
828 group ? group : "", group ? "." : "",
829 vcardproperty_kind_to_string(prop_kind));
830 } else {
831 _parse_error(state,
832 VCARD_XLICERRORTYPE_VALUEPARSEERROR,
833 "%s in %s%s%s property. Removing entire property",
834 vcardparser_errstr(r),
835 group ? group : "", group ? "." : "",
836 vcardproperty_kind_to_string(prop_kind));
837 }
838 }
839}
840
841static int _parse_vcard(struct vcardparser_state *state,
842 vcardcomponent *comp, int only_one)
843{
844 vcardcomponent *sub;
845 const char *cardstart = state->p;
846 int r = 0;
847
848 while (*state->p) {
849 /* whitespace is very skippable before AND afterwards */
850 if (*state->p == '\r' || *state->p == '\n' ||
851 *state->p == ' ' || *state->p == '\t') {
852 INC(1);
853 continue;
854 }
855
856 _parse_prop(state);
857
858 if (vcardproperty_isa(state->prop) == VCARD_BEGIN_PROPERTY) {
859 if (vcardvalue_isa(vcardproperty_get_value(state->prop)) !=
860 VCARD_TEXT_VALUE) {
861 r = PE_VALUE_INVALID;
862 break;
863 }
864
865 const char *val =
866 vcardvalue_get_text(vcardproperty_get_value(state->prop));
867 vcardcomponent_kind kind = vcardcomponent_string_to_kind(val);
868
869 if (kind == VCARD_NO_COMPONENT) {
870 state->itemstart = cardstart;
871 r = PE_MISMATCHED_CARD;
872 break;
873 }
874
875 vcardproperty_free(state->prop);
876 state->prop = NULL;
877
878 sub = vcardcomponent_new(kind);
879 vcardcomponent_add_component(comp, sub);
880 r = _parse_vcard(state, sub, /*only_one*/ 0);
881 if (r || only_one) {
882 break;
883 }
884 } else if (!comp) {
885 /* no comp means we're at the top level, haven't seen a BEGIN! */
886 state->itemstart = cardstart;
887 r = PE_MISMATCHED_CARD;
888 break;
889 } else if (vcardproperty_isa(state->prop) == VCARD_END_PROPERTY) {
890 if (vcardvalue_isa(vcardproperty_get_value(state->prop)) !=
891 VCARD_TEXT_VALUE) {
892 r = PE_VALUE_INVALID;
893 break;
894 }
895
896 const char *val =
897 vcardvalue_get_text(vcardproperty_get_value(state->prop));
898 vcardcomponent_kind kind = vcardcomponent_string_to_kind(val);
899
900 if (kind != vcardcomponent_isa(comp)) {
901 /* special case mismatched card, the "start" was the start of
902 * the card */
903 state->itemstart = cardstart;
904 r = PE_MISMATCHED_CARD;
905 }
906
907 break;
908 } else {
909 vcardcomponent_add_property(comp, state->prop);
910 state->prop = NULL;
911 }
912 }
913
914 if (state->prop) {
915 if (vcardproperty_isa(state->prop) != VCARD_END_PROPERTY) {
916 r = PE_FINISHED_EARLY;
917 }
918
919 vcardproperty_free(state->prop);
920 state->prop = NULL;
921 }
922
923 return r;
924}
925
926static int vcardparser_parse(struct vcardparser_state *state, int only_one)
927{
928 state->root = vcardcomponent_new(VCARD_XROOT_COMPONENT);
929
930 state->p = state->base;
931
932 buf_init(&state->buf, BUF_GROW);
933 buf_init(&state->errbuf, BUF_GROW);
934
935 /* don't parse trailing non-whitespace */
936 return _parse_vcard(state, state->root, only_one);
937}
938
939/* FREE MEMORY */
940
941static void _free_state(struct vcardparser_state *state)
942{
943 buf_free(&state->buf);
944 buf_free(&state->errbuf);
945
946 if (state->root) {
947 vcardcomponent_free(state->root);
948 }
949
950 memset(state, 0, sizeof(struct vcardparser_state));
951}
952
953static void vcardparser_free(struct vcardparser_state *state)
954{
955 _free_state(state);
956}
957
958/* PUBLIC API */
959
960const char *vcardparser_errstr(int err)
961{
962 switch (err) {
963 case PE_BACKQUOTE_EOF:
964 return "EOF after backslash";
965 case PE_BEGIN_PARAMS:
966 return "Params on BEGIN field";
967 case PE_PROP_MULTIGROUP:
968 return "Multiple group levels in property name";
969 case PE_FINISHED_EARLY:
970 return "vCard not completed";
971 case PE_KEY_EOF:
972 return "End of data while parsing parameter key";
973 case PE_KEY_EOL:
974 return "End of line while parsing parameter key";
975 case PE_MISMATCHED_CARD:
976 return "Closed a different card name than opened";
977 case PE_NAME_EOF:
978 return "End of data while parsing property name";
979 case PE_NAME_EOL:
980 return "End of line while parsing property name";
981 case PE_NAME_INVALID:
982 return "Invalid property name";
983 case PE_PARAMVALUE_EOF:
984 return "End of data while parsing parameter value";
985 case PE_PARAMVALUE_EOL:
986 return "End of line while parsing parameter value";
987 case PE_QSTRING_EOF:
988 return "End of data while parsing quoted value";
989 case PE_QSTRING_EOL:
990 return "End of line while parsing quoted value";
991 case PE_QSTRING_EOV:
992 return "End of line while parsing multi or structured value";
993 case PE_VALUE_INVALID:
994 return "Invalid value for property";
995 case PE_ILLEGAL_CHAR:
996 return "Illegal character in vCard";
997 case PE_NUMERR:
998 default:
999 return "Unknown error";
1000 }
1001}
1002
1003vcardcomponent *vcardparser_parse_string(const char *str)
1004{
1005 struct vcardparser_state parser;
1006 vcardcomponent *vcard = NULL;
1007 int r;
1008
1009 memset(&parser, 0, sizeof(struct vcardparser_state));
1010
1011 parser.base = str;
1012 r = vcardparser_parse(&parser, 0);
1013 if (!r) {
1014 if (vcardcomponent_count_components(parser.root,
1015 VCARD_VCARD_COMPONENT) == 1) {
1016 vcard = vcardcomponent_get_first_component(parser.root,
1017 VCARD_VCARD_COMPONENT);
1018 vcardcomponent_remove_component(parser.root, vcard);
1019 } else {
1020 vcard = parser.root;
1021 parser.root = NULL;
1022 }
1023 }
1024
1025 vcardparser_free(&parser);
1026
1027 return vcard;
1028}
1029
1031{
1032 xprop_value_kind_func = func;
1033 xprop_value_kind_data = data;
1034}
void icalmemory_free_buffer(void *buf)
Releases a buffer.
Definition icalmemory.c:353
void * icalmemory_resize_buffer(void *buf, size_t size)
Resizes a buffer created with icalmemory_new_buffer().
Definition icalmemory.c:334
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
Definition icalmemory.c:313
char * icalmemory_tmp_copy(const char *str)
Creates a copy of the given string, stored on the ring buffer, and returns it.
Definition icalmemory.c:220
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_tmp_buffer(size_t size)
Creates a new temporary buffer on the ring and returns it.
Definition icalmemory.c:180
Common memory management routines.
vcardcomponent * vcardcomponent_new(vcardcomponent_kind kind)
Constructor.
Defines the data structure representing vCard components.
void vcardparameter_set_xname(vcardparameter *param, const char *v)
Sets the X-name of param to v.
vcardparameter_kind vcardparameter_isa(const vcardparameter *parameter)
void vcardparameter_set_iana_name(vcardparameter *param, const char *v)
Sets the IANA name of param to v.
vcardparameter * vcardparameter_new(vcardparameter_kind kind)
Creates new vcardparameter object.
Defines the data structure representing vCard parameters.
const char * vcardparameter_kind_to_string(vcardparameter_kind kind)
Returns a string representing the given vcardparameter_kind.
vcardparameter_kind vcardparameter_string_to_kind(const char *string)
Returns the vcardparameter_kind for a given string.
void vcardparser_set_xprop_value_kind(vcard_xprop_value_kind_func func, void *data)
Registers a parser callback to override the default value type of an x-property.
Line-oriented parsing vCard format.
vcardvalue_kind(* vcard_xprop_value_kind_func)(const char *name, void *data)
Callback function pointer to define x-property default value types.
Definition vcardparser.h:39
Defines the data structure representing vCard properties.
Defines functions for creating vCard text lists.
Defines the data structure representing vCard values.