vici: Add generic callback based vici message parsing
authorMartin Willi <martin@revosec.ch>
Mon, 17 Feb 2014 17:27:45 +0000 (18:27 +0100)
committerMartin Willi <martin@revosec.ch>
Wed, 7 May 2014 12:13:36 +0000 (14:13 +0200)
src/libcharon/plugins/vici/vici_message.c
src/libcharon/plugins/vici/vici_message.h

index 644cbce..078c33e 100644 (file)
@@ -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,
                },
index 4fc59d7..8817959 100644 (file)
@@ -24,6 +24,7 @@
 #include <library.h>
 
 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;
 
 /**
@@ -48,6 +49,30 @@ enum vici_type_t {
 };
 
 /**
+ * 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
  */
 extern enum_name_t *vici_type_names;
@@ -138,6 +163,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.
         *
         * @param label label to print for message