/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */

#include "auth-common.h"
#include "userdb.h"
#include "auth-cache.h"

#if defined(BUILTIN_LUA) || defined(PLUGIN_BUILD)

#include "db-lua.h"

struct dlua_userdb_module {
	struct userdb_module module;
	struct dlua_script *script;
	const char *file;
};

static void userdb_lua_lookup(struct auth_request *auth_request,
			      userdb_callback_t *callback)
{
	struct userdb_module *_module = auth_request->userdb->userdb;
	struct dlua_userdb_module *module =
		(struct dlua_userdb_module *)_module;
	const char *error;
	enum userdb_result result =
		auth_lua_call_userdb_lookup(module->script, auth_request, &error);
	if (result == USERDB_RESULT_INTERNAL_FAILURE)
		e_error(authdb_event(auth_request),
			"userdb-lua: %s", error);
	callback(result, auth_request);
}

static struct userdb_module *
userdb_lua_preinit(pool_t pool, const char *args)
{
	struct dlua_userdb_module *module;
	const char *cache_key = "%u";
	bool blocking = TRUE;

	module = p_new(pool, struct dlua_userdb_module, 1);
	const char *const *fields = t_strsplit_spaces(args, " ");
	while(*fields != NULL) {
		if (str_begins(*fields, "file=")) {
			 module->file = p_strdup(pool, (*fields)+5);
		} else if (str_begins(*fields, "blocking=")) {
			const char *value = (*fields)+9;
			if (strcmp(value, "yes") == 0) {
				blocking = TRUE;
			} else if (strcmp(value, "no") == 0) {
				blocking = FALSE;
			} else {
				i_fatal("Invalid value %s. "
					"Field blocking must be yes or no",
					value);
			}
		} else if (str_begins(*fields, "cache_key=")) {
			if (*((*fields)+10) != '\0')
				cache_key = (*fields)+10;
			else /* explicitly disable auth caching for lua */
				cache_key = NULL;
		} else {
			i_fatal("Unsupported parameter %s", *fields);
		}
		fields++;
	}

	if (module->file == NULL)
		i_fatal("userdb-lua: Missing mandatory file= parameter");

	module->module.blocking = blocking;
	if (cache_key != NULL) {
		module->module.default_cache_key =
			auth_cache_parse_key(pool, cache_key);
	}
	return &module->module;
}

static void userdb_lua_init(struct userdb_module *_module)
{
	struct dlua_userdb_module *module =
		(struct dlua_userdb_module *)_module;
	const char *error;

	if (dlua_script_create_file(module->file, &module->script, auth_event, &error) < 0 ||
	    auth_lua_script_init(module->script, &error) < 0)
		i_fatal("userdb-lua: initialization failed: %s", error);
}

static void userdb_lua_deinit(struct userdb_module *_module)
{
	struct dlua_userdb_module *module =
		(struct dlua_userdb_module *)_module;
	dlua_script_unref(&module->script);
}

static struct userdb_iterate_context *
userdb_lua_iterate_init(struct auth_request *auth_request,
			userdb_iter_callback_t *callback,
			void *context)
{
	struct userdb_module *_module = auth_request->userdb->userdb;
	struct dlua_userdb_module *module =
		(struct dlua_userdb_module *)_module;
	return auth_lua_call_userdb_iterate_init(module->script, auth_request,
						 callback, context);
}

static void userdb_lua_iterate_next(struct userdb_iterate_context *ctx)
{
	auth_lua_userdb_iterate_next(ctx);
}

static int userdb_lua_iterate_deinit(struct userdb_iterate_context *ctx)
{
	return auth_lua_userdb_iterate_deinit(ctx);
}

#ifndef PLUGIN_BUILD
struct userdb_module_interface userdb_lua =
#else
struct userdb_module_interface userdb_lua_plugin =
#endif
{
	"lua",

	userdb_lua_preinit,
	userdb_lua_init,
	userdb_lua_deinit,

	userdb_lua_lookup,

	userdb_lua_iterate_init,
	userdb_lua_iterate_next,
	userdb_lua_iterate_deinit
};
#else
struct userdb_module_interface userdb_lua = {
	.name = "lua"
};
#endif
