Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117752473
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
26 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/vparse.c b/lib/vparse.c
index fb9ad8c18..5d98e1122 100644
--- a/lib/vparse.c
+++ b/lib/vparse.c
@@ -1,974 +1,974 @@
/* vparse.c : fast vcard parser */
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include "xmalloc.h"
#include "vparse.h"
#define LC(s) do { char *p; for (p = s; *p; p++) if (*p >= 'A' && *p <= 'Z') *p += ('a' - 'A'); } while (0)
static char *buf_dup_cstring(struct buf *buf)
{
char *ret = strndup(buf->s, buf->len);
/* more space efficient than returning overlength buffers, and
* you would just wind up mallocing another buffer anyway */
buf->len = 0;
return ret;
}
static char *buf_dup_lcstring(struct buf *buf)
{
char *ret = buf_dup_cstring(buf);
LC(ret);
return ret;
}
#define NOTESTART() state->itemstart = state->p
#define MAKE(X, Y) X = xzmalloc(sizeof(struct Y));
#define PUTC(C) buf_putc(&state->buf, C)
#define INC(I) state->p += I
/* just leaves it on the buffer */
static int _parse_param_quoted(struct vparse_state *state, int multiparam)
{
NOTESTART();
while (*state->p) {
switch (*state->p) {
case '"':
INC(1);
return 0;
/* normal backslash quoting - NOTE, not strictly RFC complient,
* but I figure anyone who generates one PROBABLY meant to escape
* the next character because it's so common, and LABEL definitely
* allows \n, so we have to handle that anyway */
case '\\':
/* seen in the wild - \n split by line wrapping */
if (state->p[1] == '\r') INC(1);
if (state->p[1] == '\n') {
if (state->p[2] != ' ' && state->p[2] != '\t')
return PE_QSTRING_EOL;
INC(2);
}
if (!state->p[1])
return PE_BACKQUOTE_EOF;
if (state->p[1] == 'n' || state->p[1] == 'N')
PUTC('\n');
else
PUTC(state->p[1]);
INC(2);
break;
/* special value quoting for doublequote and endline (RFC 6868) */
case '^':
if (state->p[1] == '\r') INC(1);
if (state->p[1] == '\n') {
if (state->p[2] != ' ' && state->p[2] != '\t')
return PE_QSTRING_EOL;
INC(2);
}
if (state->p[1] == '\'') {
PUTC('"');
INC(2);
}
else if (state->p[1] == 'n') { /* only lower case per the RFC */
PUTC('\n');
INC(2);
}
else if (state->p[1] == '^') {
PUTC('^');
INC(2);
}
else {
PUTC('^');
INC(1); /* treat next char normally */
}
break;
case '\r':
INC(1);
break; /* just skip */
case '\n':
if (state->p[1] != ' ' && state->p[1] != '\t')
return PE_QSTRING_EOL;
INC(2);
break;
case ',':
if (multiparam)
return PE_QSTRING_COMMA;
/* or fall through, comma isn't special */
default:
PUTC(*state->p);
INC(1);
break;
}
}
return PE_QSTRING_EOF;
}
static int _parse_param_key(struct vparse_state *state, int *haseq)
{
*haseq = 0;
while (*state->p) {
switch (*state->p) {
case '=':
state->param->name = buf_dup_lcstring(&state->buf);
*haseq = 1;
INC(1);
return 0;
case ';': /* vcard 2.1 parameter with no value */
case ':':
if (state->barekeys) {
state->param->name = buf_dup_lcstring(&state->buf);
}
else {
state->param->name = strdup("type");
state->param->value = buf_dup_cstring(&state->buf);
}
/* no INC - we need to see this char up a layer */
return 0;
case '\r':
INC(1);
break; /* just skip */
case '\n':
if (state->p[1] != ' ' && state->p[1] != '\t')
return PE_KEY_EOL;
INC(2);
break;
/* XXX - check exact legal set? */
default:
PUTC(*state->p);
INC(1);
break;
}
}
return PE_KEY_EOF;
}
static int _parse_entry_params(struct vparse_state *state)
{
struct vparse_param **paramp = &state->entry->params;
int multiparam = 0;
int haseq = 0;
int r;
repeat:
multiparam = 0;
haseq = 0;
MAKE(state->param, vparse_param);
NOTESTART();
r = _parse_param_key(state, &haseq);
if (r) return r;
if (strarray_find(state->multiparam, state->param->name, 0))
multiparam = 1;
/* now get the value */
while (*state->p) {
switch (*state->p) {
case '\\': /* normal backslash quoting */
/* seen in the wild - \n split by line wrapping */
if (state->p[1] == '\r') INC(1);
if (state->p[1] == '\n') {
if (state->p[2] != ' ' && state->p[2] != '\t')
return PE_PARAMVALUE_EOL;
INC(2);
}
if (!state->p[1])
return PE_BACKQUOTE_EOF;
if (state->p[1] == 'n' || state->p[1] == 'N')
PUTC('\n');
else
PUTC(state->p[1]);
INC(2);
break;
case '^': /* special value quoting for doublequote (RFC 6868) */
/* seen in the wild - \n split by line wrapping */
if (state->p[1] == '\r') INC(1);
if (state->p[1] == '\n') {
if (state->p[2] != ' ' && state->p[2] != '\t')
return PE_PARAMVALUE_EOL;
INC(2);
}
if (state->p[1] == '\'') {
PUTC('"');
INC(2);
}
else if (state->p[1] == 'n') {
PUTC('\n');
INC(2);
}
else if (state->p[1] == '^') {
PUTC('^');
INC(2);
}
else {
PUTC('^');
INC(1); /* treat next char normally */
}
break;
case '"':
INC(1);
loop:
r = _parse_param_quoted(state, multiparam);
if (r == PE_QSTRING_COMMA) {
char *name = strdup(state->param->name);
state->param->value = buf_dup_cstring(&state->buf);
*paramp = state->param;
paramp = &state->param->next;
MAKE(state->param, vparse_param);
state->param->name = name;
INC(1);
goto loop;
}
if (r) return r;
break;
case ':':
/* done - all parameters parsed */
if (haseq)
state->param->value = buf_dup_cstring(&state->buf);
*paramp = state->param;
state->param = NULL;
INC(1);
return 0;
case ';':
/* another parameter to parse */
if (haseq)
state->param->value = buf_dup_cstring(&state->buf);
*paramp = state->param;
paramp = &state->param->next;
INC(1);
goto repeat;
case '\r':
INC(1);
break; /* just skip */
case '\n':
if (state->p[1] != ' ' && state->p[1] != '\t')
return PE_PARAMVALUE_EOL;
INC(2);
break;
case ',':
if (multiparam) {
char *name = strdup(state->param->name);
if (haseq)
state->param->value = buf_dup_cstring(&state->buf);
*paramp = state->param;
paramp = &state->param->next;
MAKE(state->param, vparse_param);
state->param->name = name;
INC(1);
break;
}
/* or fall through, comma isn't special */
default:
PUTC(*state->p);
INC(1);
break;
}
}
return PE_PARAMVALUE_EOF;
}
static int _parse_entry_key(struct vparse_state *state)
{
NOTESTART();
while (*state->p) {
switch (*state->p) {
case ':':
state->entry->name = buf_dup_lcstring(&state->buf);
INC(1);
return 0;
case ';':
state->entry->name = buf_dup_lcstring(&state->buf);
INC(1);
return _parse_entry_params(state);
case '.':
if (state->entry->group)
return PE_ENTRY_MULTIGROUP;
state->entry->group = buf_dup_lcstring(&state->buf);
INC(1);
break;
case '\r':
INC(1);
break; /* just skip */
case '\n':
if (state->p[1] == ' ' || state->p[1] == '\t') /* wrapped line */
INC(2);
else if (!state->buf.len) /* no key yet? blank intermediate lines are OK */
INC(1);
else
return PE_NAME_EOL;
break;
default:
PUTC(*state->p);
INC(1);
break;
}
}
return PE_NAME_EOF;
}
static int _parse_entry_multivalue(struct vparse_state *state)
{
state->entry->multivalue = 1;
state->entry->v.values = strarray_new();
NOTESTART();
while (*state->p) {
switch (*state->p) {
/* only one type of quoting */
case '\\':
/* seen in the wild - \n split by line wrapping */
if (state->p[1] == '\r') INC(1);
if (state->p[1] == '\n') {
if (state->p[2] != ' ' && state->p[2] != '\t')
return PE_BACKQUOTE_EOF;
INC(2);
}
if (!state->p[1])
return PE_BACKQUOTE_EOF;
if (state->p[1] == 'n' || state->p[1] == 'N')
PUTC('\n');
else
PUTC(state->p[1]);
INC(2);
break;
case ';':
strarray_appendm(state->entry->v.values, buf_dup_cstring(&state->buf));
INC(1);
break;
case '\r':
INC(1);
break; /* just skip */
case '\n':
if (state->p[1] == ' ' || state->p[1] == '\t') {/* wrapped line */
INC(2);
break;
}
/* otherwise it's the end of the value */
INC(1);
goto out;
default:
PUTC(*state->p);
INC(1);
break;
}
}
out:
/* reaching the end of the file isn't a failure here,
* it's just another type of end-of-value */
strarray_appendm(state->entry->v.values, buf_dup_cstring(&state->buf));
return 0;
}
static int _parse_entry_value(struct vparse_state *state)
{
if (state->multival && strarray_find(state->multival, state->entry->name, 0) >= 0)
return _parse_entry_multivalue(state);
NOTESTART();
while (*state->p) {
switch (*state->p) {
/* only one type of quoting */
case '\\':
/* seen in the wild - \n split by line wrapping */
if (state->p[1] == '\r') INC(1);
if (state->p[1] == '\n') {
if (state->p[2] != ' ' && state->p[2] != '\t')
return PE_BACKQUOTE_EOF;
INC(2);
}
if (!state->p[1])
return PE_BACKQUOTE_EOF;
if (state->p[1] == 'n' || state->p[1] == 'N')
PUTC('\n');
else
PUTC(state->p[1]);
INC(2);
break;
case '\r':
INC(1);
break; /* just skip */
case '\n':
if (state->p[1] == ' ' || state->p[1] == '\t') {/* wrapped line */
INC(2);
break;
}
/* otherwise it's the end of the value */
INC(1);
goto out;
default:
PUTC(*state->p);
INC(1);
break;
}
}
out:
/* reaching the end of the file isn't a failure here,
* it's just another type of end-of-value */
state->entry->v.value = buf_dup_cstring(&state->buf);
return 0;
}
/* FREE MEMORY */
static void _free_param(struct vparse_param *param)
{
struct vparse_param *paramnext;
for (; param; param = paramnext) {
paramnext = param->next;
free(param->name);
free(param->value);
free(param);
}
}
static void _free_entry(struct vparse_entry *entry)
{
struct vparse_entry *entrynext;
for (; entry; entry = entrynext) {
entrynext = entry->next;
free(entry->name);
free(entry->group);
if (entry->multivalue)
strarray_free(entry->v.values);
else
free(entry->v.value);
_free_param(entry->params);
free(entry);
}
}
static void _free_card(struct vparse_card *card)
{
struct vparse_card *cardnext;
for (; card; card = cardnext) {
cardnext = card->next;
free(card->type);
_free_entry(card->properties);
_free_card(card->objects);
free(card);
}
}
static void _free_state(struct vparse_state *state)
{
buf_free(&state->buf);
_free_card(state->card);
_free_entry(state->entry);
_free_param(state->param);
if (state->multival) strarray_free(state->multival);
memset(state, 0, sizeof(struct vparse_state));
}
static int _parse_entry(struct vparse_state *state)
{
int r = _parse_entry_key(state);
if (r) return r;
return _parse_entry_value(state);
}
static int _parse_vcard(struct vparse_state *state, struct vparse_card *card, int only_one)
{
struct vparse_card **subp = &card->objects;
struct vparse_entry **entryp = &card->properties;
struct vparse_card *sub;
const char *cardstart = state->p;
const char *entrystart;
int r;
while (*state->p) {
/* whitespace is very skippable before AND afterwards */
if (*state->p == '\r' || *state->p == '\n' || *state->p == ' ' || *state->p == '\t') {
INC(1);
continue;
}
entrystart = state->p;
MAKE(state->entry, vparse_entry);
r = _parse_entry(state);
if (r) return r;
if (!strcmp(state->entry->name, "begin")) {
/* shouldn't be any params */
if (state->entry->params) {
state->itemstart = entrystart;
return PE_BEGIN_PARAMS;
}
/* only possible if some idiot passes 'begin' as
* multivalue field name */
if (state->entry->multivalue) {
state->itemstart = entrystart;
return PE_BEGIN_PARAMS;
}
MAKE(sub, vparse_card);
sub->type = strdup(state->entry->v.value);
LC(sub->type);
_free_entry(state->entry);
state->entry = NULL;
/* we must stitch it in first, because state won't hold it */
*subp = sub;
subp = &sub->next;
r = _parse_vcard(state, sub, /*only_one*/0);
if (r) return r;
if (only_one) return 0;
}
else if (!strcmp(state->entry->name, "end")) {
/* shouldn't be any params */
if (state->entry->params) {
state->itemstart = entrystart;
return PE_BEGIN_PARAMS;
}
/* only possible if some idiot passes 'end' as
* multivalue field name */
if (state->entry->multivalue) {
state->itemstart = entrystart;
return PE_BEGIN_PARAMS;
}
if (strcasecmp(state->entry->v.value, card->type)) {
/* special case mismatched card, the "start" was the start of
* the card */
state->itemstart = cardstart;
return PE_MISMATCHED_CARD;
}
_free_entry(state->entry);
state->entry = NULL;
return 0;
}
else {
/* it's a parameter on this one */
*entryp = state->entry;
entryp = &state->entry->next;
state->entry = NULL;
}
}
if (card->type)
return PE_FINISHED_EARLY;
return 0;
}
/* PUBLIC API */
EXPORTED int vparse_parse(struct vparse_state *state, int only_one)
{
MAKE(state->card, vparse_card);
state->p = state->base;
/* don't parse trailing non-whitespace */
return _parse_vcard(state, state->card, only_one);
}
EXPORTED void vparse_free(struct vparse_state *state)
{
_free_state(state);
}
EXPORTED void vparse_free_card(struct vparse_card *card)
{
_free_card(card);
}
EXPORTED void vparse_fillpos(struct vparse_state *state, struct vparse_errorpos *pos)
{
int l = 1;
int c = 0;
const char *p;
memset(pos, 0, sizeof(struct vparse_errorpos));
pos->errorpos = state->p - state->base;
pos->startpos = state->itemstart - state->base;
for (p = state->base; p < state->p; p++) {
if (*p == '\n') {
l++;
c = 0;
}
else {
c++;
}
if (p == state->itemstart) {
pos->startline = l;
pos->startchar = c;
}
}
pos->errorline = l;
pos->errorchar = c;
}
EXPORTED const char *vparse_errstr(int err)
{
switch(err) {
case PE_BACKQUOTE_EOF:
return "EOF after backslash";
case PE_BEGIN_PARAMS:
return "Params on BEGIN field";
case PE_ENTRY_MULTIGROUP:
return "Multiple group levels in property name";
case PE_FINISHED_EARLY:
return "VCard not completed";
case PE_KEY_EOF:
return "End of data while parsing parameter key";
case PE_KEY_EOL:
return "End of line while parsing parameter key";
case PE_MISMATCHED_CARD:
return "Closed a different card name than opened";
case PE_NAME_EOF:
return "End of data while parsing entry name";
case PE_NAME_EOL:
return "End of line while parsing entry name";
case PE_PARAMVALUE_EOF:
return "End of data while parsing parameter value";
case PE_PARAMVALUE_EOL:
return "End of line while parsing parameter value";
case PE_QSTRING_EOF:
return "End of data while parsing quoted value";
case PE_QSTRING_EOL:
return "End of line while parsing quoted value";
}
return "Unknown error";
}
EXPORTED const char *vparse_stringval(const struct vparse_card *card, const char *name)
{
struct vparse_entry *entry;
for (entry = card->properties; entry; entry = entry->next) {
if (entry->multivalue == 1) continue;
if (!strcasecmp(name, entry->name))
return entry->v.value;
}
return NULL;
}
EXPORTED const strarray_t *vparse_multival(const struct vparse_card *card, const char *name)
{
struct vparse_entry *entry;
for (entry = card->properties; entry; entry = entry->next) {
if (entry->multivalue == 0) continue;
if (!strcasecmp(name, entry->name))
return entry->v.values;
}
return NULL;
}
EXPORTED void vparse_set_multival(struct vparse_state *state, const char *name)
{
if (!state->multival) state->multival = strarray_new();
strarray_append(state->multival, name);
}
struct vparse_target {
struct buf *buf;
size_t last;
};
static void _endline(struct vparse_target *tgt)
{
buf_appendcstr(tgt->buf, "\r\n");
tgt->last = buf_len(tgt->buf);
}
static void _checkwrap(unsigned char c, struct vparse_target *tgt)
{
if (buf_len(tgt->buf) - tgt->last < 75)
return; /* still short line */
if (c >= 0x80 && c < 0xC0)
return; /* never wrap continuation chars */
/* wrap */
_endline(tgt);
buf_putc(tgt->buf, ' ');
}
static void _value_to_tgt(const char *value, struct vparse_target *tgt)
{
for (; *value; value++) {
_checkwrap(*value, tgt);
switch (*value) {
case '\r':
break;
case '\n':
buf_putc(tgt->buf, '\\');
buf_putc(tgt->buf, 'N');
break;
case ';':
case ',':
case '\\':
buf_putc(tgt->buf, '\\');
buf_putc(tgt->buf, *value);
break;
default:
buf_putc(tgt->buf, *value);
}
}
}
static void _paramval_to_tgt(const char *value, struct vparse_target *tgt)
{
for (; *value; value++) {
_checkwrap(*value, tgt);
switch (*value) {
case '\r':
break;
case '\n':
buf_putc(tgt->buf, '^');
buf_putc(tgt->buf, 'n');
break;
case '^':
buf_putc(tgt->buf, '^');
buf_putc(tgt->buf, '^');
break;
case '"':
buf_putc(tgt->buf, '^');
buf_putc(tgt->buf, '\'');
break;
default:
buf_putc(tgt->buf, *value);
}
}
}
static void _key_to_tgt(const char *key, struct vparse_target *tgt)
{
/* uppercase keys */
for (; *key; key++) {
_checkwrap(*key, tgt);
buf_putc(tgt->buf, toupper(*key));
}
}
static void _entry_to_tgt(const struct vparse_entry *entry, struct vparse_target *tgt)
{
struct vparse_param *param;
// rfc6350 3.3 - it is RECOMMENDED that property and parameter names be upper-case on output.
if (entry->group) {
_key_to_tgt(entry->group, tgt);
buf_putc(tgt->buf, '.');
}
_key_to_tgt(entry->name, tgt);
for (param = entry->params; param; param = param->next) {
buf_putc(tgt->buf, ';');
_key_to_tgt(param->name, tgt);
buf_putc(tgt->buf, '=');
/* XXX - smart quoting? */
buf_putc(tgt->buf, '"');
_paramval_to_tgt(param->value, tgt);
buf_putc(tgt->buf, '"');
}
buf_putc(tgt->buf, ':');
if (entry->multivalue) {
int i;
for (i = 0; i < entry->v.values->count; i++) {
if (i) buf_putc(tgt->buf, ';');
_value_to_tgt(strarray_nth(entry->v.values, i), tgt);
}
}
else {
_value_to_tgt(entry->v.value, tgt);
}
_endline(tgt);
}
static void _card_to_tgt(const struct vparse_card *card, struct vparse_target *tgt)
{
const struct vparse_entry *entry;
const struct vparse_card *sub;
if (card->type) {
_key_to_tgt("BEGIN", tgt);
buf_putc(tgt->buf, ':');
_key_to_tgt(card->type, tgt);
_endline(tgt);
}
for (entry = card->properties; entry; entry = entry->next)
_entry_to_tgt(entry, tgt);
for (sub = card->objects; sub; sub = sub->next)
_card_to_tgt(sub, tgt);
if (card->type) {
_key_to_tgt("END", tgt);
buf_putc(tgt->buf, ':');
_key_to_tgt(card->type, tgt);
_endline(tgt);
}
}
EXPORTED void vparse_tobuf(const struct vparse_card *card, struct buf *buf)
{
struct vparse_target tgt;
tgt.buf = buf;
tgt.last = 0;
for (; card; card = card->next)
_card_to_tgt(card, &tgt);
}
EXPORTED struct vparse_card *vparse_new_card(const char *type)
{
struct vparse_card *card = xzmalloc(sizeof(struct vparse_card));
card->type = xstrdupnull(type);
return card;
}
EXPORTED struct vparse_entry *vparse_add_entry(struct vparse_card *card, const char *group, const char *name, const char *value)
{
struct vparse_entry **entryp = &card->properties;
while (*entryp) entryp = &((*entryp)->next);
struct vparse_entry *entry = xzmalloc(sizeof(struct vparse_entry));
entry->group = xstrdupnull(group);
entry->name = xstrdupnull(name);
entry->v.value = xstrdupnull(value);
*entryp = entry;
return entry;
}
EXPORTED struct vparse_entry *vparse_get_entry(struct vparse_card *card, const char *group, const char *name)
{
struct vparse_entry *entry = NULL;
for (entry = card->properties; entry; entry = entry->next) {
- if (!strcmpsafe(entry->group, group) && !strcmpsafe(entry->name, name))
+ if (!strcasecmpsafe(entry->group, group) && !strcasecmpsafe(entry->name, name))
break;
}
return entry;
}
EXPORTED void vparse_delete_entries(struct vparse_card *card, const char *group, const char *name)
{
struct vparse_entry **entryp = &card->properties;
while (*entryp) {
struct vparse_entry *entry = *entryp;
- if (!strcmpsafe(entry->group, group) && !strcmpsafe(entry->name, name)) {
+ if (!strcasecmpsafe(entry->group, group) && !strcasecmpsafe(entry->name, name)) {
*entryp = entry->next;
_free_entry(entry);
}
else {
entryp = &((*entryp)->next);
}
}
}
#ifdef DEBUG
static int _dump_card(struct vparse_card *card)
{
struct vparse_entry *entry;
struct vparse_param *param;
struct vparse_card *sub;
printf("begin:%s\n", card->type);
for (entry = card->properties; entry; entry = entry->next) {
printf("%s", entry->name);
for (param = entry->params; param; param = param->next)
printf(";%s=%s", param->name, param->value);
if (entry->multivalue)
printf(":multivalue\n");
else
printf(":%s\n", entry->v.value);
}
for (sub = card->objects; sub; sub = sub->next)
_dump_card(sub);
printf("end:%s\n", card->type);
}
static int _dump(struct vparse_card *card)
{
_dump_card(card->objects);
}
int main(int argv, const char **argc)
{
const char *fname = argc[1];
struct stat sbuf;
int fd = open(fname, O_RDONLY);
struct vparse_state parser;
char *data;
int r;
memset(&parser, 0, sizeof(struct vparse_state));
fstat(fd, &sbuf);
data = malloc(sbuf.st_size+1);
read(fd, data, sbuf.st_size);
data[sbuf.st_size] = '\0';
parser.base = data;
r = vparse_parse(&parser);
if (r) {
struct vparse_errorpos pos;
vparse_fillpos(&parser, &pos);
printf("error %s at line %d char %d: %.*s ... %.*s <--- (started at line %d char %d)\n",
vparse_errstr(r), pos.errorline, pos.errorchar,
20, parser.base + pos.startpos,
20, parser.base + pos.errorpos - 20,
pos.startline, pos.startchar);
return 1;
}
_dump(parser.card);
vparse_free(&parser);
return 0;
}
#endif
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Apr 4, 4:11 AM (18 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18822538
Default Alt Text
(26 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline