bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "lib.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen#include "json-tree.h"
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstruct json_tree {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen pool_t pool;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct json_tree_node *root, *cur, *cur_child;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen};
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmodystruct json_tree *
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmodyjson_tree_init_type(enum json_type container)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct json_tree *tree;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen pool_t pool;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen pool = pool_alloconly_create("json tree", 1024);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree = p_new(pool, struct json_tree, 1);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->pool = pool;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->root = tree->cur = p_new(tree->pool, struct json_tree_node, 1);
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody tree->cur->value_type = container == JSON_TYPE_ARRAY ? container : JSON_TYPE_OBJECT;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return tree;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenvoid json_tree_deinit(struct json_tree **_tree)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct json_tree *tree = *_tree;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen *_tree = NULL;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen pool_unref(&tree->pool);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstatic void
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenjson_tree_append_child(struct json_tree *tree, enum json_type type,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen const char *value)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen struct json_tree_node *node;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen node = p_new(tree->pool, struct json_tree_node, 1);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen node->parent = tree->cur;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen node->value_type = type;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen node->value.str = p_strdup(tree->pool, value);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (tree->cur_child == NULL)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->cur->value.child = node;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen else
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->cur_child->next = node;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->cur_child = node;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstatic void
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenjson_tree_set_cur(struct json_tree *tree, struct json_tree_node *node)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->cur = node;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->cur_child = tree->cur->value.child;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (tree->cur_child != NULL) {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen while (tree->cur_child->next != NULL)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->cur_child = tree->cur_child->next;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen }
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenstatic int
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenjson_tree_append_value(struct json_tree *tree, enum json_type type,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen const char *value)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen switch (tree->cur->value_type) {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case JSON_TYPE_OBJECT_KEY:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen /* "key": value - we already added the node and set its key,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen so now just set the value */
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->cur->value_type = type;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->cur->value.str = p_strdup(tree->pool, value);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen json_tree_set_cur(tree, tree->cur->parent);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen break;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case JSON_TYPE_ARRAY:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen /* element in array - add a new node */
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen json_tree_append_child(tree, type, value);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen break;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen default:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return -1;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen }
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return 0;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainenint json_tree_append(struct json_tree *tree, enum json_type type,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen const char *value)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen switch (type) {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case JSON_TYPE_OBJECT_KEY:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (tree->cur->value_type != JSON_TYPE_OBJECT)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return -1;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen json_tree_append_child(tree, type, NULL);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen json_tree_set_cur(tree, tree->cur_child);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->cur->key = p_strdup(tree->pool, value);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen break;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case JSON_TYPE_ARRAY:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (json_tree_append_value(tree, type, NULL) < 0)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return -1;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen json_tree_set_cur(tree, tree->cur_child);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen break;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case JSON_TYPE_OBJECT:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (tree->cur->value_type == JSON_TYPE_OBJECT_KEY)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->cur->value_type = JSON_TYPE_OBJECT;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen else if (tree->cur->value_type == JSON_TYPE_ARRAY) {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen json_tree_append_child(tree, type, NULL);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen json_tree_set_cur(tree, tree->cur_child);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen } else {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return -1;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen }
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen break;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case JSON_TYPE_OBJECT_END:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (tree->cur->parent == NULL ||
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->cur->value_type != JSON_TYPE_OBJECT)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return -1;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen json_tree_set_cur(tree, tree->cur->parent);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen break;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case JSON_TYPE_ARRAY_END:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (tree->cur->parent == NULL ||
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen tree->cur->value_type != JSON_TYPE_ARRAY)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return -1;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen json_tree_set_cur(tree, tree->cur->parent);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen break;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case JSON_TYPE_STRING:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case JSON_TYPE_NUMBER:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case JSON_TYPE_TRUE:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case JSON_TYPE_FALSE:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen case JSON_TYPE_NULL:
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (json_tree_append_value(tree, type, value) < 0)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return -1;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen break;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen }
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return 0;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmodyconst struct json_tree_node *
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmodyjson_tree_root(const struct json_tree *tree)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return tree->root;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmodyconst struct json_tree_node *
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmodyjson_tree_find_key(const struct json_tree_node *node, const char *key)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody i_assert(node->value_type == JSON_TYPE_OBJECT);
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody node = json_tree_get_child(node);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen for (; node != NULL; node = node->next) {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (node->key != NULL && strcmp(node->key, key) == 0)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return node;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen }
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return NULL;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmodyconst struct json_tree_node *
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmodyjson_tree_find_child_with(const struct json_tree_node *node,
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen const char *key, const char *value)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen{
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody const struct json_tree_node *child;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen i_assert(node->value_type == JSON_TYPE_OBJECT ||
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen node->value_type == JSON_TYPE_ARRAY);
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody for (node = json_tree_get_child(node); node != NULL; node = node->next) {
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen if (node->value_type != JSON_TYPE_OBJECT)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen continue;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody child = json_tree_find_key(node, key);
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody if (child != NULL &&
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody json_tree_get_value_str(child) != NULL &&
fcd3d6214ce1b8169b6481c78e02d9054901fed3Phil Carmody strcmp(json_tree_get_value_str(child), value) == 0)
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return node;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen }
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen return NULL;
31e6dbee5fc7d6c33b44c75c7e18ea576f44184bTimo Sirainen}