Skip to content
Snippets Groups Projects
data.c 80.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			break;
    		}
    
    		tmp_provider_child = data_provider_find(provider_child->children,
    				node_name, NULL);
    
    		/* release the reference from this child */
    		ao2_ref(provider_child, -1);
    
    		provider_child = tmp_provider_child;
    	}
    
    	if (!provider_child) {
    		ast_log(LOG_ERROR, "Invalid path '%s', '%s' not found.\n",
    				tmp_path, node_name);
    		return NULL;
    	}
    
    	/* generate the search tree. */
    	if (query->search) {
    		search = data_search_generate(query->search);
    		if (search) {
    			search_child = data_search_find(search->children,
    				provider_child->name);
    		}
    	}
    
    	/* generate the filter tree. */
    	if (query->filter) {
    		filter = data_filter_generate(query->filter);
    		if (filter) {
    			filter_child = data_filter_find(filter->children,
    				provider_child->name);
    		}
    	}
    
    	result = data_result_generate_node(query, provider_child, provider_child->name,
    			search_child, filter_child);
    
    	/* release the requested provider. */
    	ao2_ref(provider_child, -1);
    
    	/* release the generated search tree. */
    	if (search_child) {
    		ao2_ref(search_child, -1);
    	}
    
    	if (filter_child) {
    		ao2_ref(filter_child, -1);
    	}
    
    	if (search) {
    		data_search_release(search);
    	}
    
    	result_filtered = result;
    
    	/* release the generated filter tree. */
    	if (filter) {
    		data_filter_release(filter);
    	}
    
    	return result_filtered;
    }
    
    struct ast_data *ast_data_get(const struct ast_data_query *query)
    {
    	struct ast_data *res;
    
    	/* check compatibility */
    	if (!data_structure_compatible(query->version, latest_query_compatible_version,
    		current_query_version)) {
    		return NULL;
    	}
    
    	data_read_lock();
    	res = data_result_generate(query, query->path);
    	data_unlock();
    
    	if (!res) {
    		ast_log(LOG_ERROR, "Unable to get data from %s\n", query->path);
    		return NULL;
    	}
    
    	return res;
    }
    
    
    /*!
     * \internal
     * \brief Helper function to move an ast_data tree to xml.
     * \param[in] parent_data The initial ast_data node to be passed to xml.
     * \param[out] parent_xml The root node to insert the xml.
     */
    static void data_get_xml_add_child(struct ast_data *parent_data,
    	struct ast_xml_node *parent_xml)
    {
    	struct ao2_iterator i;
    	struct ast_data *node;
    	struct ast_xml_node *child_xml;
    	char node_content[256];
    
    	i = ao2_iterator_init(parent_data->children, 0);
    	while ((node = ao2_iterator_next(&i))) {
    		child_xml = ast_xml_new_node(node->name);
    		if (!child_xml) {
    			ao2_ref(node, -1);
    			continue;
    		}
    
    		switch (node->type) {
    		case AST_DATA_CONTAINER:
    			data_get_xml_add_child(node, child_xml);
    			break;
    
    		case AST_DATA_PASSWORD:
    			ast_xml_set_text(child_xml, node->payload.str);
    			break;
    		case AST_DATA_TIMESTAMP:
    
    			snprintf(node_content, sizeof(node_content), "%u",
    
    				node->payload.uint);
    			ast_xml_set_text(child_xml, node_content);
    			break;
    		case AST_DATA_SECONDS:
    
    			snprintf(node_content, sizeof(node_content), "%u",
    
    				node->payload.uint);
    			ast_xml_set_text(child_xml, node_content);
    			break;
    		case AST_DATA_MILLISECONDS:
    
    			snprintf(node_content, sizeof(node_content), "%u",
    
    				node->payload.uint);
    			ast_xml_set_text(child_xml, node_content);
    			break;
    
    		case AST_DATA_STRING:
    			ast_xml_set_text(child_xml, node->payload.str);
    			break;
    
    		case AST_DATA_CHARACTER:
    			snprintf(node_content, sizeof(node_content), "%c",
    				node->payload.character);
    			ast_xml_set_text(child_xml, node_content);
    			break;
    
    		case AST_DATA_INTEGER:
    			snprintf(node_content, sizeof(node_content), "%d",
    				node->payload.sint);
    			ast_xml_set_text(child_xml, node_content);
    			break;
    		case AST_DATA_UNSIGNED_INTEGER:
    			snprintf(node_content, sizeof(node_content), "%u",
    				node->payload.uint);
    			ast_xml_set_text(child_xml, node_content);
    			break;
    		case AST_DATA_DOUBLE:
    			snprintf(node_content, sizeof(node_content), "%f",
    				node->payload.dbl);
    			ast_xml_set_text(child_xml, node_content);
    			break;
    		case AST_DATA_BOOLEAN:
    			if (node->payload.boolean) {
    				ast_xml_set_text(child_xml, "true");
    			} else {
    				ast_xml_set_text(child_xml, "false");
    			}
    			break;
    		case AST_DATA_POINTER:
    			snprintf(node_content, sizeof(node_content), "%p",
    				node->payload.ptr);
    			ast_xml_set_text(child_xml, node_content);
    			break;
    		case AST_DATA_IPADDR:
    			snprintf(node_content, sizeof(node_content), "%s",
    				ast_inet_ntoa(node->payload.ipaddr));
    			ast_xml_set_text(child_xml, node_content);
    			break;
    		}
    		ast_xml_add_child(parent_xml, child_xml);
    
    		ao2_ref(node, -1);
    	}
    	ao2_iterator_destroy(&i);
    
    }
    
    struct ast_xml_doc *ast_data_get_xml(const struct ast_data_query *query)
    {
    	struct ast_xml_doc *doc;
    	struct ast_xml_node *root;
    	struct ast_data *res;
    
    	res = ast_data_get(query);
    	if (!res) {
    		return NULL;
    	}
    
    	doc = ast_xml_new();
    	if (!doc) {
    
    		ast_data_free(res);
    
    		return NULL;
    	}
    
    	root = ast_xml_new_node(res->name);
    	if (!root) {
    		ast_xml_close(doc);
    	}
    
    	ast_xml_set_root(doc, root);
    
    	data_get_xml_add_child(res, root);
    
    	ast_data_free(res);
    
    	return doc;
    }
    
    
    enum ast_data_type ast_data_retrieve_type(struct ast_data *node, const char *path)
    {
    	struct ast_data *internal;
    
    	internal = data_result_get_node(node, path);
    	if (!internal) {
    		return -1;
    	}
    
    	return internal->type;
    }
    
    char *ast_data_retrieve_name(struct ast_data *node)
    {
    	return node->name;
    }
    
    /*!
     * \internal
     * \brief Insert a child node inside a passed parent node.
     * \param root Where we are going to insert the child node.
     * \param name The name of the child node to add.
     * \param type The type of content inside the child node.
     * \param ptr The actual content of the child node.
     * \retval NULL on error.
     * \retval non-NULL The added child node pointer.
     */
    static struct ast_data *__ast_data_add(struct ast_data *root, const char *name,
    	enum ast_data_type type, void *ptr)
    {
    	struct ast_data *node;
    	struct data_filter *filter, *filter_child = NULL;
    
    	if (!root || !root->children) {
    		/* invalid data result node. */
    		return NULL;
    	}
    
    	/* check if we need to add this node, based on the filter. */
    	if (root->filter) {
    		filter = data_filter_find(root->filter->children, name);
    		if (!filter) {
    			return NULL;
    		}
    		ao2_ref(filter, -1);
    	}
    
    	node = data_result_create(name);
    	if (!node) {
    		return NULL;
    	}
    
    	node->type = type;
    
    	switch (type) {
    	case AST_DATA_BOOLEAN:
    		node->payload.boolean = *(unsigned int *) ptr;
    		break;
    	case AST_DATA_INTEGER:
    
    	case AST_DATA_TIMESTAMP:
    	case AST_DATA_SECONDS:
    	case AST_DATA_MILLISECONDS:
    
    	case AST_DATA_UNSIGNED_INTEGER:
    
    		node->payload.uint = *(unsigned int *) ptr;
    
    		break;
    	case AST_DATA_DOUBLE:
    		node->payload.dbl = *(double *) ptr;
    		break;
    
    	case AST_DATA_STRING:
    
    		node->payload.str = (char *) ptr;
    		break;
    	case AST_DATA_CHARACTER:
    		node->payload.character = *(char *) ptr;
    		break;
    
    	case AST_DATA_POINTER:
    		node->payload.ptr = ptr;
    		break;
    	case AST_DATA_IPADDR:
    		node->payload.ipaddr = *(struct in_addr *) ptr;
    		break;
    	case AST_DATA_CONTAINER:
    		if (root->filter) {
    			filter_child = data_filter_find(root->filter->children, name);
    			if (filter_child) {
    				/* do not increment the refcount because it is not neccesary. */
    				ao2_ref(filter_child, -1);
    			}
    		}
    		node->filter = filter_child;
    		break;
    	default:
    		break;
    	}
    
    	data_result_add_child(root, node);
    
    	ao2_ref(node, -1);
    
    	return node;
    }
    
    struct ast_data *ast_data_add_node(struct ast_data *root, const char *name)
    {
    	return __ast_data_add(root, name, AST_DATA_CONTAINER, NULL);
    }
    
    struct ast_data *ast_data_add_int(struct ast_data *root, const char *name, int value)
    {
    	return __ast_data_add(root, name, AST_DATA_INTEGER, &value);
    }
    
    
    struct ast_data *ast_data_add_char(struct ast_data *root, const char *name, char value)
    {
    	return __ast_data_add(root, name, AST_DATA_CHARACTER, &value);
    }
    
    
    struct ast_data *ast_data_add_uint(struct ast_data *root, const char *name,
    	unsigned int value)
    {
    	return __ast_data_add(root, name, AST_DATA_UNSIGNED_INTEGER, &value);
    }
    
    struct ast_data *ast_data_add_dbl(struct ast_data *root, const char *childname,
    	double dbl)
    {
    	return __ast_data_add(root, childname, AST_DATA_DOUBLE, &dbl);
    }
    
    struct ast_data *ast_data_add_bool(struct ast_data *root, const char *childname,
    	unsigned int boolean)
    {
    	return __ast_data_add(root, childname, AST_DATA_BOOLEAN, &boolean);
    }
    
    struct ast_data *ast_data_add_ipaddr(struct ast_data *root, const char *childname,
    	struct in_addr addr)
    {
    	return __ast_data_add(root, childname, AST_DATA_IPADDR, &addr);
    }
    
    struct ast_data *ast_data_add_ptr(struct ast_data *root, const char *childname,
    	void *ptr)
    {
    	return __ast_data_add(root, childname, AST_DATA_POINTER, ptr);
    }
    
    
    struct ast_data *ast_data_add_timestamp(struct ast_data *root, const char *childname,
    	unsigned int timestamp)
    {
    	return __ast_data_add(root, childname, AST_DATA_TIMESTAMP, &timestamp);
    }
    
    struct ast_data *ast_data_add_seconds(struct ast_data *root, const char *childname,
    	unsigned int seconds)
    {
    	return __ast_data_add(root, childname, AST_DATA_SECONDS, &seconds);
    }
    
    struct ast_data *ast_data_add_milliseconds(struct ast_data *root, const char *childname,
    	unsigned int milliseconds)
    {
    	return __ast_data_add(root, childname, AST_DATA_MILLISECONDS, &milliseconds);
    }
    
    struct ast_data *ast_data_add_password(struct ast_data *root, const char *childname,
    	const char *value)
    {
    	char *name;
    	size_t namelen = 1 + (ast_strlen_zero(value) ? 0 : strlen(value));
    	struct ast_data *res;
    
    	if (!(name = ast_malloc(namelen))) {
    		return NULL;
    	}
    
    	strcpy(name, (ast_strlen_zero(value) ? "" : value));
    
    	res = __ast_data_add(root, childname, AST_DATA_PASSWORD, name);
    	if (!res) {
    		ast_free(name);
    	}
    
    	return res;
    }
    
    
    struct ast_data *ast_data_add_str(struct ast_data *root, const char *childname,
    	const char *value)
    {
    	char *name;
    	size_t namelen = 1 + (ast_strlen_zero(value) ? 0 : strlen(value));
    	struct ast_data *res;
    
    	if (!(name = ast_malloc(namelen))) {
    		return NULL;
    	}
    
    	strcpy(name, (ast_strlen_zero(value) ? "" : value));
    
    	res = __ast_data_add(root, childname, AST_DATA_STRING, name);
    	if (!res) {
    		ast_free(name);
    	}
    
    	return res;
    }
    
    int __ast_data_add_structure(struct ast_data *root,
    	const struct ast_data_mapping_structure *mapping, size_t mapping_len,
    	void *structure)
    {
    	int i;
    
    	for (i = 0; i < mapping_len; i++) {
    		switch (mapping[i].type) {
    		case AST_DATA_INTEGER:
    			ast_data_add_int(root, mapping[i].name,
    				mapping[i].get.AST_DATA_INTEGER(structure));
    			break;
    		case AST_DATA_UNSIGNED_INTEGER:
    			ast_data_add_uint(root, mapping[i].name,
    				mapping[i].get.AST_DATA_UNSIGNED_INTEGER(structure));
    			break;
    		case AST_DATA_DOUBLE:
    			ast_data_add_dbl(root, mapping[i].name,
    				mapping[i].get.AST_DATA_DOUBLE(structure));
    			break;
    		case AST_DATA_BOOLEAN:
    			ast_data_add_bool(root, mapping[i].name,
    				mapping[i].get.AST_DATA_BOOLEAN(structure));
    			break;
    
    		case AST_DATA_PASSWORD:
    			ast_data_add_password(root, mapping[i].name,
    				mapping[i].get.AST_DATA_PASSWORD(structure));
    			break;
    		case AST_DATA_TIMESTAMP:
    			ast_data_add_timestamp(root, mapping[i].name,
    				mapping[i].get.AST_DATA_TIMESTAMP(structure));
    			break;
    		case AST_DATA_SECONDS:
    			ast_data_add_seconds(root, mapping[i].name,
    				mapping[i].get.AST_DATA_SECONDS(structure));
    			break;
    		case AST_DATA_MILLISECONDS:
    			ast_data_add_milliseconds(root, mapping[i].name,
    				mapping[i].get.AST_DATA_MILLISECONDS(structure));
    			break;
    
    		case AST_DATA_STRING:
    			ast_data_add_str(root, mapping[i].name,
    				mapping[i].get.AST_DATA_STRING(structure));
    			break;
    
    		case AST_DATA_CHARACTER:
    			ast_data_add_char(root, mapping[i].name,
    				mapping[i].get.AST_DATA_CHARACTER(structure));
    			break;
    
    		case AST_DATA_CONTAINER:
    			break;
    		case AST_DATA_IPADDR:
    			ast_data_add_ipaddr(root, mapping[i].name,
    				mapping[i].get.AST_DATA_IPADDR(structure));
    			break;
    		case AST_DATA_POINTER:
    			ast_data_add_ptr(root, mapping[i].name,
    				mapping[i].get.AST_DATA_POINTER(structure));
    			break;
    		}
    	}
    
    	return 0;
    }
    
    void ast_data_remove_node(struct ast_data *root, struct ast_data *child)
    {
    	ao2_unlink(root->children, child);
    }
    
    void ast_data_free(struct ast_data *root)
    {
    	/* destroy it, this will destroy all the internal nodes. */
    	ao2_ref(root, -1);
    }
    
    struct ast_data_iterator *ast_data_iterator_init(struct ast_data *tree,
    	const char *elements)
    {
    	struct ast_data_iterator *iterator;
    	struct ao2_iterator i;
    	struct ast_data *internal = tree;
    	char *path, *ptr = NULL;
    
    
    	/* tree is the node we want to use to iterate? or we are going
    	 * to iterate thow an internal node? */
    
    	path = ast_strdupa(elements);
    
    	ptr = strrchr(path, '/');
    	if (ptr) {
    		*ptr = '\0';
    		internal = data_result_get_node(tree, path);
    		if (!internal) {
    			return NULL;
    
    		}
    	}
    
    	iterator = ast_calloc(1, sizeof(*iterator));
    	if (!iterator) {
    		return NULL;
    	}
    
    	i = ao2_iterator_init(internal->children, 0);
    
    	iterator->pattern = (ptr ? strrchr(elements, '/') + 1 : elements);
    
    	/* is the last node a regular expression?, compile it! */
    	if (!regcomp(&(iterator->regex_pattern), iterator->pattern,
    			REG_EXTENDED | REG_NOSUB | REG_ICASE)) {
    		iterator->is_pattern = 1;
    	}
    
    	iterator->internal_iterator = i;
    
    	return iterator;
    }
    
    void ast_data_iterator_end(struct ast_data_iterator *iterator)
    {
    	/* decrement the reference counter. */
    	if (iterator->last) {
    		ao2_ref(iterator->last, -1);
    	}
    
    	/* release the generated pattern. */
    	if (iterator->is_pattern) {
    		regfree(&(iterator->regex_pattern));
    	}
    
    	ao2_iterator_destroy(&(iterator->internal_iterator));
    
    	ast_free(iterator);
    	iterator = NULL;
    }
    
    struct ast_data *ast_data_iterator_next(struct ast_data_iterator *iterator)
    {
    	struct ast_data *res;
    
    	if (iterator->last) {
    		/* release the last retrieved node reference. */
    		ao2_ref(iterator->last, -1);
    	}
    
    	while ((res = ao2_iterator_next(&iterator->internal_iterator))) {
    		/* if there is no node name pattern specified, return
    		 * the next node. */
    		if (!iterator->pattern) {
    			break;
    		}
    
    		/* if the pattern is a regular expression, check if this node
    		 * matches. */
    		if (iterator->is_pattern && !regexec(&(iterator->regex_pattern),
    			res->name, 0, NULL, 0)) {
    			break;
    		}
    
    		/* if there is a pattern specified, check if this node matches
    		 * the wanted node names. */
    		if (!iterator->is_pattern && (iterator->pattern &&
    				!strcasecmp(res->name, iterator->pattern))) {
    			break;
    		}
    
    		ao2_ref(res, -1);
    	}
    
    	iterator->last = res;
    
    	return res;
    }
    
    int ast_data_retrieve(struct ast_data *tree, const char *path,
    	struct ast_data_retrieve *content)
    {
    	struct ast_data *node;
    
    	if (!content) {
    		return -1;
    	}
    
    	node = data_result_get_node(tree, path);
    	if (!node) {
    		ast_log(LOG_ERROR, "Invalid internal node %s\n", path);
    		return -1;
    	}
    
    	content->type = node->type;
    	switch (node->type) {
    	case AST_DATA_STRING:
    		content->value.AST_DATA_STRING = node->payload.str;
    		break;
    
    	case AST_DATA_PASSWORD:
    		content->value.AST_DATA_PASSWORD = node->payload.str;
    		break;
    	case AST_DATA_TIMESTAMP:
    		content->value.AST_DATA_TIMESTAMP = node->payload.uint;
    		break;
    	case AST_DATA_SECONDS:
    		content->value.AST_DATA_SECONDS = node->payload.uint;
    		break;
    	case AST_DATA_MILLISECONDS:
    		content->value.AST_DATA_MILLISECONDS = node->payload.uint;
    		break;
    	case AST_DATA_CHARACTER:
    		content->value.AST_DATA_CHARACTER = node->payload.character;
    		break;
    
    	case AST_DATA_INTEGER:
    		content->value.AST_DATA_INTEGER = node->payload.sint;
    		break;
    	case AST_DATA_UNSIGNED_INTEGER:
    		content->value.AST_DATA_UNSIGNED_INTEGER = node->payload.uint;
    		break;
    	case AST_DATA_BOOLEAN:
    		content->value.AST_DATA_BOOLEAN = node->payload.boolean;
    		break;
    	case AST_DATA_IPADDR:
    		content->value.AST_DATA_IPADDR = node->payload.ipaddr;
    		break;
    	case AST_DATA_DOUBLE:
    		content->value.AST_DATA_DOUBLE = node->payload.dbl;
    		break;
    	case AST_DATA_CONTAINER:
    		break;
    	case AST_DATA_POINTER:
    		content->value.AST_DATA_POINTER = node->payload.ptr;
    		break;
    	}
    
    	return 0;
    }
    
    /*!
     * \internal
     * \brief One color for each node type.
     */
    static const struct {
    	enum ast_data_type type;
    	int color;
    } data_result_color[] = {
    
    	{ AST_DATA_STRING, COLOR_BLUE },
    	{ AST_DATA_PASSWORD, COLOR_BRBLUE },
    	{ AST_DATA_TIMESTAMP, COLOR_CYAN },
    	{ AST_DATA_SECONDS, COLOR_MAGENTA },
    	{ AST_DATA_MILLISECONDS, COLOR_BRMAGENTA },
    	{ AST_DATA_CHARACTER, COLOR_GRAY },
    
    	{ AST_DATA_INTEGER, COLOR_RED },
    	{ AST_DATA_UNSIGNED_INTEGER, COLOR_RED },
    	{ AST_DATA_DOUBLE, COLOR_RED },
    	{ AST_DATA_BOOLEAN, COLOR_BRRED },
    	{ AST_DATA_CONTAINER, COLOR_GREEN },
    	{ AST_DATA_IPADDR, COLOR_BROWN },
    	{ AST_DATA_POINTER, COLOR_YELLOW },
    };
    
    /*!
     * \internal
     * \brief Get the color configured for a specific node type.
     * \param[in] type The node type.
     * \returns The color specified for the passed type.
     */
    static int data_result_get_color(enum ast_data_type type)
    {
    	int i;
    	for (i = 0; i < ARRAY_LEN(data_result_color); i++) {
    		if (data_result_color[i].type == type) {
    			return data_result_color[i].color;
    		}
    	}
    
    	return COLOR_BLUE;
    }
    
    /*!
     * \internal
     * \brief Print a node to the CLI.
     * \param[in] fd The CLI file descriptor.
     * \param[in] node The node to print.
     * \param[in] depth The actual node depth in the tree.
     */
    static void data_result_print_cli_node(int fd, const struct ast_data *node, uint32_t depth)
    {
    	int i;
    	struct ast_str *tabs, *output;
    
    	tabs = ast_str_create(depth * 10 + 1);
    	if (!tabs) {
    		return;
    	}
    	ast_str_reset(tabs);
    	for (i = 0; i < depth; i++) {
    		ast_str_append(&tabs, 0, "  ");
    	}
    
    	output = ast_str_create(20);
    	if (!output) {
    		ast_free(tabs);
    		return;
    	}
    
    	ast_str_reset(output);
    	ast_term_color_code(&output, data_result_get_color(node->type), 0);
    
    	switch (node->type) {
    	case AST_DATA_POINTER:
    		ast_str_append(&output, 0, "%s%s: %p\n", ast_str_buffer(tabs),
    				node->name, node->payload.ptr);
    		break;
    
    	case AST_DATA_PASSWORD:
    		ast_str_append(&output, 0, "%s%s: \"%s\"\n",
    				ast_str_buffer(tabs),
    				node->name,
    				node->payload.str);
    		break;
    
    	case AST_DATA_STRING:
    		ast_str_append(&output, 0, "%s%s: \"%s\"\n",
    				ast_str_buffer(tabs),
    				node->name,
    				node->payload.str);
    		break;
    
    	case AST_DATA_CHARACTER:
    		ast_str_append(&output, 0, "%s%s: \'%c\'\n",
    				ast_str_buffer(tabs),
    				node->name,
    				node->payload.character);
    		break;
    
    	case AST_DATA_CONTAINER:
    		ast_str_append(&output, 0, "%s%s\n", ast_str_buffer(tabs),
    				node->name);
    		break;
    
    		ast_str_append(&output, 0, "%s%s: %u\n", ast_str_buffer(tabs),
    
    				node->name,
    				node->payload.uint);
    		break;
    	case AST_DATA_SECONDS:
    
    		ast_str_append(&output, 0, "%s%s: %u\n", ast_str_buffer(tabs),
    
    				node->name,
    				node->payload.uint);
    		break;
    	case AST_DATA_MILLISECONDS:
    
    		ast_str_append(&output, 0, "%s%s: %u\n", ast_str_buffer(tabs),
    
    	case AST_DATA_INTEGER:
    		ast_str_append(&output, 0, "%s%s: %d\n", ast_str_buffer(tabs),
    				node->name,
    				node->payload.sint);
    		break;
    	case AST_DATA_UNSIGNED_INTEGER:
    		ast_str_append(&output, 0, "%s%s: %u\n", ast_str_buffer(tabs),
    				node->name,
    				node->payload.uint);
    		break;
    	case AST_DATA_DOUBLE:
    		ast_str_append(&output, 0, "%s%s: %lf\n", ast_str_buffer(tabs),
    				node->name,
    				node->payload.dbl);
    		break;
    	case AST_DATA_BOOLEAN:
    		ast_str_append(&output, 0, "%s%s: %s\n", ast_str_buffer(tabs),
    				node->name,
    				((node->payload.boolean) ? "True" : "False"));
    		break;
    	case AST_DATA_IPADDR:
    		ast_str_append(&output, 0, "%s%s: %s\n", ast_str_buffer(tabs),
    				node->name,
    				ast_inet_ntoa(node->payload.ipaddr));
    		break;
    	}
    
    	ast_free(tabs);
    
    
    	ast_term_color_code(&output, 0, 0);
    
    
    	ast_cli(fd, "%s", ast_str_buffer(output));
    
    	ast_free(output);
    
    	if (node->type == AST_DATA_CONTAINER) {
    		__data_result_print_cli(fd, node, depth + 1);
    	}
    }
    
    /*!
     * \internal
     * \brief Print out an ast_data tree to the CLI.
     * \param[in] fd The CLI file descriptor.
     * \param[in] root The root node of the tree.
     * \param[in] depth Actual depth.
     */
    
    static void __data_result_print_cli(int fd, const struct ast_data *root, uint32_t depth)
    {
    	struct ao2_iterator iter;
    	struct ast_data *node;
    
    	if (root->type == AST_DATA_CONTAINER) {
    		iter = ao2_iterator_init(root->children, 0);
    		while ((node = ao2_iterator_next(&iter))) {
    			data_result_print_cli_node(fd, node, depth + 1);
    			ao2_ref(node, -1);
    		}
    		ao2_iterator_destroy(&iter);
    	} else {
    		data_result_print_cli_node(fd, root, depth);
    	}
    }
    
    /*!
     * \internal
     * \brief
     * \param[in] fd The CLI file descriptor.
     * \param[in] root The root node of the tree.
     */
    static void data_result_print_cli(int fd, const struct ast_data *root)
    {
    
    	ast_cli(fd, COLORIZE_FMT "\n", COLORIZE(data_result_get_color(root->type), 0, root->name));
    
    
    	__data_result_print_cli(fd, root, 0);
    
    }
    
    /*!
     * \internal
     * \brief Handle the CLI command "data get".
     */
    static char *handle_cli_data_get(struct ast_cli_entry *e, int cmd,
    		struct ast_cli_args *a)
    {
    	struct ast_data_query query = {
    		.version = AST_DATA_QUERY_VERSION
    	};
    	struct ast_data *tree;
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "data get";
    		e->usage = ""
    			"Usage: data get <path> [<search> [<filter>]]\n"
    			"       Get the tree based on a path.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	if (a->argc < e->args + 1) {
    		return CLI_SHOWUSAGE;
    	}
    
    	query.path = (char *) a->argv[e->args];
    
    	if (a->argc > e->args + 1) {
    		query.search = (char *) a->argv[e->args + 1];
    	}
    
    	if (a->argc > e->args + 2) {
    		query.filter = (char *) a->argv[e->args + 2];
    	}
    
    	tree = ast_data_get(&query);
    	if (!tree) {
    		return CLI_FAILURE;
    	}
    
    	data_result_print_cli(a->fd, tree);
    
    	ast_data_free(tree);
    
    	return CLI_SUCCESS;
    }
    
    /*!
     * \internal
     * \brief Print the list of data providers.
     * \param[in] fd The CLI file descriptor.
     * \param[in] name The last node visited name.
     * \param[in] container The childrens of the last node.
     * \param[in] path The path to the current node.
     */
    static void data_provider_print_cli(int fd, const char *name,
    	struct ao2_container *container, struct ast_str *path)
    {
    	struct ao2_iterator i;
    	struct ast_str *current_path;
    	struct data_provider *provider;
    
    	current_path = ast_str_create(60);
    	if (!current_path) {
    		return;
    	}
    
    	ast_str_reset(current_path);
    	if (path) {
    		ast_str_set(&current_path, 0, "%s/%s", ast_str_buffer(path), name);
    	} else {
    		ast_str_set(&current_path, 0, "%s", name);
    	}
    
    	i = ao2_iterator_init(container, 0);
    	while ((provider = ao2_iterator_next(&i))) {
    		if (provider->handler) {
    			/* terminal node, print it. */
    			ast_cli(fd, "%s/%s (", ast_str_buffer(current_path),
    				provider->name);
    			if (provider->handler->get) {
    				ast_cli(fd, "get");
    			}
    			ast_cli(fd, ") [%s]\n", provider->registrar);
    		}
    		data_provider_print_cli(fd, provider->name, provider->children,
    			current_path);
    		ao2_ref(provider, -1);
    	}
    	ao2_iterator_destroy(&i);
    
    	ast_free(current_path);
    }
    
    /*!
     * \internal
     * \brief Handle CLI command "data show providers"
     */
    static char *handle_cli_data_show_providers(struct ast_cli_entry *e, int cmd,
    		struct ast_cli_args *a)
    {
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "data show providers";
    		e->usage = ""
    			"Usage: data show providers\n"
    			"       Show the list of registered providers\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    	data_read_lock();
    	data_provider_print_cli(a->fd, "", root_data.container, NULL);
    	data_unlock();
    
    	return CLI_SUCCESS;
    }
    
    /*!
     * \internal
     * \brief Data API CLI commands.
     */
    static struct ast_cli_entry cli_data[] = {
    	AST_CLI_DEFINE(handle_cli_data_get, "Data API get"),
    	AST_CLI_DEFINE(handle_cli_data_show_providers, "Show data providers")
    };
    
    /*!
     * \internal
     * \brief Output a tree to the AMI.
     * \param[in] s AMI session.
     * \param[in] name The root node name.
     * \param[in] container The root container.
     * \param[in] path The current path.
     */
    static void data_result_manager_output(struct mansession *s, const char *name,
    	struct ao2_container *container, struct ast_str *path, int id)
    {
    	struct ao2_iterator i;
    	struct ast_str *current_path;
    	struct ast_data *node;
    	int current_id = id;
    
    	current_path = ast_str_create(60);
    	if (!current_path) {
    		return;