/*
 * Color ban. (C) 2015 Sam van Kampen.
 *
 * This program is free software, you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation blah blah blah
 * licensing crap find it at https://www.gnu.org/licenses/gpl-3.0.en.html
 */

#include <ctype.h>

#include "config.h"
#include "struct.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>
#include <fcntl.h>

#include "h.h"

#define MAX_EXTBANC_PER_CHAN	15 /* Maximum amount of color bans */
#define MAX_LENGTH				150 /* Max ban length (in characters) */

/* Taken from xchat by Peter Zelezny
 * changed very slightly by codemastr
 * RGB color stripping support added -- codemastr
 */

char *_StripColors(unsigned char *text) {
	int i = 0, len = strlen(text), save_len=0;
	char nc = 0, col = 0, rgb = 0, *save_text=NULL;
	static unsigned char new_str[4096];

	while (len > 0)
	{
		if ((col && isdigit(*text) && nc < 2) || (col && *text == ',' && nc < 3))
		{
			nc++;
			if (*text == ',')
				nc = 0;
		}
		/* Syntax for RGB is ^DHHHHHH where H is a hex digit.
		 * If < 6 hex digits are specified, the code is displayed
		 * as text
		 */
		else if ((rgb && isxdigit(*text) && nc < 6) || (rgb && *text == ',' && nc < 7))
		{
			nc++;
			if (*text == ',')
				nc = 0;
		}
		else
		{
			if (col)
				col = 0;
			if (rgb)
			{
				if (nc != 6)
				{
					text = save_text+1;
					len = save_len-1;
					rgb = 0;
					continue;
				}
				rgb = 0;
			}
			if (*text == '\003')
			{
				col = 1;
				nc = 0;
			}
			else if (*text == '\004')
			{
				save_text = text;
				save_len = len;
				rgb = 1;
				nc = 0;
			}
			else if (*text != '\026') /* (strip reverse too) */
			{
				new_str[i] = *text;
				i++;
			}
		}
		text++;
		len--;
	}
	new_str[i] = 0;
	return new_str;
}

ModuleHeader MOD_HEADER(colorban)
	= {
		"colorban",
		"v1.0",
		"ExtBan ~C (colorban) by Sam van Kampen",
		"3.2-b8-1",
		NULL
	};

char *extban_modeC_conv_param(char *parameters);
int extban_modeC_is_banned(aClient *sptr, aChannel *chptr, char *ban, int type);
int extban_modeC_is_ok(aClient *sptr, aChannel *chptr, char *para, int checkt, int mode, int exbtype);
DLLFUNC char *colorban_chanmsg(aClient *, aClient *, aChannel *, char *, int);

DLLFUNC int MOD_INIT(colorban)(ModuleInfo *modinfo) {
	ExtbanInfo request;

	memset(&request, 0, sizeof(ExtbanInfo));
	request.flag = 'C';
	request.conv_param = extban_modeC_conv_param;
	request.is_banned = extban_modeC_is_banned;
	request.is_ok = extban_modeC_is_ok;

	if (!ExtbanAdd(modinfo->handle, request)) {
		config_error("colorban: adding extban ~C failed; module not loaded.");
		return MOD_FAILED;
	}

	ModuleSetOptions(modinfo->handle, MOD_OPT_PERM);

	HookAddPCharEx(modinfo->handle, HOOKTYPE_CHANMSG, colorban_chanmsg);

	sendto_opers("Successfully loaded module: colorban");
	return MOD_SUCCESS;
}

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

DLLFUNC int MOD_UNLOAD(colorban)(int module_unload) {
	return MOD_SUCCESS;
}

_Bool isColorBan(char *string) {
	return strncmp("~C:", string, 3) == 0;
}

unsigned int countcolorbans(aChannel *chptr) {
	Ban *ban;
	unsigned int count = 0;

	for (ban = chptr->banlist; ban; ban = ban->next) {
		if (isColorBan(ban->banstr))
			count++;
	}

	for (ban = chptr->exlist; ban; ban = ban->next) {
		if (isColorBan(ban->banstr))
			count++;
	}

	return count;
}

int extban_modeC_is_ok(aClient *sptr, aChannel *chptr, char *param,
		int checkt, int mode, int exbtype)
{
	int n;

	if ((mode == MODE_ADD) && (exbtype == EXBTYPE_EXCEPT) && MyClient(sptr))
		return 0;

	/* check the # of bans in the channel, may not exceed maximum */
	if ((mode == MODE_ADD) && (checkt == EXBCHK_PARAM) &&
		MyClient(sptr) && !IsAnOper(sptr) &&
		(( n = countcolorbans(chptr)) >= MAX_EXTBANC_PER_CHAN))
	{
		sendto_one(sptr, err_str(ERR_BANLISTFULL), me.name, sptr->name, chptr->chname, param);
		sendnotice(sptr, "Too many colorbans for this channel");
		return 0;
	}
	return 1;
}

char *extban_modeC_conv_param(char *parameters) {
	// TODO: parameter checking?

	return parameters;
}

int extban_modeC_is_banned(aClient *sptr, aChannel *chptr, char *ban, int type)
{
	/* Never banned here */
	return 0;
}

_Bool uhostMatches(char *banstring, char *uhost) {
	char tmp[512] = {0};
	strncpy(tmp, banstring + 3, strlen(banstring));
	return match(banstring, uhost);
}

DLLFUNC char *colorban_chanmsg(aClient *cptr, aClient *sptr,
		aChannel *chptr, char *text, int notice)
{
	char uhost[USERLEN + HOSTLEN + 16];
	size_t text_length = strlen(text);

	ircsprintf(uhost, "%s@%s", sptr->user->username, GetHost(sptr));

	Ban *ban;

	if (!MyClient(sptr))
		return text; /* Remote server */

	for (ban = chptr->banlist; ban; ban = ban->next)
	{
		if (isColorBan(ban->banstr) && uhostMatches(ban->banstr, uhost)) {
			char *stripped = _StripColors(text);
			memset(text, 0, text_length);
			memcpy(text, stripped, strlen(stripped));
			break;
		}
	}

	return text;
}
