| /* |
| * 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_builder.h" |
| |
| #include <bio/bio_writer.h> |
| |
| typedef struct private_vici_builder_t private_vici_builder_t; |
| |
| /** |
| * Private data of an vici_builder_t object. |
| */ |
| struct private_vici_builder_t { |
| |
| /** |
| * Public vici_builder_t interface. |
| */ |
| vici_builder_t public; |
| |
| /** |
| * Writer for elements |
| */ |
| bio_writer_t *writer; |
| |
| /** |
| * Errors encountered |
| */ |
| u_int error; |
| |
| /** |
| * Section nesting level |
| */ |
| u_int section; |
| |
| /** |
| * In list element? |
| */ |
| bool list; |
| }; |
| |
| METHOD(vici_builder_t, add, void, |
| private_vici_builder_t *this, vici_type_t type, ...) |
| { |
| va_list args; |
| char *name = NULL; |
| chunk_t value = chunk_empty; |
| |
| va_start(args, type); |
| switch (type) |
| { |
| case VICI_SECTION_END: |
| case VICI_LIST_END: |
| case VICI_END: |
| break; |
| case VICI_LIST_START: |
| case VICI_SECTION_START: |
| name = va_arg(args, char*); |
| break; |
| case VICI_KEY_VALUE: |
| name = va_arg(args, char*); |
| value = va_arg(args, chunk_t); |
| break; |
| case VICI_LIST_ITEM: |
| value = va_arg(args, chunk_t); |
| break; |
| default: |
| va_end(args); |
| this->error++; |
| return; |
| } |
| va_end(args); |
| |
| if (value.len > 0xffff) |
| { |
| DBG1(DBG_ENC, "vici value exceeds size limit (%zu > %u)", |
| value.len, 0xffff); |
| this->error++; |
| return; |
| } |
| if (!vici_verify_type(type, this->section, this->list)) |
| { |
| this->error++; |
| return; |
| } |
| if (type != VICI_END) |
| { |
| this->writer->write_uint8(this->writer, type); |
| } |
| switch (type) |
| { |
| case VICI_SECTION_START: |
| this->writer->write_data8(this->writer, chunk_from_str(name)); |
| this->section++; |
| break; |
| case VICI_SECTION_END: |
| this->section--; |
| break; |
| case VICI_KEY_VALUE: |
| this->writer->write_data8(this->writer, chunk_from_str(name)); |
| this->writer->write_data16(this->writer, value); |
| break; |
| case VICI_LIST_START: |
| this->writer->write_data8(this->writer, chunk_from_str(name)); |
| this->list = TRUE; |
| break; |
| case VICI_LIST_ITEM: |
| this->writer->write_data16(this->writer, value); |
| break; |
| case VICI_LIST_END: |
| this->list = FALSE; |
| break; |
| default: |
| this->error++; |
| break; |
| } |
| } |
| |
| /** |
| * Add a list item or a key/value, if key given |
| */ |
| static void vadd_kv_or_li(private_vici_builder_t *this, char *key, |
| char *fmt, va_list args) |
| { |
| u_char buf[512]; |
| chunk_t value; |
| ssize_t len; |
| va_list copy; |
| |
| va_copy(copy, args); |
| len = vsnprintf(buf, sizeof(buf), fmt, copy); |
| va_end(copy); |
| if (len >= sizeof(buf)) |
| { |
| value = chunk_alloc(len + 1); |
| len = vsnprintf(value.ptr, value.len, fmt, args); |
| value.len = len; |
| } |
| else |
| { |
| value = chunk_create(buf, len); |
| } |
| |
| if (len < 0) |
| { |
| DBG1(DBG_ENC, "vici builder format print failed"); |
| this->error++; |
| } |
| else |
| { |
| if (key) |
| { |
| add(this, VICI_KEY_VALUE, key, value); |
| } |
| else |
| { |
| add(this, VICI_LIST_ITEM, value); |
| } |
| } |
| if (value.ptr != buf) |
| { |
| free(value.ptr); |
| } |
| } |
| |
| METHOD(vici_builder_t, vadd_kv, void, |
| private_vici_builder_t *this, char *key, char *fmt, va_list args) |
| { |
| vadd_kv_or_li(this, key, fmt, args); |
| } |
| |
| METHOD(vici_builder_t, add_kv, void, |
| private_vici_builder_t *this, char *key, char *fmt, ...) |
| { |
| va_list args; |
| |
| va_start(args, fmt); |
| vadd_kv(this, key, fmt, args); |
| va_end(args); |
| } |
| |
| METHOD(vici_builder_t, vadd_li, void, |
| private_vici_builder_t *this, char *fmt, va_list args) |
| { |
| vadd_kv_or_li(this, NULL, fmt, args); |
| } |
| |
| METHOD(vici_builder_t, add_li, void, |
| private_vici_builder_t *this, char *fmt, ...) |
| { |
| va_list args; |
| |
| va_start(args, fmt); |
| vadd_li(this, fmt, args); |
| va_end(args); |
| } |
| |
| METHOD(vici_builder_t, begin_section, void, |
| private_vici_builder_t *this, char *name) |
| { |
| add(this, VICI_SECTION_START, name); |
| } |
| |
| METHOD(vici_builder_t, end_section, void, |
| private_vici_builder_t *this) |
| { |
| add(this, VICI_SECTION_END); |
| } |
| |
| METHOD(vici_builder_t, begin_list, void, |
| private_vici_builder_t *this, char *name) |
| { |
| add(this, VICI_LIST_START, name); |
| } |
| |
| METHOD(vici_builder_t, end_list, void, |
| private_vici_builder_t *this) |
| { |
| add(this, VICI_LIST_END); |
| } |
| |
| METHOD(vici_builder_t, destroy, void, |
| private_vici_builder_t *this) |
| { |
| this->writer->destroy(this->writer); |
| free(this); |
| } |
| |
| METHOD(vici_builder_t, finalize, vici_message_t*, |
| private_vici_builder_t *this) |
| { |
| vici_message_t *product; |
| |
| if (this->error || this->section || this->list) |
| { |
| DBG1(DBG_ENC, "vici builder error: %u errors (section: %u, list %u)", |
| this->error, this->section, this->list); |
| destroy(this); |
| return NULL; |
| } |
| product = vici_message_create_from_data( |
| this->writer->extract_buf(this->writer), TRUE); |
| destroy(this); |
| return product; |
| } |
| |
| /** |
| * See header |
| */ |
| vici_builder_t *vici_builder_create() |
| { |
| private_vici_builder_t *this; |
| |
| INIT(this, |
| .public = { |
| .add = _add, |
| .add_kv = _add_kv, |
| .vadd_kv = _vadd_kv, |
| .add_li = _add_li, |
| .vadd_li = _vadd_li, |
| .begin_section = _begin_section, |
| .end_section = _end_section, |
| .begin_list = _begin_list, |
| .end_list = _end_list, |
| .finalize = _finalize, |
| .destroy = _destroy, |
| }, |
| .writer = bio_writer_create(0), |
| ); |
| |
| return &this->public; |
| } |