From db18495514e4a8a7adcedacd69334c44eaf7add9 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 10 Feb 2014 17:09:52 +0100 Subject: [PATCH] vici: Add some convenience getters on message --- .../plugins/vici/suites/test_message.c | 76 +++++++ src/libcharon/plugins/vici/vici_message.c | 201 +++++++++++++++++- src/libcharon/plugins/vici/vici_message.h | 61 ++++++ 3 files changed, 337 insertions(+), 1 deletion(-) diff --git a/src/libcharon/plugins/vici/suites/test_message.c b/src/libcharon/plugins/vici/suites/test_message.c index a8196bd63..293117348 100644 --- a/src/libcharon/plugins/vici/suites/test_message.c +++ b/src/libcharon/plugins/vici/suites/test_message.c @@ -304,6 +304,76 @@ START_TEST(test_builder_fmt) } END_TEST +static vici_message_t* build_getter_msg() +{ + return vici_message_create_from_args( + VICI_KEY_VALUE, "key1", chunk_from_str("1"), + VICI_SECTION_START, "section1", + VICI_KEY_VALUE, "key2", chunk_from_str("0x12"), + VICI_SECTION_START, "section2", + VICI_KEY_VALUE, "key3", chunk_from_str("-1"), + VICI_SECTION_END, + VICI_KEY_VALUE, "key4", chunk_from_str("asdf"), + VICI_SECTION_END, + VICI_KEY_VALUE, "key5", chunk_from_str(""), + VICI_END); +} + +START_TEST(test_get_str) +{ + vici_message_t *m; + + m = build_getter_msg(); + + ck_assert_str_eq(m->get_str(m, "def", "key1"), "1"); + ck_assert_str_eq(m->get_str(m, "def", "section1.key2"), "0x12"); + ck_assert_str_eq(m->get_str(m, "def", "section%d.section2.key3", 1), "-1"); + ck_assert_str_eq(m->get_str(m, "def", "section1.key4"), "asdf"); + ck_assert_str_eq(m->get_str(m, "def", "key5"), ""); + ck_assert_str_eq(m->get_str(m, "no", "nonexistent"), "no"); + ck_assert_str_eq(m->get_str(m, "no", "n.o.n.e.x.i.s.t.e.n.t"), "no"); + + m->destroy(m); +} +END_TEST + +START_TEST(test_get_int) +{ + vici_message_t *m; + + m = build_getter_msg(); + + ck_assert_int_eq(m->get_int(m, 2, "key1"), 1); + ck_assert_int_eq(m->get_int(m, 2, "section1.key2"), 0x12); + ck_assert_int_eq(m->get_int(m, 2, "section1.section2.key3"), -1); + ck_assert_int_eq(m->get_int(m, 2, "section1.key4"), 2); + ck_assert_int_eq(m->get_int(m, 2, "key5"), 0); + ck_assert_int_eq(m->get_int(m, 2, "nonexistent"), 2); + ck_assert_int_eq(m->get_int(m, 2, "n.o.n.e.x.i.s.t.e.n.t"), 2); + + m->destroy(m); +} +END_TEST + +START_TEST(test_get_value) +{ + vici_message_t *m; + chunk_t d = chunk_from_chars('d','e','f'); + + m = build_getter_msg(); + + ck_assert_chunk_eq(m->get_value(m, d, "key1"), chunk_from_str("1")); + ck_assert_chunk_eq(m->get_value(m, d, "section1.key2"), chunk_from_str("0x12")); + ck_assert_chunk_eq(m->get_value(m, d, "section1.section2.key3"), chunk_from_str("-1")); + ck_assert_chunk_eq(m->get_value(m, d, "section1.key4"), chunk_from_str("asdf")); + ck_assert_chunk_eq(m->get_value(m, d, "key5"), chunk_empty); + ck_assert_chunk_eq(m->get_value(m, d, "nonexistent"), d); + ck_assert_chunk_eq(m->get_value(m, d, "n.o.n.e.x.i.s.t.e.n.t"), d); + + m->destroy(m); +} +END_TEST + Suite *message_suite_create() { Suite *s; @@ -327,5 +397,11 @@ Suite *message_suite_create() tcase_add_test(tc, test_builder_fmt); suite_add_tcase(s, tc); + tc = tcase_create("convenience getters"); + tcase_add_test(tc, test_get_str); + tcase_add_test(tc, test_get_int); + tcase_add_test(tc, test_get_value); + suite_add_tcase(s, tc); + return s; } diff --git a/src/libcharon/plugins/vici/vici_message.c b/src/libcharon/plugins/vici/vici_message.c index 52094550c..1a1820acb 100644 --- a/src/libcharon/plugins/vici/vici_message.c +++ b/src/libcharon/plugins/vici/vici_message.c @@ -19,6 +19,8 @@ #include #include +#include + typedef struct private_vici_message_t private_vici_message_t; /** @@ -40,6 +42,11 @@ struct private_vici_message_t { * Free encoding during destruction? */ bool cleanup; + + /** + * Allocated strings we maintain for get_str() + */ + linked_list_t *strings; }; ENUM(vici_type_names, VICI_SECTION_START, VICI_END, @@ -129,7 +136,8 @@ METHOD(enumerator_t, parse_enumerate, bool, u_int8_t type; chunk_t data; - if (!this->reader->read_uint8(this->reader, &type)) + if (!this->reader->remaining(this->reader) || + !this->reader->read_uint8(this->reader, &type)) { *out = VICI_END; return TRUE; @@ -215,6 +223,189 @@ METHOD(vici_message_t, create_enumerator, enumerator_t*, 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; + } + break; + 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 (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_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) { @@ -228,6 +419,7 @@ METHOD(vici_message_t, destroy, void, { chunk_clear(&this->encoding); } + this->strings->destroy_function(this->strings, free); free(this); } @@ -241,9 +433,16 @@ vici_message_t *vici_message_create_from_data(chunk_t data, bool cleanup) INIT(this, .public = { .create_enumerator = _create_enumerator, + .get_str = _get_str, + .vget_str = _vget_str, + .get_int = _get_int, + .vget_int = _vget_int, + .get_value = _get_value, + .vget_value = _vget_value, .get_encoding = _get_encoding, .destroy = _destroy, }, + .strings = linked_list_create(), .encoding = data, .cleanup = cleanup, ); diff --git a/src/libcharon/plugins/vici/vici_message.h b/src/libcharon/plugins/vici/vici_message.h index a7f506a20..9e1a36659 100644 --- a/src/libcharon/plugins/vici/vici_message.h +++ b/src/libcharon/plugins/vici/vici_message.h @@ -69,6 +69,67 @@ struct vici_message_t { */ enumerator_t* (*create_enumerator)(vici_message_t *this); + /** + * Get the value of a key/value pair as a string. + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param ... arguments to fmt string + * @return string + */ + char* (*get_str)(vici_message_t *this, char *def, char *fmt, ...); + + /** + * Get the value of a key/value pair as a string, va_list variant. + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param args arguments to fmt string + * @return string + */ + char* (*vget_str)(vici_message_t *this, char *def, char *fmt, va_list args); + + /** + * Get the value of a key/value pair as integer. + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param ... arguments to fmt string + * @return value + */ + int (*get_int)(vici_message_t *this, int def, char *fmt, ...); + + /** + * Get the value of a key/value pair as integer, va_list variant + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param args arguments to fmt string + * @return value + */ + int (*vget_int)(vici_message_t *this, int def, char *fmt, va_list args); + + /** + * Get the raw value of a key/value pair. + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param ... arguments to fmt string + * @return value + */ + chunk_t (*get_value)(vici_message_t *this, chunk_t def, char *fmt, ...); + + /** + * Get the raw value of a key/value pair, va_list variant. + * + * @param def default value if not found + * @param fmt printf style format string for key, with sections + * @param args arguments to fmt string + * @return value + */ + chunk_t (*vget_value)(vici_message_t *this, chunk_t def, + char *fmt, va_list args); + /** * Get encoded message. *