123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769 |
- /*
- * Copyright (C) 2015 Tobias Brunner
- * HSR Hochschule fuer Technik Rapperswil
- *
- * Copyright (C) 2014 Martin Willi
- * Copyright (C) 2014 revosec AG
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
- #include "vici_message.h"
- #include "vici_builder.h"
- #include <bio/bio_reader.h>
- #include <bio/bio_writer.h>
- #include <errno.h>
- typedef struct private_vici_message_t private_vici_message_t;
- /**
- * Private data of an vici_message_t object.
- */
- struct private_vici_message_t {
- /**
- * Public vici_message_t interface.
- */
- vici_message_t public;
- /**
- * Message encoding
- */
- chunk_t encoding;
- /**
- * Free encoding during destruction?
- */
- bool cleanup;
- /**
- * Allocated strings we maintain for get_str()
- */
- linked_list_t *strings;
- };
- ENUM(vici_type_names, VICI_START, VICI_END,
- "start",
- "section-start",
- "section-end",
- "key-value",
- "list-start",
- "list-item",
- "list-end",
- "end"
- );
- /**
- * See header.
- */
- bool vici_stringify(chunk_t chunk, char *buf, size_t size)
- {
- if (!chunk_printable(chunk, NULL, 0))
- {
- return FALSE;
- }
- snprintf(buf, size, "%.*s", (int)chunk.len, chunk.ptr);
- return TRUE;
- }
- /**
- * See header.
- */
- bool vici_verify_type(vici_type_t type, u_int section, bool list)
- {
- if (list)
- {
- if (type != VICI_LIST_END && type != VICI_LIST_ITEM)
- {
- DBG1(DBG_ENC, "'%N' within list", vici_type_names, type);
- return FALSE;
- }
- }
- else
- {
- if (type == VICI_LIST_ITEM || type == VICI_LIST_END)
- {
- DBG1(DBG_ENC, "'%N' outside list", vici_type_names, type);
- return FALSE;
- }
- }
- if (type == VICI_SECTION_END && section == 0)
- {
- DBG1(DBG_ENC, "'%N' outside of section", vici_type_names, type);
- return FALSE;
- }
- if (type == VICI_END && section)
- {
- DBG1(DBG_ENC, "'%N' within section", vici_type_names, type);
- return FALSE;
- }
- return TRUE;
- }
- /**
- * Enumerator parsing message
- */
- typedef struct {
- /* implements enumerator */
- enumerator_t public;
- /** reader to parse from */
- bio_reader_t *reader;
- /** section nesting level */
- int section;
- /** currently parsing list? */
- bool list;
- /** string currently enumerating */
- char name[257];
- } parse_enumerator_t;
- METHOD(enumerator_t, parse_enumerate, bool,
- parse_enumerator_t *this, va_list args)
- {
- vici_type_t *out;
- chunk_t *value;
- char **name;
- uint8_t type;
- chunk_t data;
- VA_ARGS_VGET(args, out, name, value);
- if (!this->reader->remaining(this->reader) ||
- !this->reader->read_uint8(this->reader, &type))
- {
- *out = VICI_END;
- return TRUE;
- }
- if (!vici_verify_type(type, this->section, this->list))
- {
- return FALSE;
- }
- switch (type)
- {
- case VICI_SECTION_START:
- if (!this->reader->read_data8(this->reader, &data) ||
- !vici_stringify(data, this->name, sizeof(this->name)))
- {
- DBG1(DBG_ENC, "invalid '%N' encoding", vici_type_names, type);
- return FALSE;
- }
- *name = this->name;
- this->section++;
- break;
- case VICI_SECTION_END:
- this->section--;
- break;
- case VICI_KEY_VALUE:
- if (!this->reader->read_data8(this->reader, &data) ||
- !vici_stringify(data, this->name, sizeof(this->name)) ||
- !this->reader->read_data16(this->reader, value))
- {
- DBG1(DBG_ENC, "invalid '%N' encoding", vici_type_names, type);
- return FALSE;
- }
- *name = this->name;
- break;
- case VICI_LIST_START:
- if (!this->reader->read_data8(this->reader, &data) ||
- !vici_stringify(data, this->name, sizeof(this->name)))
- {
- DBG1(DBG_ENC, "invalid '%N' encoding", vici_type_names, type);
- return FALSE;
- }
- *name = this->name;
- this->list = TRUE;
- break;
- case VICI_LIST_ITEM:
- this->reader->read_data16(this->reader, value);
- break;
- case VICI_LIST_END:
- this->list = FALSE;
- break;
- case VICI_END:
- return TRUE;
- default:
- DBG1(DBG_ENC, "unknown encoding type: %u", type);
- return FALSE;
- }
- *out = type;
- return TRUE;
- }
- METHOD(enumerator_t, parse_destroy, void,
- parse_enumerator_t *this)
- {
- this->reader->destroy(this->reader);
- free(this);
- }
- METHOD(vici_message_t, create_enumerator, enumerator_t*,
- private_vici_message_t *this)
- {
- parse_enumerator_t *enumerator;
- INIT(enumerator,
- .public = {
- .enumerate = enumerator_enumerate_default,
- .venumerate = _parse_enumerate,
- .destroy = _parse_destroy,
- },
- .reader = bio_reader_create(this->encoding),
- );
- return &enumerator->public;
- }
- /**
- * Find a value for given vararg key
- */
- static bool find_value(private_vici_message_t *this, chunk_t *value,
- char *fmt, va_list args)
- {
- enumerator_t *enumerator;
- char buf[128], *name, *key, *dot, *next;
- int section = 0, keysection = 0;
- bool found = FALSE;
- chunk_t current;
- vici_type_t type;
- vsnprintf(buf, sizeof(buf), fmt, args);
- next = buf;
- enumerator = create_enumerator(this);
- /* descent into section */
- while (TRUE)
- {
- dot = strchr(next, '.');
- if (!dot)
- {
- key = next;
- break;
- }
- *dot = '\0';
- key = next;
- next = dot + 1;
- keysection++;
- while (enumerator->enumerate(enumerator, &type, &name, ¤t))
- {
- switch (type)
- {
- case VICI_SECTION_START:
- section++;
- if (section == keysection && streq(name, key))
- {
- break;
- }
- continue;
- case VICI_SECTION_END:
- section--;
- continue;
- case VICI_END:
- break;
- default:
- continue;
- }
- break;
- }
- }
- /* find key/value in current section */
- while (enumerator->enumerate(enumerator, &type, &name, ¤t))
- {
- switch (type)
- {
- case VICI_KEY_VALUE:
- if (section == keysection && streq(key, name))
- {
- *value = current;
- found = TRUE;
- break;
- }
- continue;
- case VICI_SECTION_START:
- section++;
- continue;
- case VICI_SECTION_END:
- section--;
- continue;
- case VICI_END:
- break;
- default:
- continue;
- }
- break;
- }
- enumerator->destroy(enumerator);
- return found;
- }
- METHOD(vici_message_t, vget_str, char*,
- private_vici_message_t *this, char *def, char *fmt, va_list args)
- {
- chunk_t value;
- bool found;
- char *str;
- found = find_value(this, &value, fmt, args);
- if (found)
- {
- if (chunk_printable(value, NULL, 0))
- {
- str = strndup(value.ptr, value.len);
- /* keep a reference to string, so caller doesn't have to care */
- this->strings->insert_last(this->strings, str);
- return str;
- }
- }
- return def;
- }
- METHOD(vici_message_t, get_str, char*,
- private_vici_message_t *this, char *def, char *fmt, ...)
- {
- va_list args;
- char *str;
- va_start(args, fmt);
- str = vget_str(this, def, fmt, args);
- va_end(args);
- return str;
- }
- METHOD(vici_message_t, vget_int, int,
- private_vici_message_t *this, int def, char *fmt, va_list args)
- {
- chunk_t value;
- bool found;
- char buf[32], *pos;
- int ret;
- found = find_value(this, &value, fmt, args);
- if (found)
- {
- if (value.len == 0)
- {
- return def;
- }
- if (chunk_printable(value, NULL, 0))
- {
- snprintf(buf, sizeof(buf), "%.*s", (int)value.len, value.ptr);
- errno = 0;
- ret = strtol(buf, &pos, 0);
- if (errno == 0 && pos == buf + strlen(buf))
- {
- return ret;
- }
- }
- }
- return def;
- }
- METHOD(vici_message_t, get_int, int,
- private_vici_message_t *this, int def, char *fmt, ...)
- {
- va_list args;
- int val;
- va_start(args, fmt);
- val = vget_int(this, def, fmt, args);
- va_end(args);
- return val;
- }
- METHOD(vici_message_t, vget_bool, bool,
- private_vici_message_t *this, bool def, char *fmt, va_list args)
- {
- chunk_t value;
- bool found;
- char buf[16];
- found = find_value(this, &value, fmt, args);
- if (found)
- {
- if (value.len == 0)
- {
- return def;
- }
- if (chunk_printable(value, NULL, 0))
- {
- snprintf(buf, sizeof(buf), "%.*s", (int)value.len, value.ptr);
- return settings_value_as_bool(buf, def);
- }
- }
- return def;
- }
- METHOD(vici_message_t, get_bool, bool,
- private_vici_message_t *this, bool def, char *fmt, ...)
- {
- va_list args;
- bool val;
- va_start(args, fmt);
- val = vget_bool(this, def, fmt, args);
- va_end(args);
- return val;
- }
- METHOD(vici_message_t, vget_value, chunk_t,
- private_vici_message_t *this, chunk_t def, char *fmt, va_list args)
- {
- chunk_t value;
- bool found;
- found = find_value(this, &value, fmt, args);
- if (found)
- {
- return value;
- }
- return def;
- }
- METHOD(vici_message_t, get_value, chunk_t,
- private_vici_message_t *this, chunk_t def, char *fmt, ...)
- {
- va_list args;
- chunk_t value;
- va_start(args, fmt);
- value = vget_value(this, def, fmt, args);
- va_end(args);
- return value;
- }
- METHOD(vici_message_t, get_encoding, chunk_t,
- private_vici_message_t *this)
- {
- 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_START:
- /* should never occur */
- continue;
- 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, bool pretty, FILE *out)
- {
- enumerator_t *enumerator;
- int ident = 0, delta;
- vici_type_t type, last_type = VICI_START;
- char *name, *term, *sep, *separ, *assign;
- chunk_t value;
- /* pretty print uses indentation on multiple lines */
- if (pretty)
- {
- delta = 2;
- term = "\n";
- separ = "";
- assign = " = ";
- }
- else
- {
- delta = 0;
- term = "";
- separ = " ";
- assign = "=";
- }
- fprintf(out, "%s {%s", label, term);
- ident += delta;
- enumerator = create_enumerator(this);
- while (enumerator->enumerate(enumerator, &type, &name, &value))
- {
- switch (type)
- {
- case VICI_START:
- /* should never occur */
- break;
- case VICI_SECTION_START:
- sep = (last_type != VICI_SECTION_START &&
- last_type != VICI_START) ? separ : "";
- fprintf(out, "%*s%s%s {%s", ident, "", sep, name, term);
- ident += delta;
- break;
- case VICI_SECTION_END:
- ident -= delta;
- fprintf(out, "%*s}%s", ident, "", term);
- break;
- case VICI_KEY_VALUE:
- sep = (last_type != VICI_SECTION_START &&
- last_type != VICI_START) ? separ : "";
- if (chunk_printable(value, NULL, ' '))
- {
- fprintf(out, "%*s%s%s%s%.*s%s", ident, "", sep, name,
- assign, (int)value.len, value.ptr, term);
- }
- else
- {
- fprintf(out, "%*s%s%s%s0x%+#B%s", ident, "", sep, name,
- assign, &value, term);
- }
- break;
- case VICI_LIST_START:
- sep = (last_type != VICI_SECTION_START &&
- last_type != VICI_START) ? separ : "";
- fprintf(out, "%*s%s%s%s[%s", ident, "", sep, name, assign, term);
- ident += delta;
- break;
- case VICI_LIST_END:
- ident -= delta;
- fprintf(out, "%*s]%s", ident, "", term);
- break;
- case VICI_LIST_ITEM:
- sep = (last_type != VICI_LIST_START) ? separ : "";
- if (chunk_printable(value, NULL, ' '))
- {
- fprintf(out, "%*s%s%.*s%s", ident, "", sep,
- (int)value.len, value.ptr, term);
- }
- else
- {
- fprintf(out, "%*s%s0x%+#B%s", ident, "", sep,
- &value, term);
- }
- break;
- case VICI_END:
- fprintf(out, "}\n");
- enumerator->destroy(enumerator);
- return TRUE;
- }
- last_type = type;
- }
- enumerator->destroy(enumerator);
- return FALSE;
- }
- METHOD(vici_message_t, destroy, void,
- private_vici_message_t *this)
- {
- if (this->cleanup)
- {
- chunk_clear(&this->encoding);
- }
- this->strings->destroy_function(this->strings, free);
- free(this);
- }
- /**
- * See header
- */
- vici_message_t *vici_message_create_from_data(chunk_t data, bool cleanup)
- {
- private_vici_message_t *this;
- INIT(this,
- .public = {
- .create_enumerator = _create_enumerator,
- .get_str = _get_str,
- .vget_str = _vget_str,
- .get_int = _get_int,
- .vget_int = _vget_int,
- .get_bool = _get_bool,
- .vget_bool = _vget_bool,
- .get_value = _get_value,
- .vget_value = _vget_value,
- .get_encoding = _get_encoding,
- .parse = _parse,
- .dump = _dump,
- .destroy = _destroy,
- },
- .strings = linked_list_create(),
- .encoding = data,
- .cleanup = cleanup,
- );
- return &this->public;
- }
- /**
- * See header
- */
- vici_message_t *vici_message_create_from_enumerator(enumerator_t *enumerator)
- {
- vici_builder_t *builder;
- vici_type_t type;
- char *name;
- chunk_t value;
- builder = vici_builder_create();
- while (enumerator->enumerate(enumerator, &type, &name, &value))
- {
- switch (type)
- {
- case VICI_SECTION_START:
- case VICI_LIST_START:
- builder->add(builder, type, name);
- continue;
- case VICI_KEY_VALUE:
- builder->add(builder, type, name, value);
- continue;
- case VICI_LIST_ITEM:
- builder->add(builder, type, value);
- continue;
- case VICI_SECTION_END:
- case VICI_LIST_END:
- default:
- builder->add(builder, type);
- continue;
- case VICI_END:
- break;
- }
- break;
- }
- enumerator->destroy(enumerator);
- return builder->finalize(builder);
- }
- /**
- * See header
- */
- vici_message_t *vici_message_create_from_args(vici_type_t type, ...)
- {
- vici_builder_t *builder;
- va_list args;
- char *name;
- chunk_t value;
- builder = vici_builder_create();
- va_start(args, type);
- while (type != VICI_END)
- {
- switch (type)
- {
- case VICI_LIST_START:
- case VICI_SECTION_START:
- name = va_arg(args, char*);
- builder->add(builder, type, name);
- break;
- case VICI_KEY_VALUE:
- name = va_arg(args, char*);
- value = va_arg(args, chunk_t);
- builder->add(builder, type, name, value);
- break;
- case VICI_LIST_ITEM:
- value = va_arg(args, chunk_t);
- builder->add(builder, type, value);
- break;
- case VICI_SECTION_END:
- case VICI_LIST_END:
- default:
- builder->add(builder, type);
- break;
- }
- type = va_arg(args, vici_type_t);
- }
- va_end(args);
- return builder->finalize(builder);
- }
|