diff options
Diffstat (limited to 'src/lib/sifp/sss_sifp_parser.c')
-rw-r--r-- | src/lib/sifp/sss_sifp_parser.c | 723 |
1 files changed, 723 insertions, 0 deletions
diff --git a/src/lib/sifp/sss_sifp_parser.c b/src/lib/sifp/sss_sifp_parser.c new file mode 100644 index 0000000..150bd56 --- /dev/null +++ b/src/lib/sifp/sss_sifp_parser.c @@ -0,0 +1,723 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2014 Red Hat + + 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 3 of the License, or + (at your option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <dbus/dbus.h> +#include <string.h> +#include <dhash.h> + +#include "lib/sifp/sss_sifp.h" +#include "lib/sifp/sss_sifp_private.h" + +#define check_dbus_arg(iter, type, ret, done) do { \ + if (dbus_message_iter_get_arg_type((iter)) != (type)) { \ + ret = SSS_SIFP_INTERNAL_ERROR; \ + goto done; \ + } \ +} while (0) + +#define parse_basic(ctx, iter, ret, attr_type, dbus_type, \ + data_type, field, done) \ +do { \ + dbus_type val; \ + dbus_message_iter_get_basic(iter, &val); \ + attr->type = attr_type; \ + attr->data.field = _alloc_zero(ctx, data_type, 1); \ + \ + if (attr->data.field == NULL) { \ + ret = SSS_SIFP_OUT_OF_MEMORY; \ + goto done; \ + } \ + \ + attr->data.field[0] = val; \ + attr->num_values = 1; \ + \ + ret = SSS_SIFP_OK; \ +} while (0) + +#define parse_array(ctx, iter, ret, attr_type, dbus_type, \ + data_type, field, done) \ +do { \ + dbus_type val; \ + unsigned int i; \ + \ + attr->type = attr_type; \ + if (attr->num_values == 0) { \ + attr->data.field = NULL; \ + ret = SSS_SIFP_OK; \ + goto done; \ + } \ + \ + attr->data.field = _alloc_zero(ctx, data_type, attr->num_values); \ + if (attr->data.field == NULL) { \ + ret = SSS_SIFP_OUT_OF_MEMORY; \ + goto done; \ + } \ + \ + for (i = 0; i < attr->num_values; i++) { \ + dbus_message_iter_get_basic(iter, &val); \ + attr->data.field[i] = val; \ + \ + if (!dbus_message_iter_next(iter) && i + 1 < attr->num_values) { \ + ret = SSS_SIFP_INTERNAL_ERROR; \ + goto done; \ + } \ + } \ + \ + ret = SSS_SIFP_OK; \ +} while (0) + +static unsigned int +sss_sifp_get_array_length(DBusMessageIter *iter) +{ + DBusMessageIter array_iter; + unsigned int size; + + dbus_message_iter_recurse(iter, &array_iter); + + if (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_INVALID) { + return 0; + } + + size = 0; + do { + size++; + } while (dbus_message_iter_next(&array_iter)); + + return size; +} + +static void hash_delete_cb(hash_entry_t *item, + hash_destroy_enum type, + void *pvt) +{ + sss_sifp_ctx *ctx = (sss_sifp_ctx*)pvt; + char **values = (char**)(item->value.ptr); + int i; + + if (values == NULL) { + return; + } + + for (i = 0; values[i] != NULL; i++) { + _free(ctx, values[i]); + values[i] = NULL; + } + + _free(ctx, values); + item->value.ptr = NULL; +} + +static sss_sifp_error +sss_sifp_parse_dict(sss_sifp_ctx *ctx, + DBusMessageIter *iter, + hash_table_t *table) +{ + DBusMessageIter dict_iter; + DBusMessageIter array_iter; + sss_sifp_error ret; + hash_key_t table_key = {0}; + hash_value_t table_value; + const char *key = NULL; + const char *value = NULL; + char **values = NULL; + unsigned int i; + unsigned int num_values; + int hret; + + dbus_message_iter_recurse(iter, &dict_iter); + + /* get the key */ + check_dbus_arg(&dict_iter, DBUS_TYPE_STRING, ret, done); + dbus_message_iter_get_basic(&dict_iter, &key); + + table_key.type = HASH_KEY_STRING; + table_key.str = sss_sifp_strdup(ctx, key); + if (table_key.str == NULL) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + if (!dbus_message_iter_next(&dict_iter)) { + ret = SSS_SIFP_INTERNAL_ERROR; + goto done; + } + + /* now read the value */ + switch (dbus_message_iter_get_arg_type(&dict_iter)) { + case DBUS_TYPE_STRING: + dbus_message_iter_get_basic(&dict_iter, &value); + values = _alloc_zero(ctx, char *, 2); + if (values == NULL) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + values[0] = sss_sifp_strdup(ctx, value); + if (values[0] == NULL) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + values[1] = NULL; + + ret = SSS_SIFP_OK; + break; + case DBUS_TYPE_ARRAY: + num_values = sss_sifp_get_array_length(&dict_iter); + if (num_values == 0) { + values = NULL; + ret = SSS_SIFP_OK; + goto done; + } + + if (dbus_message_iter_get_element_type(&dict_iter) + != DBUS_TYPE_STRING) { + ret = SSS_SIFP_NOT_SUPPORTED; + goto done; + } + + dbus_message_iter_recurse(&dict_iter, &array_iter); + + values = _alloc_zero(ctx, char*, num_values + 1); + if (values == NULL) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + for (i = 0; i < num_values; i++) { + dbus_message_iter_get_basic(&array_iter, &value); + values[i] = sss_sifp_strdup(ctx, value); + if (values[i] == NULL) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + dbus_message_iter_next(&array_iter); + } + + ret = SSS_SIFP_OK; + break; + default: + ret = SSS_SIFP_NOT_SUPPORTED; + break; + } + + table_value.type = HASH_VALUE_PTR; + table_value.ptr = values; + + hret = hash_enter(table, &table_key, &table_value); + if (hret == HASH_ERROR_NO_MEMORY) { + ret = SSS_SIFP_OUT_OF_MEMORY; + } else if (hret != HASH_SUCCESS) { + ret = SSS_SIFP_INTERNAL_ERROR; + } + +done: + if (table_key.str != NULL) { + _free(ctx, table_key.str); + } + + if (ret != SSS_SIFP_OK) { + if (values != NULL) { + for (i = 0; values[i] != NULL; i++) { + _free(ctx, values[i]); + } + _free(ctx, values); + } + } + + return ret; +} + +static sss_sifp_error +sss_sifp_parse_basic(sss_sifp_ctx *ctx, + DBusMessageIter *iter, + sss_sifp_attr *attr) +{ + sss_sifp_error ret; + + switch (dbus_message_iter_get_arg_type(iter)) { + case DBUS_TYPE_BOOLEAN: + parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_BOOL, + dbus_bool_t, bool, boolean, done); + break; + case DBUS_TYPE_INT16: + parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_INT16, + int16_t, int16_t, int16, done); + break; + case DBUS_TYPE_UINT16: + parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_UINT16, + uint16_t, uint16_t, uint16, done); + break; + case DBUS_TYPE_INT32: + parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_INT32, + int32_t, int32_t, int32, done); + break; + case DBUS_TYPE_UINT32: + parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_UINT32, + uint32_t, uint32_t, uint32, done); + break; + case DBUS_TYPE_INT64: + parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_INT64, + int64_t, int64_t, int64, done); + break; + case DBUS_TYPE_UINT64: + parse_basic(ctx, iter, ret, SSS_SIFP_ATTR_TYPE_UINT64, + uint64_t, uint64_t, uint64, done); + break; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + { + const char *val = NULL; + + dbus_message_iter_get_basic(iter, &val); + + attr->type = SSS_SIFP_ATTR_TYPE_STRING; + attr->data.str = _alloc_zero(ctx, char*, 1); + if (attr->data.str == NULL) { \ + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + attr->data.str[0] = sss_sifp_strdup(ctx, val); + if (attr->data.str[0] == NULL) { + _free(ctx, attr->data.str); + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + attr->num_values = 1; + + ret = SSS_SIFP_OK; + break; + } + default: + ret = SSS_SIFP_INVALID_ARGUMENT; + break; + } + +done: + return ret; +} + +static sss_sifp_error +sss_sifp_parse_array(sss_sifp_ctx *ctx, + DBusMessageIter *iter, + sss_sifp_attr *attr) +{ + DBusMessageIter array_iter; + sss_sifp_error ret; + int hret; + + attr->num_values = sss_sifp_get_array_length(iter); + dbus_message_iter_recurse(iter, &array_iter); + + switch (dbus_message_iter_get_element_type(iter)) { + case DBUS_TYPE_BOOLEAN: + parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_BOOL, + dbus_bool_t, bool, boolean, done); + break; + case DBUS_TYPE_INT16: + parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_INT16, + int16_t, int16_t, int16, done); + break; + case DBUS_TYPE_UINT16: + parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_UINT16, + uint16_t, uint16_t, uint16, done); + break; + case DBUS_TYPE_INT32: + parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_INT32, + int32_t, int32_t, int32, done); + break; + case DBUS_TYPE_UINT32: + parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_UINT32, + uint32_t, uint32_t, uint32, done); + break; + case DBUS_TYPE_INT64: + parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_INT64, + int64_t, int64_t, int64, done); + break; + case DBUS_TYPE_UINT64: + parse_array(ctx, &array_iter, ret, SSS_SIFP_ATTR_TYPE_UINT64, + uint64_t, uint64_t, uint64, done); + break; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: ; + const char *val; + unsigned int i; + + attr->type = SSS_SIFP_ATTR_TYPE_STRING; + if (attr->num_values == 0) { + attr->data.str = NULL; + ret = SSS_SIFP_OK; + goto done; + } + + attr->data.str = _alloc_zero(ctx, char *, attr->num_values); + if (attr->data.str == NULL) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + for (i = 0; i < attr->num_values; i++) { + dbus_message_iter_get_basic(&array_iter, &val); + attr->data.str[i] = sss_sifp_strdup(ctx, val); + if (attr->data.str[i] == NULL) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + if (!dbus_message_iter_next(&array_iter) + && i + 1 < attr->num_values) { + ret = SSS_SIFP_INTERNAL_ERROR; + goto done; + } + } + + ret = SSS_SIFP_OK; + break; + case DBUS_TYPE_DICT_ENTRY: + attr->type = SSS_SIFP_ATTR_TYPE_STRING_DICT; + if (attr->num_values == 0) { + attr->data.str_dict = NULL; + ret = SSS_SIFP_OK; + goto done; + } + + hret = hash_create_ex(0, &(attr->data.str_dict), 0, 0, 0, 0, + ctx->alloc_fn, ctx->free_fn, ctx->alloc_pvt, + hash_delete_cb, ctx); + if (hret != HASH_SUCCESS) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + for (i = 0; i < attr->num_values; i++) { + ret = sss_sifp_parse_dict(ctx, &array_iter, attr->data.str_dict); + if (ret != SSS_SIFP_OK) { + _free(ctx, attr->data.str_dict); + goto done; + } + + if (!dbus_message_iter_next(&array_iter) + && i + 1 < attr->num_values) { + ret = SSS_SIFP_INTERNAL_ERROR; + goto done; + } + } + + ret = SSS_SIFP_OK; + break; + default: + ret = SSS_SIFP_INVALID_ARGUMENT; + break; + } + +done: + if (ret != SSS_SIFP_OK) { + if (attr->type == SSS_SIFP_ATTR_TYPE_STRING && attr->data.str != NULL) { + for (unsigned int i = 0; + (i < attr->num_values) && (attr->data.str[i] != NULL); + i++) { + _free(ctx, attr->data.str[i]); + } + _free(ctx, attr->data.str); + } else if (attr->type == SSS_SIFP_ATTR_TYPE_STRING_DICT + && attr->data.str_dict != NULL) { + hash_destroy(attr->data.str_dict); + attr->data.str_dict = NULL; + } + } + + return ret; +} + +static sss_sifp_error +sss_sifp_parse_variant(sss_sifp_ctx *ctx, + DBusMessageIter *iter, + sss_sifp_attr *attr) +{ + DBusMessageIter variant_iter; + sss_sifp_error ret; + int type; + + check_dbus_arg(iter, DBUS_TYPE_VARIANT, ret, done); + + dbus_message_iter_recurse(iter, &variant_iter); + + type = dbus_message_iter_get_arg_type(&variant_iter); + if (dbus_type_is_basic(type)) { + ret = sss_sifp_parse_basic(ctx, &variant_iter, attr); + } else { + /* container types */ + switch (type) { + /* case DBUS_TYPE_DICT_ENTRY may only be contained within an array + * in variant */ + case DBUS_TYPE_ARRAY: + ret = sss_sifp_parse_array(ctx, &variant_iter, attr); + break; + default: + ret = SSS_SIFP_NOT_SUPPORTED; + break; + } + } + +done: + return ret; +} + +/** + * DBusMessage format: + * variant:value + * + * Iterator has to point to the variant but not inside the variant. + */ +static sss_sifp_error +sss_sifp_parse_single_attr(sss_sifp_ctx *ctx, + const char *name, + DBusMessageIter *iter, + sss_sifp_attr **_attr) +{ + sss_sifp_attr *attr = NULL; + sss_sifp_error ret; + + attr = _alloc_zero(ctx, sss_sifp_attr, 1); + if (attr == NULL) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + attr->name = sss_sifp_strdup(ctx, name); + if (attr->name == NULL) { + _free(ctx, attr); + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + ret = sss_sifp_parse_variant(ctx, iter, attr); + if (ret != SSS_SIFP_OK) { + _free(ctx, attr->name); + _free(ctx, attr); + } + + *_attr = attr; + +done: + return ret; +} + +/** + * DBusMessage format: + * variant:value + */ +sss_sifp_error +sss_sifp_parse_attr(sss_sifp_ctx *ctx, + const char *name, + DBusMessage *msg, + sss_sifp_attr ***_attrs) +{ + sss_sifp_attr **attrs = NULL; + DBusMessageIter iter; + sss_sifp_error ret; + + dbus_message_iter_init(msg, &iter); + + attrs = _alloc_zero(ctx, sss_sifp_attr *, 2); + if (attrs == NULL) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + ret = sss_sifp_parse_single_attr(ctx, name, &iter, &attrs[0]); + if (ret != SSS_SIFP_OK) { + goto done; + } + + *_attrs = attrs; + + ret = SSS_SIFP_OK; + +done: + if (ret != SSS_SIFP_OK) { + sss_sifp_free_attrs(ctx, &attrs); + } + + return ret; +} + +/** + * DBusMessage format: + * array of dict_entry(string:attr_name, variant:value) + */ +sss_sifp_error +sss_sifp_parse_attr_list(sss_sifp_ctx *ctx, + DBusMessage *msg, + sss_sifp_attr ***_attrs) +{ + DBusMessageIter iter; + DBusMessageIter array_iter; + DBusMessageIter dict_iter; + sss_sifp_attr **attrs = NULL; + const char *name = NULL; + unsigned int num_values; + sss_sifp_error ret; + unsigned int i; + + dbus_message_iter_init(msg, &iter); + + check_dbus_arg(&iter, DBUS_TYPE_ARRAY, ret, done); + + if (dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) { + ret = SSS_SIFP_INTERNAL_ERROR; + goto done; + } + + num_values = sss_sifp_get_array_length(&iter); + attrs = _alloc_zero(ctx, sss_sifp_attr *, num_values + 1); + if (attrs == NULL) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + dbus_message_iter_recurse(&iter, &array_iter); + + for (i = 0; i < num_values; i++) { + dbus_message_iter_recurse(&array_iter, &dict_iter); + + /* get the key */ + check_dbus_arg(&dict_iter, DBUS_TYPE_STRING, ret, done); + dbus_message_iter_get_basic(&dict_iter, &name); + + if (!dbus_message_iter_next(&dict_iter)) { + ret = SSS_SIFP_INTERNAL_ERROR; + goto done; + } + + /* now read the value */ + check_dbus_arg(&dict_iter, DBUS_TYPE_VARIANT, ret, done); + + ret = sss_sifp_parse_single_attr(ctx, name, &dict_iter, &attrs[i]); + if (ret != SSS_SIFP_OK) { + goto done; + } + + dbus_message_iter_next(&array_iter); + } + + *_attrs = attrs; + ret = SSS_SIFP_OK; + +done: + if (ret != SSS_SIFP_OK) { + sss_sifp_free_attrs(ctx, &attrs); + } + + return ret; +} + +sss_sifp_error +sss_sifp_parse_object_path(sss_sifp_ctx *ctx, + DBusMessage *msg, + char **_object_path) +{ + char *object_path = NULL; + const char *dbus_path = NULL; + DBusError dbus_error; + dbus_bool_t bret; + sss_sifp_error ret; + + dbus_error_init(&dbus_error); + + bret = dbus_message_get_args(msg, &dbus_error, + DBUS_TYPE_OBJECT_PATH, &dbus_path, + DBUS_TYPE_INVALID); + if (!bret) { + sss_sifp_set_io_error(ctx, &dbus_error); + ret = SSS_SIFP_IO_ERROR; + goto done; + } + + object_path = sss_sifp_strdup(ctx, dbus_path); + if (object_path == NULL) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + *_object_path = object_path; + ret = SSS_SIFP_OK; + +done: + dbus_error_free(&dbus_error); + + return ret; +} + +sss_sifp_error +sss_sifp_parse_object_path_list(sss_sifp_ctx *ctx, + DBusMessage *msg, + char ***_object_paths) +{ + char **object_paths = NULL; + char **dbus_paths = NULL; + int num_paths; + DBusError dbus_error; + dbus_bool_t bret; + sss_sifp_error ret; + int i; + + dbus_error_init(&dbus_error); + + bret = dbus_message_get_args(msg, &dbus_error, + DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, + &dbus_paths, &num_paths, + DBUS_TYPE_INVALID); + if (!bret) { + sss_sifp_set_io_error(ctx, &dbus_error); + ret = SSS_SIFP_IO_ERROR; + goto done; + } + + object_paths = _alloc_zero(ctx, char *, num_paths + 1); + if (object_paths == NULL) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + + for (i = 0; i < num_paths; i++) { + object_paths[i] = sss_sifp_strdup(ctx, dbus_paths[i]); + if (object_paths[i] == NULL) { + ret = SSS_SIFP_OUT_OF_MEMORY; + goto done; + } + } + + *_object_paths = object_paths; + ret = SSS_SIFP_OK; + +done: + dbus_error_free(&dbus_error); + dbus_free_string_array(dbus_paths); + + if (ret != SSS_SIFP_OK && object_paths != NULL) { + sss_sifp_free_string_array(ctx, &object_paths); + } + + return ret; +} |