36static ICAL_GLOBAL_VAR
void *xprop_value_kind_data = NULL;
66#define BUF_INITIALIZER \
70struct vcardparser_state {
74 const char *itemstart;
76 vcardproperty *version;
79 vcardcomponent *xroot;
80 vcardcomponent *vcard;
82 vcardparameter *param;
83 vcardvalue_kind value_kind;
86struct vcardparser_errorpos {
97void buf_init(
struct buf *buf,
size_t size)
104static size_t buf_len(
const struct buf *buf)
109static void buf_reset(
struct buf *buf)
114static void buf_free(
struct buf *buf)
121 buf->len = buf->alloc = 0;
124static void _buf_putc(
struct buf *buf,
char c)
126 char *pos = buf->s + buf->len;
131static void buf_putc(
struct buf *buf,
char c)
138static const char *buf_cstring(
struct buf *buf)
140 _buf_putc(buf,
'\0');
145static void buf_trim(
struct buf *buf)
152 const char *s = buf_cstring(buf);
154 for (buf->len = 0; *s; s++) {
155 if (isspace((
int)*s)) {
158 buf->s[buf->len++] = *s;
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)
172 size = buf->alloc - buf->len;
173 n = (size_t)vsnprintf(buf->s + buf->len, size, fmt, args);
181 n = (size_t)vsnprintf(buf->s + buf->len, size, fmt, ap);
187#pragma GCC diagnostic pop
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
194 ((ch) > 0 && (ch) <= 0x1f && (ch) != '\r' && (ch) != '\n' && (ch) != '\t')
195#define HANDLECTRL(state) \
197 if (IS_CTRL(*(state)->p)) { \
198 while (IS_CTRL(*(state)->p)) \
201 if ((*(state)->p) == 0) \
205static int _parse_param_name(
struct vcardparser_state *state)
207 vcardparameter_kind kind;
216 name = buf_cstring(&state->buf);
219 if (kind == VCARD_X_PARAMETER) {
221 }
else if (kind == VCARD_IANA_PARAMETER) {
224 buf_reset(&state->buf);
238 if (state->p[1] !=
' ' && state->p[1] !=
'\t') {
256static int _parse_param_quoted(
struct vcardparser_state *state,
257 bool is_structured,
bool is_multivalued)
276 if (state->p[1] ==
'\r') {
279 if (state->p[1] ==
'\n') {
280 if (state->p[2] !=
' ' && state->p[2] !=
'\t') {
281 return PE_QSTRING_EOL;
286 return PE_BACKQUOTE_EOF;
288 if (state->p[1] ==
'n' || state->p[1] ==
'N') {
290 }
else if (is_structured && strchr(
";,", state->p[1])) {
302 if (state->p[1] ==
'\r') {
305 if (state->p[1] ==
'\n') {
306 if (state->p[2] !=
' ' && state->p[2] !=
'\t') {
307 return PE_QSTRING_EOL;
311 if (state->p[1] ==
'\'') {
314 }
else if (state->p[1] ==
'n') {
317 }
else if (state->p[1] ==
'^') {
330 if (state->p[1] !=
' ' && state->p[1] !=
'\t') {
331 return PE_QSTRING_EOL;
337 if (is_multivalued && !is_structured) {
338 return PE_QSTRING_EOV;
350 return PE_QSTRING_EOF;
353static int _parse_param_value(
struct vcardparser_state *state)
355 bool is_multivalued = vcardparameter_is_multivalued(state->param);
356 bool is_structured = vcardparameter_is_structured(state->param);
366 if (state->p[1] ==
'\r') {
369 if (state->p[1] ==
'\n') {
370 if (state->p[2] !=
' ' && state->p[2] !=
'\t') {
371 return PE_PARAMVALUE_EOL;
376 return PE_BACKQUOTE_EOF;
378 if (state->p[1] ==
'n' || state->p[1] ==
'N') {
388 if (state->p[1] ==
'\r') {
391 if (state->p[1] ==
'\n') {
392 if (state->p[2] !=
' ' && state->p[2] !=
'\t') {
393 return PE_PARAMVALUE_EOL;
397 if (state->p[1] ==
'\'') {
400 }
else if (state->p[1] ==
'n') {
403 }
else if (state->p[1] ==
'^') {
414 while ((r = _parse_param_quoted(state,
416 is_multivalued)) == PE_QSTRING_EOV) {
417 vcardparameter_add_value_from_string(state->param,
418 buf_cstring(&state->buf));
420 buf_reset(&state->buf);
431 if (is_multivalued) {
432 vcardparameter_add_value_from_string(state->param,
433 buf_cstring(&state->buf));
435 vcardparameter_set_value_from_string(state->param,
436 buf_cstring(&state->buf));
440 vcardvalue_kind kind =
441 vcardvalue_string_to_kind(buf_cstring(&state->buf));
442 if (kind != VCARD_NO_VALUE) {
443 state->value_kind = kind;
448 buf_reset(&state->buf);
457 if (state->p[1] !=
' ' && state->p[1] !=
'\t') {
458 return PE_PARAMVALUE_EOL;
464 if (is_multivalued) {
465 vcardparameter_add_value_from_string(state->param,
466 buf_cstring(&state->buf));
467 buf_reset(&state->buf);
481 return PE_PARAMVALUE_EOF;
484static void _parse_error(
struct vcardparser_state *state,
485 enum vcardparameter_xlicerrortype type,
486 const char *fmt, ...)
491 buf_reset(&state->errbuf);
492 buf_vprintf(&state->errbuf, fmt, ap);
496 vcardproperty_free(state->prop);
498 state->version = NULL;
501 vcardparameter *errParam = vcardparameter_new_xlicerrortype(type);
502 state->prop = vcardproperty_vanew_xlicerror(buf_cstring(&state->errbuf),
505 buf_reset(&state->buf);
508static int _parse_prop_params(
struct vcardparser_state *state)
510 vcardproperty_kind prop_kind = vcardproperty_isa(state->prop);
511 const char *group = vcardproperty_get_group(state->prop);
521 r = _parse_param_name(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));
532 vcardproperty_add_parameter(state->prop, state->param);
535 r = _parse_param_value(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));
549 }
while (*state->p ==
';');
554static int _parse_prop_name(
struct vcardparser_state *state)
557 const char *group = NULL;
558 vcardproperty_kind kind;
559 vcardproperty_version version = VCARD_VERSION_NONE;
571 name = buf_cstring(&state->buf);
572 kind = vcardproperty_string_to_kind(name);
574 state->prop = vcardproperty_new(kind);
576 return PE_NAME_INVALID;
579 if (kind == VCARD_X_PROPERTY) {
580 vcardproperty_set_x_name(state->prop, name);
584 vcardproperty_set_group(state->prop, group);
587 if (state->version) {
588 version = vcardproperty_get_version(state->version);
593 case VCARD_GEO_PROPERTY:
594 state->value_kind = version == VCARD_VERSION_40 ? VCARD_URI_VALUE : VCARD_GEO_VALUE;
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;
604 case VCARD_TZ_PROPERTY:
605 state->value_kind = version == VCARD_VERSION_40 ? VCARD_TEXT_VALUE : VCARD_UTCOFFSET_VALUE;
608 case VCARD_UID_PROPERTY:
609 state->value_kind = version == VCARD_VERSION_40 ? VCARD_URI_VALUE : VCARD_TEXT_VALUE;
612 case VCARD_X_PROPERTY:
614 xprop_value_kind_func ? xprop_value_kind_func(name, xprop_value_kind_data)
619 state->value_kind = vcardproperty_kind_to_value_kind(kind);
623 buf_reset(&state->buf);
630 size_t tmpLen = strlen(group) + buf_len(&state->buf) + 2;
632 snprintf(tmp, tmpLen - 1,
"%s.%s", group, buf_cstring(&state->buf));
634 r = PE_PROP_MULTIGROUP;
638 buf_reset(&state->buf);
646 if (state->p[1] ==
' ' || state->p[1] ==
'\t') {
648 }
else if (!state->buf.len) {
666static int _parse_prop_value(
struct vcardparser_state *state)
668 vcardproperty_kind prop_kind = vcardproperty_isa(state->prop);
669 vcardvalue *value = NULL;
680 while (state->p[1] && strchr(
"\r\n", state->p[1])) {
681 if (state->p[1] ==
'\r') {
684 if (state->p[1] ==
'\n') {
685 if (state->p[2] !=
' ' && state->p[2] !=
'\t') {
708 if (state->p[1] ==
' ' || state->p[1] ==
'\t') {
728 if (prop_kind == VCARD_VERSION_PROPERTY) {
729 buf_trim(&state->buf);
730 state->version = state->prop;
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);
738 value = vcardvalue_new_textlist(textlist);
741 value = vcardvalue_new_from_string(state->value_kind,
742 buf_cstring(&state->buf));
746 return PE_VALUE_INVALID;
749 vcardproperty_set_value(state->prop, value);
750 buf_reset(&state->buf);
755static void _parse_eatline(
struct vcardparser_state *state)
763 if (state->p[1] ==
' ' || state->p[1] ==
'\t') {
778static void _parse_prop(
struct vcardparser_state *state)
780 int r = _parse_prop_name(state);
782 if (r == PE_PROP_MULTIGROUP) {
783 vcardproperty_kind prop_kind = vcardproperty_isa(state->prop);
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) {
794 VCARD_XLICERRORTYPE_PROPERTYPARSEERROR,
795 "%s '%s'. Removing entire property",
796 vcardparser_errstr(r), buf_cstring(&state->buf));
797 _parse_eatline(state);
800 VCARD_XLICERRORTYPE_PROPERTYPARSEERROR,
801 "%s '%s'. Ignoring property",
802 vcardparser_errstr(r), buf_cstring(&state->buf));
807 if (*state->p ==
';') {
808 r = _parse_prop_params(state);
816 r = _parse_prop_value(state);
818 vcardproperty_kind prop_kind = vcardproperty_isa(state->prop);
819 const char *group = vcardproperty_get_group(state->prop);
821 if (r == PE_VALUE_INVALID) {
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));
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));
841static int _parse_vcard(
struct vcardparser_state *state,
int only_one)
843 const char *cardstart = state->p;
848 if (*state->p ==
'\r' || *state->p ==
'\n' ||
849 *state->p ==
' ' || *state->p ==
'\t') {
856 if (vcardproperty_isa(state->prop) == VCARD_BEGIN_PROPERTY) {
859 state->itemstart = cardstart;
860 r = PE_MISMATCHED_CARD;
864 if (vcardvalue_isa(vcardproperty_get_value(state->prop)) !=
866 r = PE_VALUE_INVALID;
871 vcardvalue_get_text(vcardproperty_get_value(state->prop));
873 if (!val || strcasecmp(val,
"VCARD") != 0) {
874 state->itemstart = cardstart;
879 vcardproperty_free(state->prop);
882 vcardcomponent_add_component(state->xroot, state->vcard);
884 }
else if (!state->vcard) {
886 state->itemstart = cardstart;
887 r = PE_MISMATCHED_CARD;
889 }
else if (vcardproperty_isa(state->prop) == VCARD_END_PROPERTY) {
890 if (vcardvalue_isa(vcardproperty_get_value(state->prop)) !=
892 r = PE_VALUE_INVALID;
897 vcardvalue_get_text(vcardproperty_get_value(state->prop));
899 if (!val || strcasecmp(val,
"VCARD") != 0) {
900 state->itemstart = cardstart;
905 vcardproperty_free(state->prop);
914 vcardcomponent_add_property(state->vcard, state->prop);
920 if (vcardproperty_isa(state->prop) != VCARD_END_PROPERTY) {
921 r = PE_FINISHED_EARLY;
924 vcardproperty_free(state->prop);
931static int vcardparser_parse(
struct vcardparser_state *state,
int only_one)
935 state->p = state->base;
937 buf_init(&state->buf, BUF_GROW);
938 buf_init(&state->errbuf, BUF_GROW);
941 return _parse_vcard(state, only_one);
946static void _free_state(
struct vcardparser_state *state)
948 buf_free(&state->buf);
949 buf_free(&state->errbuf);
952 vcardcomponent_free(state->xroot);
955 memset(state, 0,
sizeof(
struct vcardparser_state));
958static void vcardparser_free(
struct vcardparser_state *state)
965const char *vcardparser_errstr(
int err)
968 case PE_BACKQUOTE_EOF:
969 return "EOF after backslash";
970 case PE_BEGIN_PARAMS:
971 return "Params on BEGIN field";
972 case PE_PROP_MULTIGROUP:
973 return "Multiple group levels in property name";
974 case PE_FINISHED_EARLY:
975 return "vCard not completed";
977 return "End of data while parsing parameter key";
979 return "End of line while parsing parameter key";
980 case PE_MISMATCHED_CARD:
981 return "Closed a different card name than opened";
983 return "End of data while parsing property name";
985 return "End of line while parsing property name";
986 case PE_NAME_INVALID:
987 return "Invalid property name";
988 case PE_PARAMVALUE_EOF:
989 return "End of data while parsing parameter value";
990 case PE_PARAMVALUE_EOL:
991 return "End of line while parsing parameter value";
993 return "End of data while parsing quoted value";
995 return "End of line while parsing quoted value";
997 return "End of line while parsing multi or structured value";
998 case PE_VALUE_INVALID:
999 return "Invalid value for property";
1000 case PE_ILLEGAL_CHAR:
1001 return "Illegal character in vCard";
1004 return "Unknown error";
1008vcardcomponent *vcardparser_parse_string(
const char *str)
1010 struct vcardparser_state parser;
1011 vcardcomponent *vcard = NULL;
1014 memset(&parser, 0,
sizeof(
struct vcardparser_state));
1017 r = vcardparser_parse(&parser, 0);
1019 if (vcardcomponent_count_components(parser.xroot,
1020 VCARD_VCARD_COMPONENT) == 1) {
1021 vcard = vcardcomponent_get_first_component(parser.xroot,
1022 VCARD_VCARD_COMPONENT);
1023 vcardcomponent_remove_component(parser.xroot, vcard);
1025 vcard = parser.xroot;
1026 parser.xroot = NULL;
1030 vcardparser_free(&parser);
1037 xprop_value_kind_func = func;
1038 xprop_value_kind_data = data;
void icalmemory_free_buffer(void *buf)
Releases a buffer.
void * icalmemory_resize_buffer(void *buf, size_t size)
Resizes a buffer created with icalmemory_new_buffer().
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
char * icalmemory_tmp_copy(const char *str)
Creates a copy of the given string, stored on the ring buffer, and returns it.
void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
Appends a character to a buffer.
void * icalmemory_tmp_buffer(size_t size)
Creates a new temporary buffer on the ring and returns it.
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.
Defines the data structure representing vCard properties.
Defines functions for creating vCard text lists.
Defines the data structure representing vCard values.