diff options
author | Suchang Woo <suchang.woo@samsung.com> | 2015-07-16 17:12:54 +0900 |
---|---|---|
committer | Suchang Woo <suchang.woo@samsung.com> | 2015-07-16 17:28:30 +0900 |
commit | d966429452d4ff6a3527f0deb8db63be5b87f720 (patch) | |
tree | a811fe0a17e1c6b69868cf1176eb99815227238b /common/serialize.c | |
parent | 99b1d99a4f93a6bf0786abc6ae5ad1f3d9415933 (diff) | |
download | buxton2-d966429452d4ff6a3527f0deb8db63be5b87f720.tar.gz buxton2-d966429452d4ff6a3527f0deb8db63be5b87f720.tar.bz2 buxton2-d966429452d4ff6a3527f0deb8db63be5b87f720.zip |
Signed-off-by: Suchang Woo <suchang.woo@samsung.com>
Diffstat (limited to 'common/serialize.c')
-rw-r--r-- | common/serialize.c | 970 |
1 files changed, 970 insertions, 0 deletions
diff --git a/common/serialize.c b/common/serialize.c new file mode 100644 index 0000000..2cda9c1 --- /dev/null +++ b/common/serialize.c @@ -0,0 +1,970 @@ +/* + * Buxton + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#include <glib.h> + +#include "buxton2.h" + +#include "serialize.h" +#include "log.h" + +#define KEY_NAME_MAX 4096 +#define VALUE_MAX 4096 + +int check_key_name(const char *key) +{ + const char *p; + int len; + + len = 0; + p = key; + while (*p) { + /* from 0x21 '!' to 0x7e '~' */ + if (*p < 0x21 || *p > 0x7e) { + errno = EINVAL; + bxt_err("Key name has invalid character '%x'", *p); + return -1; + } + p++; + len++; + if (len > KEY_NAME_MAX) { + errno = ENAMETOOLONG; + bxt_err("Key name is too long"); + return -1; + } + } + + return 0; +} + +static inline int check_val(const char *s) +{ + if (!s) + return 0; + + if (strlen(s) > VALUE_MAX) { + errno = EMSGSIZE; + return -1; + } + + return 0; +} + +static int check_values(const char *rpriv, const char *wpriv, + const struct buxton_value *val) +{ + int r; + + r = check_val(rpriv); + if (r == -1) { + bxt_err("Read priv. string length is too long"); + return -1; + } + + r = check_val(wpriv); + if (r == -1) { + bxt_err("Write priv. string length is too long"); + return -1; + } + + if (val && val->type == BUXTON_TYPE_STRING) { + r = check_val(val->value.s); + if (r == -1) { + bxt_err("Value string length is too long"); + return -1; + } + } + + return 0; +} + +static GVariant *val_to_gv(const struct buxton_value *val) +{ + GVariant *gv; + + if (!val) + return g_variant_new_tuple(NULL, 0); + + switch (val->type) { + case BUXTON_TYPE_STRING: + if (!val->value.s) { + bxt_err("Serialize: value has NULL string"); + return NULL; + } + + gv = g_variant_new_string(val->value.s); + break; + case BUXTON_TYPE_INT32: + gv = g_variant_new_int32(val->value.i); + break; + case BUXTON_TYPE_UINT32: + gv = g_variant_new_uint32(val->value.u); + break; + case BUXTON_TYPE_INT64: + gv = g_variant_new_int64(val->value.i64); + break; + case BUXTON_TYPE_UINT64: + gv = g_variant_new_uint64(val->value.u64); + break; + case BUXTON_TYPE_DOUBLE: + gv = g_variant_new_double(val->value.d); + break; + case BUXTON_TYPE_BOOLEAN: + gv = g_variant_new_boolean(val->value.b); + break; + default: + bxt_err("Serialize: Invalid value type: %d", val->type); + gv = NULL; + break; + } + + return gv; +} + +static uint8_t *gv_to_data(GVariant *gv, int *len) +{ + uint8_t *data; + int _len; + + assert(gv); + assert(len); + + _len = g_variant_get_size(gv); + assert(_len > 0); + + data = malloc(_len); + if (!data) + return NULL; + + g_variant_store(gv, data); + + *len = _len; + + return data; +} + +/* + * Data format = v Variant v + * + * In an initial version, + * Variant v = (ssv) read privilege s, write priv. w, value v + * + */ +int serialz_data(const char *rpriv, const char *wpriv, + const struct buxton_value *val, + uint8_t **data, int *len) +{ + GVariant *gv; + GVariant *vv; + GVariant *v; + int _len; + uint8_t *_data; + int r; + + if (!rpriv || !wpriv || !val || !data || !len) { + errno = EINVAL; + bxt_err("serialize data: invalid argument:%s%s%s%s%s", + rpriv ? "" : " read priv", + wpriv ? "" : " write priv", + val ? "" : " value", + data ? "" : " data", + len ? "" : " len"); + + return -1; + } + + r = check_values(rpriv, wpriv, val); + if (r == -1) + return -1; + + v = val_to_gv(val); + if (!v) { + errno = EINVAL; + return -1; + } + + vv = g_variant_new("(ssv)", rpriv, wpriv, v); + assert(vv); + + gv = g_variant_new_variant(vv); + assert(gv); + + _data = gv_to_data(gv, &_len); + + g_variant_unref(gv); + + if (!_data) + return -1; + + *data = _data; + *len = _len; + + return 0; +} + +static int gv_to_val(GVariant *v, struct buxton_value *val) +{ + const char *t; + const char *s; + + assert(v); + assert(val); + + t = g_variant_get_type_string(v); + assert(t); + + if (!strncmp(t, "()", sizeof("()"))) { + val->type = BUXTON_TYPE_UNKNOWN; + return 0; + } + + switch (*t) { + case 's': + val->type = BUXTON_TYPE_STRING; + + s = g_variant_get_string(v, NULL); + assert(s); + + val->value.s = strdup(s); + if (!val->value.s) + return -1; + + break; + case 'i': + val->type = BUXTON_TYPE_INT32; + val->value.i = g_variant_get_int32(v); + break; + case 'u': + val->type = BUXTON_TYPE_UINT32; + val->value.u = g_variant_get_uint32(v); + break; + case 'x': + val->type = BUXTON_TYPE_INT64; + val->value.i64 = g_variant_get_int64(v); + break; + case 't': + val->type = BUXTON_TYPE_UINT64; + val->value.u64 = g_variant_get_uint64(v); + break; + case 'd': + val->type = BUXTON_TYPE_DOUBLE; + val->value.d = g_variant_get_double(v); + break; + case 'b': + val->type = BUXTON_TYPE_BOOLEAN; + val->value.b = g_variant_get_boolean(v); + break; + default: + bxt_err("DeSerialz: Invalid variant type: %s", t); + errno = EBADMSG; + + return -1; + } + + return 0; +} + +static int gv_to_values(GVariant *gv, char **rpriv, char **wpriv, + struct buxton_value *val) +{ + GVariant *v; + const char *vt; + const char *rp; + const char *wp; + int r; + + assert(gv); + + if (!rpriv && !wpriv && !val) + return 0; + + vt = g_variant_get_type_string(gv); + if (strncmp(vt, "(ssv)", sizeof("(ssv)"))) { + bxt_err("Deserialize: Unsupported type: %s", vt); + errno = EBADMSG; + return -1; + } + + g_variant_get(gv, "(&s&sv)", &rp, &wp, &v); + assert(rp); + assert(wp); + assert(v); + + if (rpriv) { + *rpriv = strdup(rp); + if (!*rpriv) { + g_variant_unref(v); + return -1; + } + } + + if (wpriv) { + *wpriv = strdup(wp); + if (!*wpriv) { + if (rpriv) + free(*rpriv); + + g_variant_unref(v); + return -1; + } + } + + if (val) { + memset(val, 0, sizeof(*val)); + r = gv_to_val(v, val); + if (r == -1) { + if (rpriv) + free(*rpriv); + + if (wpriv) + free(*wpriv); + + g_variant_unref(v); + return -1; + } + } + + g_variant_unref(v); + + return 0; +} + +int deserialz_data(uint8_t *data, int len, + char **rpriv, char **wpriv, struct buxton_value *val) +{ + GVariant *gv; + GVariant *v; + char *_rpriv; + char *_wpriv; + struct buxton_value _val; + int r; + + if (!data || len <= 0) { + errno = EINVAL; + bxt_err("Deserialize data: invalid argument:%s%s", + data ? "" : " data", len > 0 ? "" : " len"); + return -1; + } + + gv = g_variant_new_from_data(G_VARIANT_TYPE("v"), + data, len, TRUE, NULL, NULL); + assert(gv); + + g_variant_get(gv, "v", &v); + assert(v); + + r = gv_to_values(v, + rpriv ? &_rpriv : NULL, + wpriv ? &_wpriv : NULL, + val ? &_val : NULL); + + g_variant_unref(v); + g_variant_unref(gv); + + if (r == -1) + return -1; + + if (rpriv) + *rpriv = _rpriv; + + if (wpriv) + *wpriv = _wpriv; + + if (val) + *val = _val; + + return 0; +} + +void free_request(struct request *req) +{ + if (!req) + return; + + layer_free(req->layer); + free(req->rpriv); + free(req->wpriv); + free(req->key); + value_free(req->val); + free(req->val); +} + +static int check_value(const struct buxton_value *val) +{ + if (!val) { + bxt_err("Serialize: value is NULL"); + return -1; + } + + switch (val->type) { + case BUXTON_TYPE_STRING: + if (!val->value.s) { + bxt_err("Serialize: value has NULL string"); + return -1; + } + break; + case BUXTON_TYPE_INT32: + case BUXTON_TYPE_UINT32: + case BUXTON_TYPE_INT64: + case BUXTON_TYPE_UINT64: + case BUXTON_TYPE_DOUBLE: + case BUXTON_TYPE_BOOLEAN: + break; + default: + bxt_err("Serialize: buxton_value has unknown type"); + return -1; + } + + return 0; +} + +static int check_request(enum message_type type, + const char *key, const struct buxton_value *val) +{ + int r; + + switch (type) { + case MSG_SET: + case MSG_CREAT: + case MSG_NOTI: + case MSG_SET_WP: + case MSG_SET_RP: + r = check_value(val); + if (r == -1) + goto err; + case MSG_GET: + case MSG_UNSET: + case MSG_NOTIFY: + case MSG_UNNOTIFY: + case MSG_GET_WP: + case MSG_GET_RP: + if (!key || !*key) { + bxt_err("Serialize: key is NULL or empty string"); + goto err; + } + + r = check_key_name(key); + if (r == -1) + return -1; + case MSG_LIST: + break; + default: + bxt_err("Serialize: message type is invalid: %d", type); + goto err; + } + + return 0; + +err: + errno = EINVAL; + + return -1; +} + +int serialz_request(const struct request *req, uint8_t **data, int *len) +{ + int r; + GVariant *gv; + GVariant *vv; + GVariant *v; + int _len; + uint8_t *_data; + + if (!data || !len || !req || !req->layer) { + errno = EINVAL; + bxt_err("Serialize request: invalid argument:%s%s%s%s", + data ? "" : " data", + len ? "" : " len", + req ? "" : " req", + req->layer ? "" : " layer"); + return -1; + } + + r = check_request(req->type, req->key, req->val); + if (r == -1) + return -1; + + v = val_to_gv(req->val); + if (!v) { + errno = EINVAL; + return -1; + } + + vv = g_variant_new("(uuissssv)", + req->msgid, + req->layer->uid, + req->layer->type, + req->layer->name, + req->rpriv ? req->rpriv : "", + req->wpriv ? req->wpriv : "", + req->key ? req->key : "", + v); + assert(vv); + + gv = g_variant_new("(qv)", req->type, vv); + assert(gv); + + _data = gv_to_data(gv, &_len); + + g_variant_unref(gv); + + if (!_data) + return -1; + + *data = _data; + *len = _len; + + return 0; +} + +static inline int _strdup(const char *src, char **dest) +{ + char *s; + + assert(dest); + + if (!src) { + *dest = NULL; + return 0; + } + + s = strdup(src); + if (!s) + return -1; + + *dest = s; + + return 0; +} + +static int set_req(struct buxton_value *val, const char *lnm, uid_t uid, + enum buxton_layer_type type, const char *rp, const char *wp, + const char *key, struct request *req) +{ + int r; + + assert(req); + + req->val = val; + + if (lnm && *lnm) { + req->layer = layer_create(lnm); + if (!req->layer) + return -1; + + req->layer->uid = uid; + req->layer->type = type; + } else { + req->layer = NULL; + } + + r = _strdup(rp, &req->rpriv); + if (r == -1) + return -1; + + r = _strdup(wp, &req->wpriv); + if (r == -1) + return -1; + + r = _strdup(key, &req->key); + if (r == -1) + return -1; + + return 0; +} + +static int gv_to_req(GVariant *gv, struct request *req) +{ + const char *vt; + uint32_t uid; + int32_t type; + const char *lnm; + const char *key; + const char *rp; + const char *wp; + GVariant *v; + int r; + struct buxton_value *val; + + assert(gv); + assert(req); + + vt = g_variant_get_type_string(gv); + if (strncmp(vt, "(uuissssv)", sizeof("(uuissssv)"))) { + bxt_err("DeSerialz: Unsupported type: %s", vt); + errno = EBADMSG; + return -1; + } + + val = calloc(1, sizeof(*val)); + if (!val) + return -1; + + g_variant_get(gv, "(uui&s&s&s&sv)", &req->msgid, &uid, &type, + &lnm, &rp, &wp, &key, &v); + assert(v); + assert(lnm); + assert(rp); + assert(wp); + assert(key); + + r = gv_to_val(v, val); + + g_variant_unref(v); + + if (r == -1) { + free(val); + return -1; + } + + if (val->type == BUXTON_TYPE_UNKNOWN) { + free(val); + val = NULL; + } + + r = set_req(val, lnm, uid, type, rp, wp, key, req); + if (r == -1) + free_request(req); + + return r; +} + +int deserialz_request(uint8_t *data, int len, struct request *req) +{ + GVariant *gv; + GVariant *v; + int r; + struct request _req; + + if (!data || len <= 0 || !req) { + errno = EINVAL; + bxt_err("Deserialize request: invalid argument:%s%s%s", + data ? "" : " data", + len > 0 ? "" : " len", + req ? "" : " req"); + return -1; + } + + gv = g_variant_new_from_data(G_VARIANT_TYPE("(qv)"), + data, len, TRUE, NULL, NULL); + assert(gv); + + memset(&_req, 0, sizeof(_req)); + + g_variant_get(gv, "(qv)", &_req.type, &v); + assert(v); + + r = gv_to_req(v, &_req); + + g_variant_unref(v); + g_variant_unref(gv); + + if (r == -1) + return -1; + + *req = _req; + + return 0; +} + +void free_response(struct response *res) +{ + if (!res) + return; + + value_free(res->val); + free(res->val); + buxton_free_keys(res->names); +} + +static int check_response(enum message_type type, int32_t res, + const struct buxton_value *val, char * const *names) +{ + int r; + + if (res) + return 0; + + switch (type) { + case MSG_LIST: + if (!names) { + bxt_err("Serialize: names is NULL"); + goto err; + } + break; + case MSG_GET: + case MSG_GET_WP: + case MSG_GET_RP: + r = check_value(val); + if (r == -1) + goto err; + break; + case MSG_SET: + case MSG_CREAT: + case MSG_UNSET: + case MSG_NOTIFY: + case MSG_UNNOTIFY: + case MSG_SET_WP: + case MSG_SET_RP: + break; + case MSG_NOTI: + errno = ENOTSUP; + bxt_err("Serialize: MSG_NOTI type has no response"); + return -1; + default: + goto err; + } + + return 0; + +err: + errno = EINVAL; + + return -1; +} + +static int res_to_gv(enum message_type type, int32_t res, + const struct buxton_value *val, char * const *names, + GVariant **gv) +{ + GVariantBuilder *builder; + GVariant *v; + + assert(gv); + + if (res) { + *gv = g_variant_new_tuple(NULL, 0); + return 0; + } + + switch (type) { + case MSG_LIST: + builder = g_variant_builder_new(G_VARIANT_TYPE("as")); + assert(names); + while (*names) { + g_variant_builder_add(builder, "s", *names); + names++; + } + v = g_variant_new("as", builder); + assert(v); + g_variant_builder_unref(builder); + break; + case MSG_GET: + case MSG_GET_WP: + case MSG_GET_RP: + if (val) { + v = val_to_gv(val); + if (!v) { + errno = EINVAL; + return -1; + } + } else { + v = g_variant_new_tuple(NULL, 0); + } + break; + default: + v = g_variant_new_tuple(NULL, 0); + break; + } + + *gv = v; + + return 0; +} + +int serialz_response(enum message_type type, uint32_t msgid, int32_t res, + const struct buxton_value *val, uint32_t nmlen, + char * const *names, uint8_t **data, int *len) +{ + int r; + GVariant *gv; + GVariant *vv; + GVariant *v; + int _len; + uint8_t *_data; + + if (!data || !len) { + errno = EINVAL; + bxt_err("Serialize response: invalid argument:%s%s", + data ? "" : " data", + len ? "" : " len"); + return -1; + } + + r = check_response(type, res, val, names); + if (r == -1) + return -1; + + r = res_to_gv(type, res, val, names, &v); + if (r == -1) + return -1; + + assert(v); + vv = g_variant_new("(uiuv)", msgid, res, nmlen, v); + assert(vv); + + gv = g_variant_new("(qv)", type, vv); + assert(gv); + + _data = gv_to_data(gv, &_len); + + g_variant_unref(gv); + + if (!_data) + return -1; + + *data = _data; + *len = _len; + + return 0; +} + +static int gv_to_res_list(GVariant *gv, struct response *res) +{ + GVariantIter iter; + gsize len; + const char *s; + int i; + + g_variant_iter_init(&iter, gv); + len = g_variant_iter_n_children(&iter); + assert(len >= 0); + + res->names = calloc(len + 1, sizeof(void *)); + if (!res->names) + return -1; + + i = 0; + while (g_variant_iter_next(&iter, "&s", &s)) { + assert(s); + res->names[i] = strdup(s); + if (!res->names[i]) + break; + i++; + + assert(i <= len); + } + /* NULL terminated */ + res->names[i] = NULL; + + if (i < len) { + buxton_free_keys(res->names); + return -1; + } + + return 0; +} + +static int gv_to_res(GVariant *gv, struct response *res) +{ + const char *vt; + GVariant *v; + struct buxton_value *val; + int r; + + assert(gv); + assert(res); + + vt = g_variant_get_type_string(gv); + if (strncmp(vt, "(uiuv)", sizeof("(uiuv)"))) { + bxt_err("DeSerialz: Unsupported type: %s", vt); + errno = EBADMSG; + return -1; + } + + g_variant_get(gv, "(uiuv)", &res->msgid, &res->res, &res->nmlen, &v); + + if (res->res) + return 0; + + if (res->type == MSG_LIST) { + r = gv_to_res_list(v, res); + g_variant_unref(v); + return r; + } + + val = calloc(1, sizeof(*val)); + if (!val) { + g_variant_unref(v); + return -1; + } + + r = gv_to_val(v, val); + if (r == -1) { + free(val); + g_variant_unref(v); + return -1; + } + + g_variant_unref(v); + + if (val->type == BUXTON_TYPE_UNKNOWN) { + free(val); + val = NULL; + } + + res->val = val; + + return 0; +} + +int deserialz_response(uint8_t *data, int len, struct response *res) +{ + GVariant *gv; + GVariant *v; + int r; + struct response _res; + + if (!data || len <= 0 || !res) { + errno = EINVAL; + bxt_err("Deserialize response: invalid argument:%s%s%s", + data ? "" : " data", + len > 0 ? "" : " len", + res ? "" : " response"); + return -1; + } + + gv = g_variant_new_from_data(G_VARIANT_TYPE("(qv)"), + data, len, TRUE, NULL, NULL); + assert(gv); + + memset(&_res, 0, sizeof(_res)); + + g_variant_get(gv, "(qv)", &_res.type, &v); + assert(v); + + r = gv_to_res(v, &_res); + + g_variant_unref(v); + g_variant_unref(gv); + + if (r == -1) { + free_response(&_res); + return -1; + } + + *res = _res; + + return 0; +} + |