<commit_before>/* Copyright (c) 2010, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *       * Redistributions of source code must retain the above copyright
 *         notice, this list of conditions and the following disclaimer.
 *       * Redistributions in binary form must reproduce the above copyright
 *         notice, this list of conditions and the following disclaimer in the
 *         documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Rambler BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "kvstorage_config.h"
#include "main.h"
#include "cfg_xml.h"

/* Global hash of storages indexed by id */
GHashTable *storages = NULL;
/* Last used id for explicit numbering */
gint last_id = 0;

struct kvstorage_config_parser {
	enum {
		KVSTORAGE_STATE_INIT,
		KVSTORAGE_STATE_PARAM,
		KVSTORAGE_STATE_BACKEND,
		KVSTORAGE_STATE_EXPIRE,
		KVSTORAGE_STATE_ID,
		KVSTORAGE_STATE_NAME,
		KVSTORAGE_STATE_CACHE_TYPE,
		KVSTORAGE_STATE_CACHE_MAX_ELTS,
		KVSTORAGE_STATE_CACHE_MAX_MEM,
		KVSTORAGE_STATE_BACKEND_TYPE,
		KVSTORAGE_STATE_EXPIRE_TYPE,
		KVSTORAGE_STATE_ERROR
	} state;
	struct kvstorage_config *current_storage;
	memory_pool_t *pool;
	gchar *cur_elt;
};

static void
kvstorage_config_destroy (gpointer k)
{
	struct kvstorage_config				*kconf = k;

	if (kconf->name) {
		g_free (kconf->name);
	}

	g_free (kconf);
}


/* XML parse callbacks */
/* Called for open tags <foo bar="baz"> */
void kvstorage_xml_start_element (GMarkupParseContext	*context,
								const gchar         *element_name,
								const gchar        **attribute_names,
								const gchar        **attribute_values,
								gpointer             user_data,
								GError             **error)
{
	struct kvstorage_config_parser 	*kv_parser = user_data;

	switch (kv_parser->state) {
	case KVSTORAGE_STATE_INIT:
		/* Make temporary pool */
		if (kv_parser->pool != NULL) {
			memory_pool_delete (kv_parser->pool);
		}
		kv_parser->pool = memory_pool_new (memory_pool_get_size ());

		/* Create new kvstorage_config */
		kv_parser->current_storage = g_malloc0 (sizeof (struct kvstorage_config));
		kv_parser->current_storage->id = ++last_id;
		break;
	case KVSTORAGE_STATE_PARAM:
		if (g_ascii_strcasecmp (element_name, "type") == 0) {
			kv_parser->state = KVSTORAGE_STATE_CACHE_TYPE;
		}
		else if (g_ascii_strcasecmp (element_name, "max_elements") == 0) {
			kv_parser->state = KVSTORAGE_STATE_CACHE_MAX_ELTS;
		}
		else if (g_ascii_strcasecmp (element_name, "max_memory") == 0) {
			kv_parser->state = KVSTORAGE_STATE_CACHE_MAX_MEM;
		}
		else if (g_ascii_strcasecmp (element_name, "id") == 0) {
			kv_parser->state = KVSTORAGE_STATE_ID;
		}
		else if (g_ascii_strcasecmp (element_name, "name") == 0) {
			kv_parser->state = KVSTORAGE_STATE_NAME;
		}
		else if (g_ascii_strcasecmp (element_name, "backend") == 0) {
			kv_parser->state = KVSTORAGE_STATE_BACKEND;
		}
		else if (g_ascii_strcasecmp (element_name, "expire") == 0) {
			kv_parser->state = KVSTORAGE_STATE_EXPIRE;
		}
		else {
			if (*error == NULL) {
				*error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "element %s is unexpected",
						element_name);
			}
			kv_parser->state = KVSTORAGE_STATE_ERROR;
		}
		kv_parser->cur_elt = memory_pool_strdup (kv_parser->pool, element_name);
		break;
	case KVSTORAGE_STATE_BACKEND:
		if (g_ascii_strcasecmp (element_name, "type") == 0) {
			kv_parser->state = KVSTORAGE_STATE_BACKEND_TYPE;
		}
		else {
			if (*error == NULL) {
				*error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "element %s is unexpected in backend definition",
						element_name);
			}
			kv_parser->state = KVSTORAGE_STATE_ERROR;
		}
		break;
	case KVSTORAGE_STATE_EXPIRE:
		if (g_ascii_strcasecmp (element_name, "type") == 0) {
			kv_parser->state = KVSTORAGE_STATE_EXPIRE_TYPE;
		}
		else {
			if (*error == NULL) {
				*error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "element %s is unexpected in expire definition",
						element_name);
			}
			kv_parser->state = KVSTORAGE_STATE_ERROR;
		}
		break;
	default:
		/* Do nothing at other states */
		break;
	}

}

#define CHECK_TAG(s)																													\
do {																																	\
if (g_ascii_strcasecmp (element_name, kv_parser->cur_elt) == 0) {																		\
	kv_parser->state = (s);																												\
}																																		\
else {																																	\
	if (*error == NULL) *error = g_error_new (xml_error_quark (), XML_UNMATCHED_TAG, "element %s is unexpected in this state, expected %s", element_name, kv_parser->cur_elt);	\
	kv_parser->state = KVSTORAGE_STATE_ERROR;																							\
}																																		\
} while (0)

/* Called for close tags </foo> */
void kvstorage_xml_end_element (GMarkupParseContext	*context,
								const gchar         *element_name,
								gpointer             user_data,
								GError             **error)
{
	struct kvstorage_config_parser 	*kv_parser = user_data;

	switch (kv_parser->state) {
	case KVSTORAGE_STATE_INIT:
	case KVSTORAGE_STATE_PARAM:
		if (g_ascii_strcasecmp (element_name, "keystorage") == 0) {
			/* XXX: Init actual storage */
			g_hash_table_insert (storages, &kv_parser->current_storage->id, kv_parser->current_storage);
			kv_parser->state = KVSTORAGE_STATE_INIT;
			g_markup_parse_context_pop (context);
			return;
		}
		if (*error == NULL) {<commit_msg>Add initialization for kvstorages after configuration.<commit_after>/* Copyright (c) 2010, <NAME>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *       * Redistributions of source code must retain the above copyright
 *         notice, this list of conditions and the following disclaimer.
 *       * Redistributions in binary form must reproduce the above copyright
 *         notice, this list of conditions and the following disclaimer in the
 *         documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Rambler BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "kvstorage_config.h"
#include "main.h"
#include "cfg_xml.h"

#define LRU_QUEUES 32

/* Global hash of storages indexed by id */
GHashTable *storages = NULL;
/* Last used id for explicit numbering */
gint last_id = 0;

struct kvstorage_config_parser {
	enum {
		KVSTORAGE_STATE_INIT,
		KVSTORAGE_STATE_PARAM,
		KVSTORAGE_STATE_BACKEND,
		KVSTORAGE_STATE_EXPIRE,
		KVSTORAGE_STATE_ID,
		KVSTORAGE_STATE_NAME,
		KVSTORAGE_STATE_CACHE_TYPE,
		KVSTORAGE_STATE_CACHE_MAX_ELTS,
		KVSTORAGE_STATE_CACHE_MAX_MEM,
		KVSTORAGE_STATE_BACKEND_TYPE,
		KVSTORAGE_STATE_EXPIRE_TYPE,
		KVSTORAGE_STATE_ERROR
	} state;
	struct kvstorage_config *current_storage;
	memory_pool_t *pool;
	gchar *cur_elt;
};

static void
kvstorage_config_destroy (gpointer k)
{
	struct kvstorage_config				*kconf = k;

	if (kconf->name) {
		g_free (kconf->name);
	}

	if (kconf->storage) {
		rspamd_kv_storage_destroy (kconf->storage);
	}

	g_free (kconf);
}

/* Init kvstorage */
static void
kvstorage_init_callback (const gpointer key, const gpointer value, gpointer unused)
{
	struct kvstorage_config				*kconf = value;
	struct rspamd_kv_cache				*cache;
	struct rspamd_kv_backend			*backend;
	struct rspamd_kv_expire				*expire;

	switch (kconf->cache.type) {
	case KVSTORAGE_TYPE_CACHE_HASH:
		cache = rspamd_kv_hash_new ();
		break;
	case KVSTORAGE_TYPE_CACHE_RADIX:
		cache = rspamd_kv_radix_new ();
		break;
	}

	switch (kconf->backend.type) {
	case KVSTORAGE_TYPE_BACKEND_NULL:
		backend = NULL;
		break;
	}

	switch (kconf->expire.type) {
	case KVSTORAGE_TYPE_EXPIRE_LRU:
		expire = rspamd_lru_expire_new (LRU_QUEUES);
		break;
	}

	kconf->storage = rspamd_kv_storage_new (kconf->id, kconf->name, cache, backend, expire,
			kconf->cache.max_elements, kconf->cache.max_memory);
}

/* XML parse callbacks */
/* Called for open tags <foo bar="baz"> */
void kvstorage_xml_start_element (GMarkupParseContext	*context,
								const gchar         *element_name,
								const gchar        **attribute_names,
								const gchar        **attribute_values,
								gpointer             user_data,
								GError             **error)
{
	struct kvstorage_config_parser 	*kv_parser = user_data;

	switch (kv_parser->state) {
	case KVSTORAGE_STATE_INIT:
		/* Make temporary pool */
		if (kv_parser->pool != NULL) {
			memory_pool_delete (kv_parser->pool);
		}
		kv_parser->pool = memory_pool_new (memory_pool_get_size ());

		/* Create new kvstorage_config */
		kv_parser->current_storage = g_malloc0 (sizeof (struct kvstorage_config));
		kv_parser->current_storage->id = ++last_id;
		break;
	case KVSTORAGE_STATE_PARAM:
		if (g_ascii_strcasecmp (element_name, "type") == 0) {
			kv_parser->state = KVSTORAGE_STATE_CACHE_TYPE;
		}
		else if (g_ascii_strcasecmp (element_name, "max_elements") == 0) {
			kv_parser->state = KVSTORAGE_STATE_CACHE_MAX_ELTS;
		}
		else if (g_ascii_strcasecmp (element_name, "max_memory") == 0) {
			kv_parser->state = KVSTORAGE_STATE_CACHE_MAX_MEM;
		}
		else if (g_ascii_strcasecmp (element_name, "id") == 0) {
			kv_parser->state = KVSTORAGE_STATE_ID;
		}
		else if (g_ascii_strcasecmp (element_name, "name") == 0) {
			kv_parser->state = KVSTORAGE_STATE_NAME;
		}
		else if (g_ascii_strcasecmp (element_name, "backend") == 0) {
			kv_parser->state = KVSTORAGE_STATE_BACKEND;
		}
		else if (g_ascii_strcasecmp (element_name, "expire") == 0) {
			kv_parser->state = KVSTORAGE_STATE_EXPIRE;
		}
		else {
			if (*error == NULL) {
				*error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "element %s is unexpected",
						element_name);
			}
			kv_parser->state = KVSTORAGE_STATE_ERROR;
		}
		kv_parser->cur_elt = memory_pool_strdup (kv_parser->pool, element_name);
		break;
	case KVSTORAGE_STATE_BACKEND:
		if (g_ascii_strcasecmp (element_name, "type") == 0) {
			kv_parser->state = KVSTORAGE_STATE_BACKEND_TYPE;
		}
		else {
			if (*error == NULL) {
				*error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "element %s is unexpected in backend definition",
						element_name);
			}
			kv_parser->state = KVSTORAGE_STATE_ERROR;
		}
		break;
	case KVSTORAGE_STATE_EXPIRE:
		if (g_ascii_strcasecmp (element_name, "type") == 0) {
			kv_parser->state = KVSTORAGE_STATE_EXPIRE_TYPE;
		}
		else {
			if (*error == NULL) {
				*error = g_error_new (xml_error_quark (), XML_EXTRA_ELEMENT, "element %s is unexpected in expire definition",
						element_name);
			}
			kv_parser->state = KVSTORAGE_STATE_ERROR;
		}
		break;
	default:
		/* Do nothing at other states */
		break;
	}

}

#define CHECK_TAG(s)																													\
do {																																	\
if (g_ascii_strcasecmp (element_name, kv_parser->cur_elt) == 0) {																		\
	kv_parser->state = (s);																												\
}																																		\
else {																																	\
	if (*error == NULL) *error = g_error_new (xml_error_quark (), XML_UNMATCHED_TAG, "element %s is unexpected in this state, expected %s", element_name, kv_parser->cur_elt);	\
	kv_parser->state = KVSTORAGE_STATE_ERROR;																							\
}																																		\
} while (0)

/* Called for close tags </foo> */
void kvstorage_xml_end_element (GMarkupParseContext	*context,
								const gchar         *element_name,
								gpointer             user_data,
								GError             **error)
{
	struct kvstorage_config_parser 	*kv_parser = user_data;

	switch (kv_parser->state) {
	case KVSTORAGE_STATE_INIT:
	case KVSTORAGE_STATE_PARAM:
		if (g_ascii_strcasecmp (element_name, "keystorage") == 0) {
			/* XXX: Init actual storage */
			g_hash_table_insert (storages, &kv_parser->current_storage->id, kv_parser->current_storage);
			kv_parser->state = KVSTORAGE_STATE_INIT;
			g_markup_parse_context_pop (context);
			g_hash_table_foreach (storages, kvstorage_init_callback, NULL);
			return;
		}
		if (*error == NULL) {
{'fraction_non_alphanumeric': 0.09588529609439175, 'fraction_numerical': 0.0028377268314539617, 'mean_word_length': 3.438846536294332, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 14}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5487758', 'n_tokens_mistral': 5127, 'n_tokens_neox': 4522, 'n_words': 1118}