/*
 * f_auth.c
 * by fez (c) 2007 - provides authentication requirement for IRC
 * this module is only a proof of concept.  It should be expanded for
 * specific authentication schema.  f_auth_local_connect() and
 * f_auth_command() would be the places to do those.
 */

// includes
#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "proto.h"
#include "channel.h"
#include <time.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#endif
#include <fcntl.h>
#include "h.h"
#ifdef STRIPBADWORDS
#include "badwords.h"
#endif
#ifdef _WIN32
#include "version.h"
#endif

// ***** module-specific defines *****
#define MOD_AUTH_NAME		"f_auth"
#define MOD_AUTH_VERS		"$Id: f_auth.c, v1.0.2 2007/10/16 20:43:00 fez Exp $"
#define MOD_AUTH_DESC		"require AUTH before registering"
// defines:
#define MSG_AUTH		"AUTH"
#define TOK_AUTH		NULL
#define AUTH_DIRECTIVE		"require-auth"
#define MSG_NOTICE		"NOTICE"

// ***** Function Declarations *****
// config loading hook functions
DLLFUNC int f_auth_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs);
DLLFUNC int f_auth_config_posttest(int *errs);
DLLFUNC int f_auth_config_run(ConfigFile *cf, ConfigEntry *ce, int type);
DLLFUNC int f_auth_config_rehash();
// Hook functions:
DLLFUNC int f_auth_local_connect(aClient *sptr);
// commands:
DLLFUNC int f_auth_command(aClient *cptr, aClient *sptr, int parc, char *parv[]);

// ***** Global Variables *****
ModuleInfo *AuthModInfo = NULL;		// for local storage of modinfo
static int auth_conf_counter = 0;	// for config file
static Hook *AuthHookTest = NULL;	// for config hashiing
static Hook *AuthHookPosttest = NULL;	//
static Hook *AuthHookRun = NULL;	//
static Hook *AuthHookRehash = NULL;	//
static Hook *AuthHookLocalConnect = NULL;	// for local connection hook
static Command *AuthCmd;		// to store command hook status
static int auth_require = 0;		// whether to require AUTH

// a description of this module
ModuleHeader MOD_HEADER(f_auth)
  = {
	MOD_AUTH_NAME,
	MOD_AUTH_VERS,
	MOD_AUTH_DESC,
	"3.2.3",
	NULL
  };

// called before config test
DLLFUNC int MOD_TEST(f_auth)(ModuleInfo *modinfo)
{
	int ret = MOD_SUCCESS;
	AuthHookTest = HookAddEx(modinfo->handle, HOOKTYPE_CONFIGTEST, f_auth_config_test);
	AuthHookPosttest = HookAddEx(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, f_auth_config_posttest);
	auth_conf_counter = 0;
	return ret;
}

// called during module initialization
DLLFUNC int MOD_INIT(f_auth)(ModuleInfo *modinfo)
{
	int ret = MOD_SUCCESS;
	AuthModInfo = modinfo;
	AuthHookRun = HookAddEx(modinfo->handle, HOOKTYPE_CONFIGRUN, f_auth_config_run);
	AuthHookRehash = HookAddEx(modinfo->handle, HOOKTYPE_REHASH, f_auth_config_rehash);
	AuthHookLocalConnect = HookAddEx(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, f_auth_local_connect);
	AuthCmd = CommandAdd(modinfo->handle, MSG_AUTH, TOK_AUTH, f_auth_command, MAXPARA, M_UNREGISTERED|M_USER);
	if (!AuthCmd)
		ret = MOD_FAILED;
	return ret;
}

// called when ircd is 100% ready
DLLFUNC int MOD_LOAD(f_auth)(int module_load)
{
	int ret = MOD_SUCCESS;
	return ret;
}

// called when unloading a module
DLLFUNC int MOD_UNLOAD(f_auth)(int module_unload)
{
	int ret = MOD_SUCCESS;
	CommandDel(AuthCmd);
	HookDel(AuthHookTest);
	HookDel(AuthHookPosttest);
	HookDel(AuthHookRun);
	HookDel(AuthHookRehash);
	HookDel(AuthHookLocalConnect);
	AuthCmd = NULL;
	AuthHookLocalConnect =
	AuthHookTest = AuthHookPosttest = AuthHookRun = AuthHookRehash = NULL;
	return ret;
}

// this is where we TEST the config file
DLLFUNC int f_auth_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
{
	int errors = 0;
	if (type != CONFIG_SET)
		return 0;
	if (!strcmp(ce->ce_varname, AUTH_DIRECTIVE))
	{
		if (ce->ce_vardata)
		{
			config_error("%s:%i: set::%s with unnecessary value",
				ce->ce_fileptr->cf_filename,
				ce->ce_varlinenum, ce->ce_varname );
			errors++;
		}
		else if (auth_conf_counter)
		{
			config_error("%s:%i: set::%s already defined on line %i",
				ce->ce_fileptr->cf_filename,
				ce->ce_varlinenum, ce->ce_varname,
				auth_conf_counter );
				errors++;
		}
		else
		{
			auth_conf_counter = ce->ce_varlinenum;
			!auth_conf_counter && ++auth_conf_counter;
		}
		*errs = errors;
		return errors ? -1 : 1;
	}
	return 0;
}

// this is run AFTER testing the config file
DLLFUNC int f_auth_config_posttest(int *errs)
{
	int errors = 0;
	if (!auth_conf_counter)
	{
		//config_error("set::%s not set.", AUTH_DIRECTIVE );
		//errors++;
		////or...
		auth_require = 0;
	}
	*errs = errors;
	return errors ? -1 : 1;
}

// this is run WHEN LOADING SETTINGS from the config file
DLLFUNC int f_auth_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
{
	if (type != CONFIG_SET)
		return 0;
	if (!strcmp(ce->ce_varname, AUTH_DIRECTIVE))
	{
		auth_require = 1;
		return 1;
	}
	return 0;
}

// this is run when REHASHING the config file
DLLFUNC int f_auth_config_rehash()
{
	auth_conf_counter = 0;
	return 1;
}

// this is the local connect hook function
DLLFUNC int f_auth_local_connect(aClient *sptr)
{
	if (!auth_require || !MyConnect(sptr) || !IsClient(sptr))
		return 0;
	sendto_one(sptr, "%s %s %s :To connect please type /quote %s %X",
		me.name, MSG_NOTICE, sptr->name,
		MSG_AUTH, sptr->firsttime );
	SetUnknown(sptr);
	IRCstats.unknown++;
	IRCstats.clients--;
	IRCstats.me_clients--;
	me.serv->users--;
	return 0;
}

// this is the AUTH command
DLLFUNC int f_auth_command(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
	char authcode[32];
	if (!auth_require || !MyConnect(sptr) || !IsUnknown(sptr))
		return 0;
	sprintf(authcode, "%X", sptr->firsttime);
	if (parc > 1 && !strcmp(parv[1], authcode))
	{
		sendto_one(sptr, "%s %s %s :%s code accepted.",
			me.name, MSG_NOTICE, sptr->name, MSG_AUTH );
		SetClient(sptr);
		IRCstats.unknown--;
		IRCstats.clients++;
		IRCstats.me_clients++;
		me.serv->users++;
	}
	else
	{
		sendto_one(sptr, "%s %s %s :%s code invalid.",
			me.name, MSG_NOTICE, sptr->name, MSG_AUTH );
	}
	return 0;
}

/* end of code */
