/* Dynamic loading of the authentication backend.
 * We use GLib's multiplatform dl() wrapper
 * to open up libauthsql or libauthldap and
 * populate the global 'auth' structure.
 *
 * (c) 2005 Aaron Stone <aaron@serendipity.cx>
 */

#include <gmodule.h>

#include "config.h"
#include "dbmailtypes.h"
#include "debug.h"
#include "auth.h"
#include "authmodule.h"

static auth_func_t *auth = NULL;

extern db_param_t _db_params;

/* Returns:
 *  1 on modules unsupported
 *  0 on success
 * -1 on failure to load module
 * -2 on missing symbols
 * -3 on memory error
 */
int auth_load_driver(void)
{
	GModule *module;
	char *lib = NULL;
	char *driver = NULL;

	if (!g_module_supported()) {
		trace(TRACE_FATAL, "%s,%s: loadable modules unsupported on this platform",
				__FILE__, __func__);
		return 1;
	}

	if (! (auth = g_new0(auth_func_t,1))) {
		trace(TRACE_FATAL, "%s,%s: cannot allocate memory",
				__FILE__, __func__);
		return -3;
	}

	if (strcasecmp(_db_params.authdriver, "SQL") == 0)
		driver = "auth_sql";
	else if (strcasecmp(_db_params.authdriver, "LDAP") == 0)
		driver = "auth_ldap";
	else
		trace(TRACE_FATAL, "%s,%s: unsupported driver: %s,"
				" please choose from SQL or LDAP",
				__FILE__, __func__,
				_db_params.authdriver);

	/* Try local build area, then dbmail lib paths, then system lib path. */
	int i;
	char *lib_path[] = {
		"modules/.libs",
		PREFIX "/lib",
		PREFIX "/lib/dbmail",
		NULL };
	for (i = 0; i < 4; i++) {
		lib = g_module_build_path(lib_path[i], driver);
		module = g_module_open(lib, 0); // non-lazy bind.
		if (module)
			break;
	}

	/* If the list is exhausted without opening a module, we'll catch it. */
	if (!module) {
		trace(TRACE_FATAL, "%s,%s: cannot load %s", 
				__FILE__, __func__, g_module_error());
		return -1;
	}

	if (!g_module_symbol(module, "auth_connect",                (gpointer)&auth->connect                )
	||  !g_module_symbol(module, "auth_disconnect",             (gpointer)&auth->disconnect             )
	||  !g_module_symbol(module, "auth_user_exists",            (gpointer)&auth->user_exists            )
	||  !g_module_symbol(module, "auth_get_userid",             (gpointer)&auth->get_userid             )
	||  !g_module_symbol(module, "auth_check_userid",           (gpointer)&auth->check_userid           )
	||  !g_module_symbol(module, "auth_get_known_users",        (gpointer)&auth->get_known_users        )
	||  !g_module_symbol(module, "auth_getclientid",            (gpointer)&auth->getclientid            )
	||  !g_module_symbol(module, "auth_getmaxmailsize",         (gpointer)&auth->getmaxmailsize         )
	||  !g_module_symbol(module, "auth_getencryption",          (gpointer)&auth->getencryption          )
	||  !g_module_symbol(module, "auth_check_user_ext",         (gpointer)&auth->check_user_ext         )
	||  !g_module_symbol(module, "auth_adduser",                (gpointer)&auth->adduser                )
	||  !g_module_symbol(module, "auth_delete_user",            (gpointer)&auth->delete_user            )
	||  !g_module_symbol(module, "auth_change_username",        (gpointer)&auth->change_username        )
	||  !g_module_symbol(module, "auth_change_password",        (gpointer)&auth->change_password        )
	||  !g_module_symbol(module, "auth_change_clientid",        (gpointer)&auth->change_clientid        )
	||  !g_module_symbol(module, "auth_change_mailboxsize",     (gpointer)&auth->change_mailboxsize     )
	||  !g_module_symbol(module, "auth_validate",               (gpointer)&auth->validate               )
	||  !g_module_symbol(module, "auth_md5_validate",           (gpointer)&auth->md5_validate           )
	||  !g_module_symbol(module, "auth_get_users_from_clientid",(gpointer)&auth->get_users_from_clientid)
	||  !g_module_symbol(module, "auth_get_user_aliases",       (gpointer)&auth->get_user_aliases       )
	||  !g_module_symbol(module, "auth_get_aliases_ext",        (gpointer)&auth->get_aliases_ext        )
	||  !g_module_symbol(module, "auth_addalias",               (gpointer)&auth->addalias               )
	||  !g_module_symbol(module, "auth_addalias_ext",           (gpointer)&auth->addalias_ext           )
	||  !g_module_symbol(module, "auth_removealias",            (gpointer)&auth->removealias            )
	||  !g_module_symbol(module, "auth_removealias_ext",        (gpointer)&auth->removealias_ext        )
	||  !g_module_symbol(module, "auth_requires_shadow_user",   (gpointer)&auth->requires_shadow_user   )) {
		trace(TRACE_FATAL, "%s,%s: cannot find function %s", 
				__FILE__, __func__, g_module_error());
		return -2;
	}

	return 0;
}

/* This is the first auth_* call anybody should make. */
int auth_connect(void)
{
	auth_load_driver();
	return auth->connect();
}

/* But sometimes this gets called after help text or an
 * error but without a matching auth_connect before it. */
int auth_disconnect(void)
{
	if (!auth) return 0;
	auth->disconnect();
	g_free(auth);
	return 0;
}

int auth_user_exists(const char *username, u64_t * user_idnr)
	{ return auth->user_exists(username, user_idnr); }
char *auth_get_userid(u64_t user_idnr)
	{ return auth->get_userid(user_idnr); }
int auth_check_userid(u64_t user_idnr)
	{ return auth->check_userid(user_idnr); }
GList * auth_get_known_users(void)
	{ return auth->get_known_users(); }
int auth_getclientid(u64_t user_idnr, u64_t * client_idnr)
	{ return auth->getclientid(user_idnr, client_idnr); }
int auth_getmaxmailsize(u64_t user_idnr, u64_t * maxmail_size)
	{ return auth->getmaxmailsize(user_idnr, maxmail_size); }
char *auth_getencryption(u64_t user_idnr)
	{ return auth->getencryption(user_idnr); }
int auth_check_user_ext(const char *username, struct dm_list *userids,
		struct dm_list *fwds, int checks)
	{ return auth->check_user_ext(username, userids, fwds, checks); }
int auth_adduser(const char *username, const char *password, const char *enctype,
		u64_t clientid, u64_t maxmail, u64_t * user_idnr)
	{ return auth->adduser(username, password, enctype,
			clientid, maxmail, user_idnr); }
int auth_delete_user(const char *username)
	{ return auth->delete_user(username); }
int auth_change_username(u64_t user_idnr, const char *new_name)
	{ return auth->change_username(user_idnr, new_name); }
int auth_change_password(u64_t user_idnr,
		const char *new_pass, const char *enctype)
	{ return auth->change_password(user_idnr, new_pass, enctype); }
int auth_change_clientid(u64_t user_idnr, u64_t new_cid)
	{ return auth->change_clientid(user_idnr, new_cid); }
int auth_change_mailboxsize(u64_t user_idnr, u64_t new_size)
	{ return auth->change_mailboxsize(user_idnr, new_size); }
int auth_validate(clientinfo_t *ci, char *username, char *password, u64_t * user_idnr)
	{ return auth->validate(ci, username, password, user_idnr); }
u64_t auth_md5_validate(clientinfo_t *ci, char *username,
		unsigned char *md5_apop_he, char *apop_stamp)
	{ return auth->md5_validate(ci, username,
			md5_apop_he, apop_stamp); }
int auth_get_users_from_clientid(u64_t client_id, 
		u64_t ** user_ids, unsigned *num_users)
	{ return auth->get_users_from_clientid(client_id,
			user_ids, num_users); }
GList * auth_get_user_aliases(u64_t user_idnr)
	{ return auth->get_user_aliases(user_idnr); }
GList * auth_get_aliases_ext(const char *alias)
	{ return auth->get_aliases_ext(alias); }
int auth_addalias(u64_t user_idnr, const char *alias, u64_t clientid)
	{ return auth->addalias(user_idnr, alias, clientid); }
int auth_addalias_ext(const char *alias, const char *deliver_to,
		u64_t clientid)
	{ return auth->addalias_ext(alias, deliver_to, clientid); }
int auth_removealias(u64_t user_idnr, const char *alias)
	{ return auth->removealias(user_idnr, alias); }
int auth_removealias_ext(const char *alias, const char *deliver_to)
	{ return auth->removealias_ext(alias, deliver_to); }
gboolean auth_requires_shadow_user(void)
	{ return auth->requires_shadow_user(); }



syntax highlighted by Code2HTML, v. 0.9.1