/*
 * ban.c
 * (C) James Craig 1998, Email: jcraig@mad.scientist.com
 * (C) Christian Magnusson 1999, Email: d1mag@dtek.chalmers.se
 * This program is copyright, and covered by the Gnu Public License.
 * The Q bot.
 */
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#include "settings.h"
#include "bottypes.h"
#include "globals.h"

int addban_ch(struct chanopts *c, char *banstring, int placed, int kick)
{
	struct ban *b, *bp;
	struct chanmod *cm, *cm2;
	struct activeuser *u;
	ListElem *el;
	char ban_mask[BANLEN + 1];
	int already_exist = 0;
	int len, i;

	if (c) {
		b = (struct ban *) Malloc(sizeof(struct ban));
		if (!b)
			return 0;
#if DEBUG
		/* printf("addban_ch: [%s] [%s]\n", c->channel, banstring); */
#endif
		strncpy(b->ban, banstring, BANLEN);
		b->ban[BANLEN] = '\0';
		b->placedby = placed;
		/* hope the banstring is correct */
		if (!split_banstylename(banstring, b->nick, b->user, b->host)) {
			Free(b);
			return 0;
		}
		/* convert it to correct format ! */
		strncpy(ban_mask, banstylename(b->nick, b->user, b->host), BANLEN);
		ban_mask[BANLEN] = '\0';
		/* overwrite the old ban string with the checked one.. */
		strncpy(b->ban, ban_mask, BANLEN);
		b->ban[BANLEN] = '\0';

#if 1
		len = strlen(b->ban);
		for (i = 0; i < len; i++) {
			if (!isprint(b->ban[i])) {
				log("ERROR: addban_ch BAD BAN %s %s\n", c->channel, banstring);
				Free(b);
				return 0;
			}
		}
#endif

		bp = c->bans;
		already_exist = 0;
		while (bp) {
			if (!strcasecmp(bp->ban, b->ban)) {
				already_exist = 1;
			}
			bp = bp->next;
		}
		if (already_exist) {
			Free(b);
			return 0;
		} else {
			b->next = c->bans;
			c->bans = b;
		}
		if (kick) {
			/* now kick all matching users at that channel */
			putserver("%s M %s +b %s\r\n", yxx, c->channel, ban_mask);

			el = GetHead(auser_list);
			while (el) {
				u = (struct activeuser *) el->data;
				cm = u->chanmodes;
				while (cm) {
					cm2 = cm->next;
					if (!strcasecmp(cm->channel, c->channel)) {
						/* user is at that channel */
						if (matches_ban(u, b)) {
							log("kick [%s]\n", u->nick);
							kick_user_reason_banned(u, c);
						}
					}
					cm = cm2;
				}
				el = GetSucc(el);
			}
		}
	} else {
#if DEBUG
		/* printf("Error: Couldn't find channel in addban_ch.\n"); */
#endif
		return 0;
	}
	return 1;
}

void delban_ch(struct chanopts *c, char *banstring, int permanent_too)
{
	struct ban *b, *ob;
	if (c) {
		ob = NULL;
		b = c->bans;
		if (!b)
			return;
		while (b) {
			if (!strcasecmp(banstring, b->ban))
				break;
			ob = b;
			b = b->next;
		}
		if (!b) {
#if DEBUG
			printf("Error: Tried to remove a ban that wasn't there.");
#endif
			return;
		}
		if (b->placedby && !permanent_too) {
			/* we don't want to remove permanent bans here */
			return;
		}
		if (ob == NULL) {
			c->bans = b->next;
		} else {
			ob->next = b->next;
		}
		Free(b);
#if DEBUG
		/* printf("delban_ch: [%s] [%s]\n", c->channel, banstring); */
#endif
	} else {
#if DEBUG
		/* printf("Error: Couldn't find channel in delban.\n");  */
#endif
	}
	return;
}

void updatebans(struct chanopts *c)
{
	struct ban *b;
	if (!c)
		return;
	return;												/* never let Q put any bans */

	b = c->bans;
	while (b) {
		putserver("%s M %s +b %s\n", yxx, c->channel, b->ban);
		b = b->next;
	}
	return;
}

int matches_ban(struct activeuser *u, struct ban *b)
{
	if (match(b->nick, u->nick)) {
		return 0;
	}
	if (match(b->user, u->username)) {
		return 0;
	}
	if (match(b->host, u->hostname)) {
		return 0;
	}
	return 1;
}

struct ban *isbanned(struct activeuser *u, struct chanopts *c)
{
	struct ban *b;
	if (!c || !u)
		return NULL;
	b = c->bans;
	while (b) {
		if (matches_ban(u, b)) {
			return b;
		}
		b = b->next;
	}
	return NULL;
}

char banstylename_buffer[BANLEN + 1];
char *banstylename(char *nick, char *user, char *host)
{
	banstylename_buffer[0] = 0;
	strcat(banstylename_buffer, nick);
	strcat(banstylename_buffer, "!");
	strcat(banstylename_buffer, user);
	strcat(banstylename_buffer, "@");
	strcat(banstylename_buffer, host);
	return banstylename_buffer;
}

int split_banstylename(char *ban, char *nick, char *user, char *host)
{
	char tmp[BANLEN + 1];
	char *st, *st2;
	int pos = 0;
	if (!ban)
		return 0;
	if (!nick || !user || !host)
		return 0;

	/* set wildcard as default */
	strcpy(nick, "*");
	strcpy(user, "*");
	strcpy(host, "*");

	strncpy(tmp, ban, BANLEN);
	tmp[BANLEN] = '\0';
	st = strtok(tmp, "!@");
	if (st) {
		pos += strlen(st);
		if (ban[pos] == '@') {
			/* @ was the first delemitter */
			if (st[0]) {
				/* in case   @bla.com */
				strncpy(user, st, USERLEN);
				user[USERLEN] = '\0';
			}
			pos++;
			st = &tmp[pos];
			if (*st) {
				/* only host should remain now */
				st = strtok(NULL, "!@");
				if (st) {
					/* 2 @ OR (! after @) would be bad */
					pos += strlen(st);
					if (ban[pos] == '@' || ban[pos] == '!') {
						return 0;
					}
					if (st[0]) {
						strncpy(host, st, HOSTLEN);
						host[HOSTLEN] = '\0';
					}
				}
				return 1;
			} else {
				if (st[0]) {
					strncpy(host, st, HOSTLEN);
					host[HOSTLEN] = '\0';
				}
			}
			return 1;
		}

		/* ! was the first delemitter */
		if (st[0]) {
			strncpy(nick, st, NICKLEN);
			nick[NICKLEN] = '\0';
		}
		pos++;
		if (ban[pos]) {
			st = strtok(NULL, "!@");
			if (st) {
				pos += strlen(st);
				if (ban[pos] == '!') {
					return 0;
				}
				if (st[0]) {
					strncpy(user, st, USERLEN);
					user[USERLEN] = '\0';
				}
				pos++;
				st = strtok(NULL, "!@");
				if (st) {
					st2 = strtok(NULL, "!@");
					if (!st2) {
						if (tmp[pos]) {
							strncpy(host, &tmp[pos], HOSTLEN);
							host[HOSTLEN] = '\0';
						}
					} else {
						return 0;
					}
				} else {
					return 1;
				}
			} else {
				return 1;
			}
			return 1;
		} else {
			/* no user or host was given, just nick! */
		}
		return 1;
	} else {
		/* never happens, since strtok always return a pointer first time */
		return 0;
	}
	return 1;
}
