aboutsummaryrefslogtreecommitdiff
/**
 * part of Hydrilla
 * Routines that perform queries on in-memory scriptbase and return results in
 * the form of JSON strings.
 *
 * Copyright (C) 2021 Wojtek Kosior
 * Redistribution terms are gathered in the `copyright' file.
 */

#include <errno.h>

#include "scriptbase.h"

#define ADD_COMPONENT(object, key, construct, adder)	\
	new = cJSON_Create##construct;			\
	if (!new)					\
		goto free_json;				\
	adder(object, key, new)

#define ADD_TO_OBJECT(object, key, construct)				\
	ADD_COMPONENT(object, key, construct, cJSON_AddItemToObject)

#define ADD_KEY(key, construct) ADD_TO_OBJECT(json, key, construct)

#define ARRAY_ADDER(object, key, var) cJSON_AddItemToArray(object, var)

#define ADD_TO_ARRAY(object, construct)				\
	ADD_COMPONENT(object, dummy, construct, ARRAY_ADDER)

char *get_script_json(const char *name, struct scriptbase *base)
{
	const struct script *script;
	cJSON *json, *new;
	char *printed = NULL;

	script = get_script(name, base);
	if (!script || !script->filled)
		return NULL;

	json = cJSON_CreateObject();
	if (!json)
		goto free_json;

	ADD_KEY("name", String(script->name));
	ADD_KEY("location", String(script->location));
	ADD_KEY("sha256", String(script->sha256));

	printed = cJSON_Print(json);

free_json:
	cJSON_Delete(json);

	if (!printed)
		errno = ENOMEM;

	return printed;
}

char *get_bag_json(const char *name, struct scriptbase *base)
{
	const struct bag *bag;
	cJSON *json, *new, *components, *current_component;
	struct component_ref *ref;
	char type_prefix[] = "\0";
	char *printed = NULL;

	bag = get_bag(name, base);
	if (!bag || !bag->filled)
		return NULL;

	json = cJSON_CreateObject();
	if (!json)
		goto free_json;

	ADD_KEY("name", String(bag->name));
	ADD_KEY("components", Array());
	components = new;

	for (ref = bag->components; ref; ref = ref->next) {
		ADD_TO_ARRAY(components, Array());
		current_component = new;
		type_prefix[0] = ref->component_type[0];
		ADD_TO_ARRAY(current_component, String(type_prefix));
		/* name is at the same offset in struct bag and struct script */
		ADD_TO_ARRAY(current_component,
			     String(ref->component.bag->name));
	}

	printed = cJSON_Print(json);

free_json:
	cJSON_Delete(json);

	if (!printed)
		errno = ENOMEM;

	return printed;
}

cJSON *page_to_cJSON(const struct page *page)
{
	cJSON *json, *new, *payload;
	char type_prefix[] = "\0";

	json = cJSON_CreateObject();
	if (!json)
		goto free_json;

	ADD_KEY("pattern", String(page->pattern));
	if (!page->payload.any)
		goto skip_payload;
	ADD_KEY("payload", Array());
	payload = new;
	type_prefix[0] = page->payload_type[0];
	ADD_TO_ARRAY(payload, String(type_prefix));
	/* name is at the same offset in struct bag and struct script */
	ADD_TO_ARRAY(payload, String(page->payload.bag->name));

skip_payload:
	return json;

free_json:
	cJSON_Delete(json);

	errno = ENOMEM;

	return NULL;
}

char *get_pattern_json(const char *pattern, struct scriptbase *base)
{
	const struct page *page;
	cJSON *json;
	char *printed = NULL;

	page = get_pattern(pattern, base);
	if (!page)
		return NULL;

	json = page_to_cJSON(page);
	if (!json)
		return NULL;

	printed = cJSON_Print(json);
	cJSON_Delete(json);

	if (!printed)
		errno = ENOMEM;

	return printed;
}

struct page_array_building {
	cJSON *page_array;
	bool OOM_error;
};

int lookup_callback(struct page *page, void *data)
{
	struct page_array_building *building = data;
	cJSON *page_json;

	page_json = page_to_cJSON(page);
	if (!page_json) {
		building->OOM_error = true;
		return -1;
	}

	cJSON_AddItemToArray(building->page_array, page_json);

	return 0;
}

#include <stdio.h>
char *get_page_query_json(const char *url, struct scriptbase *base)
{
	struct page_array_building building = {NULL, false};
	char *printed = NULL;
	int result = -2;

	building.page_array = cJSON_CreateArray();
	if (!building.page_array)
		goto free_json;

	result = lookup_url(url, base, lookup_callback, &building);

	printf("lookup returned value is %d\n", result);

	if (building.OOM_error)
		result = -2;
	if (result < 0)
		goto free_json;

	printed = cJSON_Print(building.page_array);
	if (!printed)
		result = -2;

free_json:
	cJSON_Delete(building.page_array);

	switch (result) {
	case 0:
		break;
	case -1:
	case -3:
		errno = EINVAL;
		break;
	case -2:
		errno = ENOMEM;
	}

	return printed;
}