/*
 * =================================================================
 * Filename:            m_rmtkl.c
 * Description:         Command /rmtkl
 * Author:		AngryWolf <angrywolf@flashmail.com>
 * Documentation:       m_rmtkl.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

#ifndef MODVAR /* Unreal3.2.1 */
 #define MODVAR
#endif

#define IsParam(x)      (parc > (x) && !BadPtr(parv[(x)]))
#define IsNotParam(x)   (parc <= (x) || BadPtr(parv[(x)]))
#define DelCommand(x)	if (x) CommandDel(x); x = NULL

extern void		sendto_one(aClient *to, char *pattern, ...);
extern void		sendto_serv_butone_token(aClient *one, char *prefix, char *command, char *token, char *pattern, ...);
extern aTKline		*tkl_del_line(aTKline *tkl);
#ifdef TKLISTLEN /* Unreal3.2-RC1 */
extern MODVAR int	tkl_hash(char c);
extern MODVAR aTKline	*tklines[TKLISTLEN];
#else
extern aTKline		*tklines;
#endif

static int		m_rmtkl(aClient *cptr, aClient *sptr, int parc, char *parv[]);

Command			*CmdRmtkl = NULL;

ModuleHeader MOD_HEADER(m_rmtkl)
  = {
	"rmtkl",
	"$Id: m_rmtkl.c,v 3.3 2004/08/30 13:55:54 angrywolf Exp $",
	"command /rmtkl",
	"3.2-b8-1",
	NULL 
    };


DLLFUNC int MOD_INIT(m_rmtkl)(ModuleInfo *modinfo)
{
	if (CommandExists("RMTKL"))
    	{
		config_error("Command RMTKL already exists");
		return MOD_FAILED;
    	}

	CmdRmtkl = CommandAdd(modinfo->handle, "RMTKL", NULL, m_rmtkl, 3, 0);
//	CmdRmtkl = CommandAdd(modinfo->handle, "RMTKL", NULL, m_rmtkl, MAXPARA, 0);

#ifndef STATIC_LINKING
	if (ModuleGetError(modinfo->handle) != MODERR_NOERROR || !CmdRmtkl)
#else
	if (!CmdRmtkl)
#endif
	{
#ifndef STATIC_LINKING
		config_error("Error adding command RMTKL: %s",
			ModuleGetErrorStr(modinfo->handle));
#else
		config_error("Error adding command RMTKL");
#endif
		return MOD_FAILED;
	}

	return MOD_SUCCESS;
}

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


DLLFUNC int MOD_UNLOAD(m_rmtkl)(int module_unload)
{
	DelCommand(CmdRmtkl);
	return MOD_SUCCESS;
}

/*
 * =================================================================
 * tkl_check_local_remove_shun:
 *     Copied from src/s_kline.c (because it's declared statically,
 *     but I want to use it).
 * =================================================================
 */

static void tkl_check_local_remove_shun(aTKline *tmp)
{
	long i1, i;
	char *chost, *cname, *cip;
	int  is_ip;
	aClient *acptr;

	for (i1 = 0; i1 <= 5; i1++)
	{
		for (i = 0; i <= LastSlot; ++i)
		{
			if ((acptr = local[i]))
				if (MyClient(acptr) && IsShunned(acptr))
				{
					chost = acptr->sockhost;
					cname = acptr->user->username;

	
					cip = (char *)Inet_ia2p(&acptr->ip);

					if (!(*tmp->hostmask < '0') && (*tmp->hostmask > '9'))
						is_ip = 1;
					else
						is_ip = 0;

					if (is_ip ==
					    0 ? (!match(tmp->hostmask,
					    chost)
					    && !match(tmp->usermask,
					    cname)) : (!match(tmp->
					    hostmask, chost)
					    || !match(tmp->hostmask,
					    cip))
					    && !match(tmp->usermask,
					    cname))
					{
						ClearShunned(acptr);
#ifdef SHUN_NOTICES
						sendto_one(acptr,
						    ":%s NOTICE %s :*** You are no longer shunned",
						    me.name,
						    acptr->name);
#endif
					}
				}
		}
	}
}

/*
 * =================================================================
 * my_tkl_del_line:
 *     Modified version of tkl_del_line (from src/s_kline.c),
 *     because using loops is unnecessary here). Also, I don't
 *     delete any spamfilter entries with this module either.
 * =================================================================
 */

#ifdef TKLISTLEN /* Unreal3.2-RC1 */
void my_tkl_del_line(aTKline *p, int tklindex)
{
	MyFree(p->hostmask);
	MyFree(p->reason);
	MyFree(p->setby);
 #ifdef OFLAG_ADDLINE /* Unreal3.2.1 */
	if ((p->type & TKL_KILL || p->type & TKL_ZAP || p->type & TKL_SHUN)
	    && p->ptr.netmask)
		MyFree(p->ptr.netmask);
 #endif
	DelListItem(p, tklines[tklindex]);
	MyFree(p);
}
#endif

/*
 * =================================================================
 * dumpit:
 *     Dump a NULL-terminated array of strings to user sptr using
 *     the numeric rplnum, and then return 0.
 *     (Taken from DarkFire IRCd)
 * =================================================================
 */

static int dumpit(aClient *sptr, char **p)
{
        for (; *p != NULL; p++)
                sendto_one(sptr, ":%s %03d %s :%s",
                        me.name, RPL_TEXT, sptr->name, *p);
        /* let user take 8 seconds to read it! */
        sptr->since += 8;
        return 0;
}

/* help for /rmtkl command */

static char *rmtkl_help[] =
{
	"*** Help on /rmtkl *** ",
	"COMMAND - Removes all TKLs matching the given conditions from the",
	"local server or the IRC Network depending on it's a global ban or not.",
	"With this command you can remove any type of TKLs (including K:Line",
	"G:Line, Z:Line, Global Z:Line and Shun).",
	"Syntax:",
	"    /rmtkl type user@host [comment]",
	"The type field may contain any number of the following characters:",
	"    K, z, G, Z, q, Q and *",
	"    (asterix includes every types but q & Q).",
	"The user@host field is a wildcard mask to match an user@host which",
        "    a ban was set on.",
	"The comment field is also wildcard mask that you can match the",
	"    text of the reason for a ban.",
	"Examples:",
	"    - /rmtkl * *",
	"        [remove all TKLs but q and Q lines]",
	"    - /rmtkl GZ *@*.mx",
	"        [remove all Mexican G/Z:Lines]",
	"    - /rmtkl * * *Zombie*",
	"        [remove all non-nick bans having Zombie in their reasons]",
	"*** End of help ***",
	NULL
};

// =================================================================
// Array of TKL types
// =================================================================

typedef struct _tkl_type TKLType;

struct _tkl_type
{
	int	type;
	char	flag;
	char	*txt;
	u_long	oflag;
};

TKLType tkl_types[] =
{
	{ TKL_KILL,			'K',	"K:Line",		OFLAG_KLINE	},
	{ TKL_ZAP,			'z',	"Z:Line",		OFLAG_ZLINE	},
	{ TKL_KILL | TKL_GLOBAL,	'G',	"G:Line",		OFLAG_TKL	},
	{ TKL_ZAP | TKL_GLOBAL,		'Z',	"Global Z:Line",	OFLAG_GZL	},
	{ TKL_SHUN | TKL_GLOBAL,	's',	"Shun",			OFLAG_TKL	},
#ifdef TKL_NICK
	{ TKL_NICK,			'q',	"Q:Line",		OFLAG_TKL	},
	{ TKL_NICK | TKL_GLOBAL,	'Q',	"Global Q:Line",	OFLAG_TKL	},
#endif
	{ 0,				0,	"Unknown *:Line",	0		},
};

#ifndef TKLISTLEN /* Unreal3.2-RC1 */
static TKLType *find_TKLType_by_type(int type)
{
	TKLType *t;

	for (t = tkl_types; t->type; t++)
		if (t->type == type)
			break;

	return t;
}
#endif

static TKLType *find_TKLType_by_flag(char flag)
{
	TKLType *t;

	for (t = tkl_types; t->type; t++)
		if (t->flag == flag)
			break;

	return t;
}

/*
 * =================================================================
 * m_rmtkl -- Remove all matching TKLs from the network
 *     parv[0] = sender prefix
 *     parv[1] = ban types
 *     parv[2] = userhost mask
 *     parv[3] = comment mask (optional)
 * =================================================================
 */

static int m_rmtkl(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
	aTKline		*tk, *next = NULL;
	TKLType		*tkltype;
	char		*types, *uhmask, *cmask, *p;
	char		gmt[256], flag;
#ifdef TKLISTLEN /* Unreal3.2-RC1 */
	int		tklindex;
#endif

	if (!IsULine(sptr) && !(IsPerson(sptr) && IsAnOper(sptr)))
	{
		sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
		return -1;
	}

	if (IsNotParam(1))
		return dumpit(sptr, rmtkl_help);

	if (IsNotParam(2))
	{
		/*
		 * In this case we don't send the entire help text to
		 * the client.
		 */
		sendto_one(sptr, ":%s NOTICE %s :Not enough parameters. "
			"Type /%s for help.",
			me.name, parv[0], "RMTKL");
		return 0;
	}

	types	= parv[1];
	uhmask	= parv[2];
	cmask	= IsParam(3) ? parv[3] : NULL;

	/* I don't add 'q' and 'Q' here. They are different. */
	if (strchr(types, '*'))
		types = "KzGZs";

	/* check access */
	if (!IsULine(sptr))
		for (p = types; *p; p++)
		{
			tkltype = find_TKLType_by_flag(*p);
			if (!tkltype->type)
				continue;
			if (((tkltype->type & TKL_GLOBAL) && !IsOper(sptr))
			    || !(sptr->oflag & tkltype->oflag))
			{
				sendto_one(sptr, err_str(ERR_NOPRIVILEGES),
					me.name, parv[0]);
				return -1;
			}
		}

#ifdef TKLISTLEN /* Unreal3.2-RC1 */
	for (tkltype = tkl_types; tkltype->type; tkltype++)
	{
		flag		= tkltype->flag;
		tklindex	= tkl_hash(flag);

		if (!strchr(types, flag))
			continue;

		for (tk = tklines[tklindex]; tk; tk = next)
		{
			next = tk->next;

			if (tk->type != tkltype->type)
				continue;
 #ifdef TKL_NICK
			if (tk->type & TKL_NICK)
			{
				/*
				 * If it's a services hold (ie. NickServ is holding
				 * a nick), it's better not to touch it
				 */
				if (*tk->usermask == 'H')
					continue;
				if (match(uhmask, tk->hostmask))
					continue;
			}
			else
 #endif
				if (match(uhmask, make_user_host(tk->usermask, tk->hostmask)))
					continue;

			if (cmask && _match(cmask, tk->reason))
				continue;

			strncpyzt(gmt, asctime(gmtime((TS *)&tk->set_at)),
				sizeof gmt);
			iCstrip(gmt);

 #ifdef TKL_NICK
			if (tk->type & TKL_NICK)
			{
				sendto_snomask(SNO_TKL, "%s removed %s %s (set at %s "
					"- reason: %s)",
					sptr->name, tkltype->txt, tk->hostmask, gmt,
					tk->reason);
				ircd_log(LOG_TKL, "%s removed %s %s (set at %s "
					"- reason: %s)",
					sptr->name, tkltype->txt, tk->hostmask,
					gmt, tk->reason);
			}
			else
			{
 #endif
				sendto_snomask(SNO_TKL, "%s removed %s %s@%s (set at "
					"%s - reason: %s)",
					sptr->name, tkltype->txt, tk->usermask,
					tk->hostmask, gmt, tk->reason);
				ircd_log(LOG_TKL, "%s removed %s %s@%s (set at "
					"%s - reason: %s)",
					sptr->name, tkltype->txt, tk->usermask,
					tk->hostmask, gmt, tk->reason);
 #ifdef TKL_NICK
			}
 #endif

			if ((tk->type & TKL_GLOBAL) && flag)
				sendto_serv_butone_token(&me, me.name, MSG_TKL, TOK_TKL,
					"- %c %s %s %s",
					flag, tk->usermask, tk->hostmask, parv[0]);
			if (tk->type & TKL_SHUN)
				tkl_check_local_remove_shun(tk);
			my_tkl_del_line(tk, tklindex);
		}
	}
#else
	for (tk = tklines; tk; tk = (aTKline *) next)
	{
		next = (ListStruct *) tk->next;

		tkltype = find_TKLType_by_type(tk->type);
		flag = tkltype->flag;

		if (!strchr(types, flag))
			continue;
		if (match(uhmask, make_user_host(tk->usermask, tk->hostmask)))
			continue;
		if (cmask && _match(cmask, tk->reason))
			continue;

		strncpyzt(gmt, asctime(gmtime((TS *)&tk->set_at)), sizeof(gmt));
		iCstrip(gmt);

		sendto_snomask(SNO_TKL, "%s removed %s %s@%s (set at %s - reason: %s)",
			sptr->name, tkltype->txt, tk->usermask, tk->hostmask, gmt,
			tk->reason);
		ircd_log(LOG_TKL, "%s removed %s %s@%s (set at %s - reason: %s)",
			sptr->name, tkltype->txt, tk->usermask, tk->hostmask, gmt,
			tk->reason);
		if ((tk->type & TKL_GLOBAL) && flag)
			sendto_serv_butone_token(&me, me.name,
				MSG_TKL, TOK_TKL,
				"- %c %s %s %s",
				flag, tk->usermask, tk->hostmask, parv[0]);

		if (tk->type & TKL_SHUN)
			tkl_check_local_remove_shun(tk);
		tkl_del_line(tk);
	}
#endif

	return 0;
}
