/*
 * =================================================================
 * Filename:            hostnameonly.c
 * Description:         Host-name only connections
 * Author:		AngryWolf <angrywolf@flashmail.com>
 * Documentation:	hostnameonly.txt (comes with the package)
 * =================================================================
 */

#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.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

extern ConfigEntry	*config_find_entry(ConfigEntry *ce, char *name);
extern void		sendto_one(aClient *to, char *pattern, ...);

#define DEF_REASON	"Couldn't resolve your hostname; connection refused"
#define DelHook(x)	if (x) HookDel(x); x = NULL
#define ircstrdup(x,y)	if (x) MyFree(x); if (!y) x = NULL; else x = strdup(y)
#define ircfree(x)	if (x) MyFree(x); x = NULL

static int		cb_test(ConfigFile *, ConfigEntry *, int, int *);
static int		cb_conf(ConfigFile *, ConfigEntry *, int);
static int		cb_rehash();
static int		cb_stats(aClient *sptr, char *stats);
static int		cb_pre_connect(aClient *sptr);

static Hook		*HookConfTest = NULL, *HookConfRun = NULL, *HookConfRehash = NULL;
static Hook		*HookPreConn = NULL, *HookStats = NULL;
static unsigned		hostnameonly_enable;
static unsigned		hostnameonly_notify;
static char		*hostnameonly_reason;

ModuleHeader MOD_HEADER(hostnameonly)
  = {
	"hostnameonly",
	"$Id: hostnameonly.c,v 4.1 2004/07/05 09:53:11 angrywolf Exp $",
	"hostname-only connections",
	"3.2-b8-1",
	NULL 
    };

// =================================================================
// Functions related to loading/unloading configuration
// =================================================================

static void InitConf()
{
	hostnameonly_enable	= 0;
	hostnameonly_notify	= 0;
	hostnameonly_reason	= NULL;
}

static void FreeConf()
{
	ircfree(hostnameonly_reason);
}

// =================================================================
// Module functions
// =================================================================

DLLFUNC int MOD_TEST(hostnameonly)(ModuleInfo *modinfo)
{
	HookConfTest = HookAddEx(modinfo->handle, HOOKTYPE_CONFIGTEST, cb_test);
	return MOD_SUCCESS;
}


DLLFUNC int MOD_INIT(hostnameonly)(ModuleInfo *modinfo)
{
	InitConf();

	HookConfRun	= HookAddEx(modinfo->handle, HOOKTYPE_CONFIGRUN, cb_conf);
	HookConfRehash	= HookAddEx(modinfo->handle, HOOKTYPE_REHASH, cb_rehash);
	HookStats	= HookAddEx(modinfo->handle, HOOKTYPE_STATS, cb_stats);
	HookPreConn	= HookAddEx(modinfo->handle, HOOKTYPE_PRE_LOCAL_CONNECT, cb_pre_connect);

	return MOD_SUCCESS;
}

DLLFUNC int MOD_LOAD(hostnameonly)(int module_load)
{
	return MOD_SUCCESS;
}

DLLFUNC int MOD_UNLOAD(hostnameonly)(int module_unload)
{
	FreeConf();

	DelHook(HookPreConn);
	DelHook(HookStats);
	DelHook(HookConfRehash);
	DelHook(HookConfRun);
	DelHook(HookConfTest);

	return MOD_SUCCESS;
}

// =================================================================
// Config file interfacing
// =================================================================

static int cb_rehash()
{
	FreeConf();
	InitConf();

	return 1;
}

static int cb_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
{
	ConfigEntry	*cep;
	int		errors = 0;

	if (type != CONFIG_SET)
		return 0;

	if (!strcmp(ce->ce_varname, "hostnameonly"))
	{
		for (cep = ce->ce_entries; cep; cep = cep->ce_next)
		{
			if (!cep->ce_varname)
			{
				config_error("%s:%i: blank set::hostnameonly item",
					cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum);	
				errors++;
				continue;
			}
			if (!cep->ce_vardata)
			{
				config_error("%s:%i: set::hostnameonly::%s item without value",
					cep->ce_fileptr->cf_filename,
					cep->ce_varlinenum, cep->ce_varname);
				errors++;
				continue;
			}
			if (!strcmp(cep->ce_varname, "enable"))
				;
			else if (!strcmp(cep->ce_varname, "notify-opers"))
				;
			else if (!strcmp(cep->ce_varname, "reason"))
				;
			else
			{
				config_error("%s:%i: unknown directive set::hostnameonly::%s",
					cep->ce_fileptr->cf_filename, cep->ce_varlinenum,
					cep->ce_varname);
				errors++;
			}
			
		}
		*errs = errors;
		return errors ? -1 : 1;
	}
	else
		return 0;
}

static int cb_conf(ConfigFile *cf, ConfigEntry *ce, int type)
{
	ConfigEntry	*cep;

	if (type != CONFIG_SET)
		return 0;

	if (!strcmp(ce->ce_varname, "hostnameonly"))
	{
		for (cep = ce->ce_entries; cep; cep = cep->ce_next)
		{
			if (!strcmp(cep->ce_varname, "enable"))
				hostnameonly_enable = config_checkval(cep->ce_vardata, CFG_YESNO);
			if (!strcmp(cep->ce_varname, "notify-opers"))
				hostnameonly_notify = config_checkval(cep->ce_vardata, CFG_YESNO);
			if (!strcmp(cep->ce_varname, "reason"))
			{
				ircstrdup(hostnameonly_reason, cep->ce_vardata);
			}
		}

		return 1;		
	}

	return 0;
}

static int cb_stats(aClient *sptr, char *stats)
{
	if (*stats == 'S')
	{
		sendto_one(sptr, ":%s %i %s :hostnameonly::enable: %d",
			me.name, RPL_TEXT, sptr->name, hostnameonly_enable);
		sendto_one(sptr, ":%s %i %s :hostnameonly::notify-opers: %d",
			me.name, RPL_TEXT, sptr->name, hostnameonly_notify);
		sendto_one(sptr, ":%s %i %s :hostnameonly::reason: %s",
			me.name, RPL_TEXT, sptr->name, hostnameonly_reason ?
				hostnameonly_reason : "<NULL>");
	}

        return 0;
}

static int cb_pre_connect(aClient *sptr)
{
	char *host, *ip_addr;
	
	if (hostnameonly_enable)
	{
#ifdef GetIP
		ip_addr = GetIP(sptr);
#else
		ip_addr	= Inet_ia2p(&sptr->ip);
#endif

		if (!strcmp(ip_addr, sptr->user->realhost))
		{
			host = make_user_host(sptr->user->username, ip_addr);

#ifdef GetIP
			if (Find_except(sptr, host, CONF_EXCEPT_BAN))
#else
			if (Find_except(host, CONF_EXCEPT_BAN))
#endif
				return 0;

			if (hostnameonly_notify)
				sendto_snomask(SNO_EYES,
					"*** Client %s!%s@%s has an IP address only; connection refused",
					sptr->name, sptr->user->username, ip_addr);

	    		return exit_client(sptr, sptr, sptr,
				hostnameonly_reason ? hostnameonly_reason : DEF_REASON);
		}
	}

	return 0;
}
