kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

commit e94cc2f3a861b46595d9b06c4e23eefdde2d37e8
parent 06fa452c96c86b8218bd11974346acb03a924998
Author: Joris Vink <joris@coders.se>
Date:   Sun, 20 Oct 2019 23:30:10 +0200

Yikes, add the actual JSON parser code..

Diffstat:
src/json.c | 713+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 713 insertions(+), 0 deletions(-)

diff --git a/src/json.c b/src/json.c @@ -0,0 +1,713 @@ +/* + * Copyright (c) 2019 Joris Vink <joris@coders.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> + +#include <float.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> + +#include "kore.h" + +static int json_guess_type(u_int8_t, int *); +static int json_next(struct kore_json *, u_int8_t *); +static int json_peek(struct kore_json *, u_int8_t *); + +static int json_consume_whitespace(struct kore_json *); +static int json_next_byte(struct kore_json *, u_int8_t *, int); + +static char *json_get_string(struct kore_json *); + +static int json_parse_array(struct kore_json *, struct kore_json_item *); +static int json_parse_object(struct kore_json *, struct kore_json_item *); +static int json_parse_string(struct kore_json *, struct kore_json_item *); +static int json_parse_number(struct kore_json *, struct kore_json_item *); +static int json_parse_literal(struct kore_json *, struct kore_json_item *); + +static void json_item_free(struct kore_json_item *); +static struct kore_json_item *json_item_alloc(int, const char *, + struct kore_json_item *); +static struct kore_json_item *json_find_item(struct kore_json *, + struct kore_json_item *, char **, int, int); + +static u_int8_t json_null_literal[] = { 'n', 'u', 'l', 'l' }; +static u_int8_t json_true_literal[] = { 't', 'r', 'u', 'e' }; +static u_int8_t json_false_literal[] = { 'f', 'a', 'l', 's', 'e' }; + +static const char *json_errtab[] = { + "no error", + "invalid JSON object", + "invalid JSON array", + "invalid JSON string", + "invalid JSON number", + "invalid JSON literal", + "too many nested items", + "end of stream while parsing JSON", + "invalid JSON", + "invalid search query specified", + "item not found", + "item found, but not expected value" +}; + +void +kore_json_init(struct kore_json *json, const u_int8_t *data, size_t len) +{ + memset(json, 0, sizeof(*json)); + + json->data = data; + json->length = len; + + kore_buf_init(&json->tmpbuf, 1024); +} + +int +kore_json_parse(struct kore_json *json) +{ + u_int8_t ch; + int type; + + if (json->root) + return (KORE_RESULT_OK); + + if (!json_peek(json, &ch)) + return (KORE_RESULT_ERROR); + + if (!json_guess_type(ch, &type)) { + json->error = KORE_JSON_ERR_INVALID_JSON; + return (KORE_RESULT_ERROR); + } + + json->root = json_item_alloc(type, "<root>", NULL); + + if (!json->root->parse(json, json->root)) { + if (json->error == 0) + json->error = KORE_JSON_ERR_INVALID_JSON; + return (KORE_RESULT_ERROR); + } + + return (KORE_RESULT_OK); +} + +struct kore_json_item * +kore_json_get(struct kore_json *json, const char *path, int type) +{ + struct kore_json_item *item; + char *copy; + char *tokens[KORE_JSON_DEPTH_MAX + 1]; + + copy = kore_strdup(path); + + if (!kore_split_string(copy, "/", tokens, KORE_JSON_DEPTH_MAX)) { + kore_free(copy); + return (NULL); + } + + json->error = 0; + item = json_find_item(json, json->root, tokens, type, 0); + kore_free(copy); + + if (item == NULL && json->error == 0) + json->error = KORE_JSON_ERR_NOT_FOUND; + + return (item); +} + +void +kore_json_cleanup(struct kore_json *json) +{ + kore_buf_cleanup(&json->tmpbuf); + json_item_free(json->root); +} + +const char * +kore_json_strerror(struct kore_json *json) +{ + if (json->error >= 0 && json->error <= KORE_JSON_ERR_LAST) + return (json_errtab[json->error]); + + return ("unknown JSON error"); +} + +static struct kore_json_item * +json_find_item(struct kore_json *json, struct kore_json_item *object, + char **tokens, int type, int pos) +{ + char *p, *str; + struct kore_json_item *item, *nitem; + int err, idx, spot; + + if (tokens[pos] == NULL) + return (NULL); + + if (object->type != KORE_JSON_TYPE_OBJECT && + object->type != KORE_JSON_TYPE_ARRAY) + return (NULL); + + if ((str = strchr(tokens[pos], '[')) != NULL) { + *(str)++ = '\0'; + + if ((p = strchr(str, ']')) == NULL) { + json->error = KORE_JSON_ERR_INVALID_SEARCH; + return (NULL); + } + + *p = '\0'; + + spot = kore_strtonum(str, 10, 0, USHRT_MAX, &err); + if (err != KORE_RESULT_OK) { + json->error = KORE_JSON_ERR_INVALID_SEARCH; + return (NULL); + } + } else { + spot = -1; + } + + item = NULL; + + TAILQ_FOREACH(item, &object->data.items, list) { + if (item->name && strcmp(item->name, tokens[pos])) + continue; + + if (item->type == KORE_JSON_TYPE_ARRAY && spot != -1) { + idx = 0; + nitem = NULL; + TAILQ_FOREACH(nitem, &item->data.items, list) { + if (idx++ == spot) + break; + } + + if (nitem == NULL) + return (NULL); + + item = nitem; + } + + if (tokens[pos + 1] == NULL) { + if (item->type == type) + return (item); + json->error = KORE_JSON_ERR_TYPE_MISMATCH; + return (NULL); + } + + if (item->type == KORE_JSON_TYPE_OBJECT || + item->type == KORE_JSON_TYPE_ARRAY) { + item = json_find_item(json, + item, tokens, type, pos + 1); + } else { + item = NULL; + } + + break; + } + + return (item); +} + +static struct kore_json_item * +json_item_alloc(int type, const char *name, struct kore_json_item *parent) +{ + struct kore_json_item *item; + + item = kore_calloc(1, sizeof(*item)); + item->type = type; + item->parent = parent; + + switch (item->type) { + case KORE_JSON_TYPE_OBJECT: + TAILQ_INIT(&item->data.items); + item->parse = json_parse_object; + break; + case KORE_JSON_TYPE_ARRAY: + TAILQ_INIT(&item->data.items); + item->parse = json_parse_array; + break; + case KORE_JSON_TYPE_STRING: + item->parse = json_parse_string; + break; + case KORE_JSON_TYPE_NUMBER: + item->parse = json_parse_number; + break; + case KORE_JSON_TYPE_LITERAL: + item->parse = json_parse_literal; + break; + default: + fatal("%s: unknown type %d", __func__, item->type); + } + + if (name) + item->name = kore_strdup(name); + + if (parent) { + if (parent->type != KORE_JSON_TYPE_OBJECT && + parent->type != KORE_JSON_TYPE_ARRAY) { + fatal("%s: invalid parent type (%d)", + __func__, parent->type); + } + + TAILQ_INSERT_TAIL(&parent->data.items, item, list); + } + + return (item); +} + +static void +json_item_free(struct kore_json_item *item) +{ + struct kore_json_item *node; + + switch (item->type) { + case KORE_JSON_TYPE_OBJECT: + case KORE_JSON_TYPE_ARRAY: + while ((node = TAILQ_FIRST(&item->data.items)) != NULL) { + TAILQ_REMOVE(&item->data.items, node, list); + json_item_free(node); + } + break; + case KORE_JSON_TYPE_STRING: + kore_free(item->data.string); + break; + case KORE_JSON_TYPE_NUMBER: + case KORE_JSON_TYPE_LITERAL: + break; + default: + fatal("%s: unknown type %d", __func__, item->type); + } +} + +static int +json_peek(struct kore_json *json, u_int8_t *ch) +{ + return (json_next_byte(json, ch, 1)); +} + +static int +json_next(struct kore_json *json, u_int8_t *ch) +{ + return (json_next_byte(json, ch, 0)); +} + +static int +json_next_byte(struct kore_json *json, u_int8_t *ch, int peek) +{ + if (json->offset >= json->length) { + json->error = KORE_JSON_ERR_EOF; + return (KORE_RESULT_ERROR); + } + + *ch = json->data[json->offset]; + + if (peek == 0) + json->offset++; + + return (KORE_RESULT_OK); +} + +static int +json_consume_whitespace(struct kore_json *json) +{ + u_int8_t ch; + + for (;;) { + if (!json_peek(json, &ch)) + return (KORE_RESULT_ERROR); + + if (ch != ' ' && ch != '\n' && ch != '\r' && ch != '\t') + break; + + json->offset++; + } + + return (KORE_RESULT_OK); +} + +static int +json_guess_type(u_int8_t ch, int *type) +{ + if (ch == '-' || (ch >= '0' && ch <= '9')) { + *type = KORE_JSON_TYPE_NUMBER; + return (KORE_RESULT_OK); + } + + switch (ch) { + case '{': + *type = KORE_JSON_TYPE_OBJECT; + break; + case '"': + *type = KORE_JSON_TYPE_STRING; + break; + case '[': + *type = KORE_JSON_TYPE_ARRAY; + break; + case 'f': + case 'n': + case 't': + *type = KORE_JSON_TYPE_LITERAL; + break; + default: + return (KORE_RESULT_ERROR); + } + + return (KORE_RESULT_OK); +} + +static int +json_parse_object(struct kore_json *json, struct kore_json_item *object) +{ + u_int8_t ch; + char *key; + struct kore_json_item *item; + int ret, type; + + if (json->depth++ >= KORE_JSON_DEPTH_MAX) { + json->error = KORE_JSON_ERR_DEPTH; + return (KORE_RESULT_ERROR); + } + + key = NULL; + ret = KORE_RESULT_ERROR; + + if (!json_next(json, &ch)) + goto cleanup; + + if (ch != '{') + goto cleanup; + + for (;;) { + if (!json_consume_whitespace(json)) + goto cleanup; + + if (!json_peek(json, &ch)) + goto cleanup; + + switch (ch) { + case '}': + ret = KORE_RESULT_OK; + goto cleanup; + case '"': + if ((key = json_get_string(json)) == NULL) + goto cleanup; + break; + default: + goto cleanup; + } + + if (!json_consume_whitespace(json)) + goto cleanup; + + if (!json_next(json, &ch)) + goto cleanup; + + if (ch != ':') + goto cleanup; + + if (!json_consume_whitespace(json)) + goto cleanup; + + if (!json_peek(json, &ch)) + goto cleanup; + + if (!json_guess_type(ch, &type)) + goto cleanup; + + item = json_item_alloc(type, key, object); + + if (!item->parse(json, item)) + goto cleanup; + + key = NULL; + + if (!json_consume_whitespace(json)) + goto cleanup; + + if (!json_next(json, &ch)) + goto cleanup; + + if (ch == ',') + continue; + + if (ch == '}') { + ret = KORE_RESULT_OK; + break; + } + + break; + } + +cleanup: + if (ret == KORE_RESULT_ERROR && json->error == 0) + json->error = KORE_JSON_ERR_INVALID_OBJECT; + + json->depth--; + + return (ret); +} + +static int +json_parse_array(struct kore_json *json, struct kore_json_item *array) +{ + u_int8_t ch; + char *key; + struct kore_json_item *item; + int ret, type; + + if (json->depth++ >= KORE_JSON_DEPTH_MAX) { + json->error = KORE_JSON_ERR_DEPTH; + return (KORE_RESULT_ERROR); + } + + key = NULL; + ret = KORE_RESULT_ERROR; + + if (!json_next(json, &ch)) + goto cleanup; + + if (ch != '[') + goto cleanup; + + for (;;) { + if (!json_consume_whitespace(json)) + goto cleanup; + + if (!json_peek(json, &ch)) + goto cleanup; + + if (ch == ']') { + ret = KORE_RESULT_OK; + goto cleanup; + } + + if (!json_guess_type(ch, &type)) + goto cleanup; + + item = json_item_alloc(type, key, array); + + if (!item->parse(json, item)) + goto cleanup; + + key = NULL; + + if (!json_consume_whitespace(json)) + goto cleanup; + + if (!json_next(json, &ch)) + goto cleanup; + + if (ch == ',') + continue; + + if (ch == ']') { + ret = KORE_RESULT_OK; + break; + } + + break; + } + +cleanup: + if (ret == KORE_RESULT_ERROR && json->error == 0) + json->error = KORE_JSON_ERR_INVALID_ARRAY; + + json->depth--; + + return (ret); +} + +static int +json_parse_string(struct kore_json *json, struct kore_json_item *string) +{ + char *value; + + if ((value = json_get_string(json)) == NULL) + return (KORE_RESULT_ERROR); + + string->type = KORE_JSON_TYPE_STRING; + string->data.string = kore_strdup(value); + + return (KORE_RESULT_OK); +} + +static int +json_parse_number(struct kore_json *json, struct kore_json_item *number) +{ + u_int8_t ch; + int ret; + char *str; + + str = NULL; + ret = KORE_RESULT_ERROR; + kore_buf_reset(&json->tmpbuf); + + for (;;) { + if (!json_peek(json, &ch)) + goto cleanup; + + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + case '.': + kore_buf_append(&json->tmpbuf, &ch, sizeof(ch)); + json->offset++; + continue; + } + + break; + } + + str = kore_buf_stringify(&json->tmpbuf, NULL); + + number->data.number = kore_strtodouble(str, -DBL_MAX, DBL_MAX, &ret); + if (ret != KORE_RESULT_OK) + goto cleanup; + + number->type = KORE_JSON_TYPE_NUMBER; + +cleanup: + if (ret == KORE_RESULT_ERROR && json->error == 0) + json->error = KORE_JSON_ERR_INVALID_NUMBER; + + return (ret); +} + +static int +json_parse_literal(struct kore_json *json, struct kore_json_item *literal) +{ + size_t len, idx; + int ret, val; + u_int8_t ch, *tmpl; + + ret = KORE_RESULT_ERROR; + + if (!json_next(json, &ch)) + goto cleanup; + + switch (ch) { + case 'f': + val = KORE_JSON_FALSE; + tmpl = json_false_literal; + len = sizeof(json_false_literal) - 1; + break; + case 'n': + val = KORE_JSON_NULL; + tmpl = json_null_literal; + len = sizeof(json_null_literal) - 1; + break; + case 't': + val = KORE_JSON_TRUE; + tmpl = json_true_literal; + len = sizeof(json_true_literal) - 1; + break; + default: + goto cleanup; + } + + for (idx = 0; idx < len; idx++) { + if (!json_next(json, &ch)) + goto cleanup; + + if (ch != tmpl[idx + 1]) + goto cleanup; + } + + literal->data.literal = val; + literal->type = KORE_JSON_TYPE_LITERAL; + + ret = KORE_RESULT_OK; + +cleanup: + if (ret == KORE_RESULT_ERROR && json->error == 0) + json->error = KORE_JSON_ERR_INVALID_LITERAL; + + return (ret); +} + +static char * +json_get_string(struct kore_json *json) +{ + u_int8_t ch; + char *res; + + res = NULL; + + if (!json_next(json, &ch)) + goto cleanup; + + if (ch != '"') + goto cleanup; + + kore_buf_reset(&json->tmpbuf); + + for (;;) { + if (!json_next(json, &ch)) + goto cleanup; + + if (ch == '"') + break; + + if (ch >= 0 && ch <= 0x1f) + goto cleanup; + + if (ch == '\\') { + if (!json_next(json, &ch)) + goto cleanup; + + switch (ch) { + case '\"': + case '\\': + case '/': + break; + case 'b': + ch = '\b'; + break; + case 'f': + ch = '\f'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 'u': + /* XXX - not supported. */ + goto cleanup; + } + } + + kore_buf_append(&json->tmpbuf, &ch, sizeof(ch)); + } + + res = kore_buf_stringify(&json->tmpbuf, NULL); + +cleanup: + if (res == NULL && json->error == 0) + json->error = KORE_JSON_ERR_INVALID_STRING; + + return (res); +}