From dd5ce0a97ad431c07159bac3c4bf53145eeb9731 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 17 Feb 2014 18:27:45 +0100 Subject: [PATCH] vici: Add generic callback based vici message parsing --- src/libcharon/plugins/vici/vici_message.c | 95 +++++++++++++++++++++++ src/libcharon/plugins/vici/vici_message.h | 44 +++++++++++ 2 files changed, 139 insertions(+) diff --git a/src/libcharon/plugins/vici/vici_message.c b/src/libcharon/plugins/vici/vici_message.c index 644cbce59..078c33ead 100644 --- a/src/libcharon/plugins/vici/vici_message.c +++ b/src/libcharon/plugins/vici/vici_message.c @@ -412,6 +412,100 @@ METHOD(vici_message_t, get_encoding, chunk_t, return this->encoding; } +/** + * Private parse context data + */ +struct vici_parse_context_t { + /** current section nesting level */ + int level; + /** parse enumerator */ + enumerator_t *e; +}; + +METHOD(vici_message_t, parse, bool, + private_vici_message_t *this, vici_parse_context_t *ctx, + vici_section_cb_t section, vici_value_cb_t kv, vici_value_cb_t li, + void *user) +{ + vici_parse_context_t root = {}; + char *name, *list = NULL; + vici_type_t type; + chunk_t value; + int base; + bool ok = TRUE; + + if (!ctx) + { + ctx = &root; + root.e = create_enumerator(this); + } + + base = ctx->level; + + while (ok) + { + ok = ctx->e->enumerate(ctx->e, &type, &name, &value); + if (ok) + { + switch (type) + { + case VICI_KEY_VALUE: + if (ctx->level == base && kv) + { + name = strdup(name); + this->strings->insert_last(this->strings, name); + ok = kv(user, &this->public, name, value); + } + continue; + case VICI_LIST_START: + if (ctx->level == base) + { + list = strdup(name); + this->strings->insert_last(this->strings, list); + } + continue; + case VICI_LIST_ITEM: + if (list && li) + { + name = strdup(name); + this->strings->insert_last(this->strings, name); + ok = li(user, &this->public, list, value); + } + continue; + case VICI_LIST_END: + if (ctx->level == base) + { + list = NULL; + } + continue; + case VICI_SECTION_START: + if (ctx->level++ == base && section) + { + name = strdup(name); + this->strings->insert_last(this->strings, name); + ok = section(user, &this->public, ctx, name); + } + continue; + case VICI_SECTION_END: + if (ctx->level-- == base) + { + break; + } + continue; + case VICI_END: + break; + } + } + break; + } + + if (ctx == &root) + { + root.e->destroy(root.e); + } + return ok; +} + METHOD(vici_message_t, dump, bool, private_vici_message_t *this, char *label, FILE *out) { @@ -506,6 +600,7 @@ vici_message_t *vici_message_create_from_data(chunk_t data, bool cleanup) .get_value = _get_value, .vget_value = _vget_value, .get_encoding = _get_encoding, + .parse = _parse, .dump = _dump, .destroy = _destroy, }, diff --git a/src/libcharon/plugins/vici/vici_message.h b/src/libcharon/plugins/vici/vici_message.h index 4fc59d746..8817959c1 100644 --- a/src/libcharon/plugins/vici/vici_message.h +++ b/src/libcharon/plugins/vici/vici_message.h @@ -24,6 +24,7 @@ #include typedef struct vici_message_t vici_message_t; +typedef struct vici_parse_context_t vici_parse_context_t; typedef enum vici_type_t vici_type_t; /** @@ -47,6 +48,30 @@ enum vici_type_t { VICI_END }; +/** + * Callback function for key/value and list items, invoked by parse(). + * + * @param user user data, as passed to parse() + * @param message message currently parsing + * @param name name of key or list + * @param value parsed value + * @return TRUE if parsed successfully + */ +typedef bool (*vici_value_cb_t)(void *user, vici_message_t *message, + char *name, chunk_t value); + +/** + * Callback function for sections, invoked by parse(). + * + * @param user user data, as passed to parse() + * @param message message currently parsing + * @param ctx parse context, to pass to recursive parse() invocations. + * @param name name of the section + * @return TRUE if parsed successfully + */ +typedef bool (*vici_section_cb_t)(void *user, vici_message_t *message, + vici_parse_context_t *ctx, char *name); + /** * Names for vici encoding types */ @@ -137,6 +162,25 @@ struct vici_message_t { */ chunk_t (*get_encoding)(vici_message_t *this); + /** + * Parse a message using callback functions. + * + * Any of the callbacks may be NULL to skip this kind of item. Callbacks are + * invoked for the current section level only. To descent into sections, + * call parse() from within a section callback using the provided parse + * context. + * + * @param ctx parse context, NULL for root level + * @param section callback invoked for each section + * @param kv callback invoked for key/value pairs + * @param li callback invoked for list items + * @param user user data to pass to callbacks + * @return TRUE if parsed successfully + */ + bool (*parse)(vici_message_t *this, vici_parse_context_t *ctx, + vici_section_cb_t section, vici_value_cb_t kv, + vici_value_cb_t li, void *user); + /** * Dump a message text representation to a FILE stream. *