/*
 * c_handlers.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 <string.h>
#include "bottypes.h"
#include "settings.h"
#include "globals.h"

void c_ban(int direction, char *channel, char *ban)
{
	char tmp2[512 + 10];
	char tmp[512 + 1];
	char *str;
	int len, i, l2;
	int ban_cnt = 0;
	struct chanopts *c;

	if (!channel || !strlen(channel))
		return;
	if (!ban || !strlen(ban))
		return;

	if (!direction) {
#if DEBUG2
		printf("Unbanning %s from %s.\n", ban, channel);
#endif
		c = findchannel(channel);
		/* make sure we not remove perm bans */
		delban_ch(c, ban, 0);
	} else {
#if DEBUG2
		printf("Banning %s from %s.\n", ban, channel);
#endif
		if (index(ban, ':') != NULL) {
			/* 
			   if there are any bans with ':' on the channel, then remove all of them. (including correct 
			   bans) */
			str = ban;
			i = 0;
			while (str[i]) {
				len = 0;
				while (str[i] && str[i] != ' ' && (i < 512)) {
					tmp[len++] = str[i++];
				}
				tmp[len] = '\0';
				if (len > 0) {
					l2 = sprintf(tmp2, "%c%c M %s -b ", yxx[0], yxx[1], channel);
					if (ban_cnt++ == 0) {
						/* add "::" to the first ban on the line */
						if (tmp[0] == ':')
							l2 += sprintf(&tmp2[l2], "::");
					} else {
						/* add ":" to other bans */
						if (tmp[0] == ':')
							l2 += sprintf(&tmp2[l2], ":");
					}
					/* 
					   There was a bug here if ban == dioadi2-3010!*;35$:#~%*@* l2 += sprintf(&tmp2[l2],
					   "%s\r\n", tmp); */
					strncpy(&tmp2[l2], tmp, len);
					l2 += len;
					tmp2[l2] = '\0';
					strcat(&tmp2[l2], "\r\n");
					l2 += 2;
					putserver1(tmp2);
				}
				if (!str[i] || (i >= 512))
					return;
				i++;
			}
		} else {
			c = findchannel(channel);
			addban_ch(c, ban, 0, 0);
#if 0
			if (c) {
				if (isbanned()) {
					kick_user_reason_banned(u, c);
				}
			}
#endif
		}
	}
	return;
}


void c_op_set_modes(int direction, char *channel, struct activeuser *u)
{
	struct chanmod *cm;
	if (!u) {
		log("ERROR: *** c_op_set_modes: u==NULL\n");
		return;
	}
	cm = u->chanmodes;
	while (cm) {
		if (!strcasecmp(cm->channel, channel)) {
			if (direction) {
				updatemodes(&(cm->mode), "+o");
			} else {
				updatemodes(&(cm->mode), "-o");
			}
		}
		cm = cm->next;
	}
	return;
}

int c_op_get_modes(char *channel, struct activeuser *u)
{
	struct chanmod *cm;
	if (!u) {
		log("c_op_get_modes: *** c_op_get_modes: u==NULL\n");
		return 0;
	}
	cm = u->chanmodes;
	while (cm) {
		if (!strcasecmp(cm->channel, channel)) {
			return cm->mode;
		}
		cm = cm->next;
	}
	return 0;
}


void c_op(int direction, char *channel, char *victim)
{
	struct chanopts *c;
	struct activeuser *u;
	if (!channel || !strlen(channel))
		return;
	if (!victim || !strlen(victim))
		return;

	c = findchannel(channel);
	u = find_yxx(victim);
	if (!u) {
		printf("c_op: *** couldn't find user [%s] [%s]\n", channel, victim);
		return;
	}

	if (c && (c->flags & CO_BITCH) && direction) {
		if (!(userlevelonchan_d(u->dbuser, channel) & (CHAN_OPER | CHAN_MASTER | CHAN_OWNER))) {
			char pyxx[YXXLEN + 1];
			strncpy(pyxx, u->yxx, YXXLEN);
			pyxx[YXXLEN] = '\0';
			putserver("%s M %s -o %s\r\n", yxx, channel, pyxx);
		}
	}

	if (c && (c->flags & CO_PROTECT) && (direction == 0)) {
		if (userlevelonchan_d(u->dbuser, channel) & (CHAN_OPER | CHAN_MASTER | CHAN_OWNER)) {
			char pyxx[YXXLEN + 1];
			strncpy(pyxx, u->yxx, YXXLEN);
			pyxx[YXXLEN] = '\0';
			putserver("%s M %s +o %s\r\n", yxx, channel, pyxx);
		}
	}
	c_op_set_modes(direction, channel, u);
	return;
}


void c_key(int direction, char *channel, char *key)
{
	struct chanopts *c;
	char newkey[KEYLEN + 1];
	if (!channel || !strlen(channel))
		return;

	c = findchannel(channel);
	if (!c)
		return;

	if (c->flags & (1 << ('k' - 'a'))) {
		/* key changed manually, restore it to the saved value */
		strncpy(newkey, c->key, KEYLEN);
		newkey[KEYLEN] = '\0';
#if DEBUG
		log("c_key: restore to %s %s\n", c->channel, newkey);
#endif
		addkey_ch(c, newkey, 1);
		return;
	}

	if (direction) {
	        if (!key) {
		  irclog(DEBUG_ALL, "c_key: key is NULL [%s]\n", c->channel);
		  return;
		}
	        if (!*key) {
		  irclog(DEBUG_ALL, "c_key: key is empty [%s]\n", c->channel);
		  return;
		}
		strncpy(newkey, key, KEYLEN);
		newkey[KEYLEN] = '\0';
		c->modes |= (1 << ('k' - 'a'));
		strncpy(c->key, newkey, KEYLEN);
		c->key[KEYLEN] = '\0';
		irclog(DEBUG_ALL, "c_key: set %s %s\n", c->channel, c->key);
	} else {
		irclog(DEBUG_ALL, "c_key: unset %s %s\n", c->channel, c->key);
		c->modes &= (~0 - (1 << ('k' - 'a')));
		c->key[0] = '\0';
	}
	return;
}

void update_keys_limits(void)
{
	struct chanopts *c;
	ListElem *el;
	el = GetHead(channel_list);
	while (el) {
		c = (struct chanopts *) el->data;
		if (c->flags & (1 << ('k' - 'a'))) {
			log("updatekeys %s %s\n", c->channel, c->key);
			if (strlen(c->key)) {
				addkey_ch(c, c->key, 1);
			} else {
				/* no key... then clear flag */
				c->flags &= (~0 - (1 << ('k' - 'a')));
			}
		}
		if (c->flags & CO_LIMIT) {
			log("updatelimits %s %s\n", c->channel, c->limit);
			if (strlen(c->limit)) {
				addlimit_ch(c, c->limit, 1);
			} else {
				/* no limit... then clear flag */
				c->flags &= (~0 - CO_LIMIT);
			}
		}
		/* 
		   I don't know if this is a good idea... Q will set the last used topic with this
		   construction... */
		if (c->flags & (1 << ('f' - 'a'))) {
#if DEBUG
			log("update_keys_limits: update topic %s [%s] [%s]\n", c->channel, c->topic, c->last_topic);
#endif
			if (strlen(c->topic)) {
				settopic(c, c->topic);
			}
		} else {
			if (c->flags & (1 << ('t' - 'a'))) {
				settopic(c, c->last_topic);
			}
		}
		el = GetSucc(el);
	}
	return;
}

/*
  not correct name for this function, but it's called from the parse() function
  and it will be changed later..
*/
void c_topic(char *buffer)
{
	int len;
	char *ptr, *p;
	struct chanopts *c = findchannel(args[0]);
	if (!c)
		return;

	/* If someone tried to set the topic, then change it back. */
	if (c->flags & CO_FORCE_TOPIC) {
		settopic(c, c->topic);
	} else {
		ptr = strstr(buffer, args[0]);
		if (ptr) {
			p = &ptr[strlen(args[0]) + 2];
			strncpy(c->last_topic, p, TOPICLEN);
			c->last_topic[TOPICLEN] = '\0';
			len = strlen(c->last_topic);
			if (c->last_topic[len - 1] == '\012' || c->last_topic[len - 1] == '\015') {
				len--;
				c->last_topic[len] = '\0';
			}
			if (c->last_topic[len - 1] == '\012' || c->last_topic[len - 1] == '\015') {
				len--;
				c->last_topic[len] = '\0';
			}
		}
	}
	return;
}


void c_voice_set_modes(int direction, char *channel, struct activeuser *u)
{
	struct chanmod *cm = u->chanmodes;
	while (cm) {
		if (!strcasecmp(cm->channel, channel)) {
			if (direction) {
				updatemodes(&(cm->mode), "+v");
			} else {
				updatemodes(&(cm->mode), "-v");
			}
		}
		cm = cm->next;
	}
	return;
}



/*
 * Just the same as oper really.
 */
void c_voice(int direction, char *channel, char *victim)
{
	struct chanopts *c;
	struct activeuser *u;
	if (!channel || !strlen(channel))
		return;
	if (!victim || !strlen(victim))
		return;

	c = findchannel(channel);
	u = find_yxx(victim);
	if (!u) {
#if DEBUG2
		log("ERROR: c_voice couldn't find user [%s]\n", victim);
#endif
		return;
	}
	if (c && (c->flags & CO_BITCH) && (direction == 1)) {
		if (!(userlevelonchan_d(u->dbuser, channel) & CHAN_VOICE)) {
			char pyxx[YXXLEN + 1];
			strncpy(pyxx, u->yxx, YXXLEN);
			pyxx[YXXLEN] = '\0';
			putserver("%s M %s -v %s\r\n", yxx, channel, pyxx);
		}
	}

	if (c && (c->flags & CO_PROTECT) && (direction == 0)) {
		if (userlevelonchan_d(u->dbuser, channel) & CHAN_VOICE) {
			char pyxx[YXXLEN + 1];
			strncpy(pyxx, u->yxx, YXXLEN);
			pyxx[YXXLEN] = '\0';
			putserver("%s M %s +v %s\r\n", yxx, channel, pyxx);
		}
	}
	c_voice_set_modes(direction, channel, u);
	return;
}


void c_limit(int direction, char *channel, char *limit)
{
	struct chanopts *c;
	char newlimit[LIMITLEN + 1];
	if (!channel || !channel[0])
		return;

	c = findchannel(channel);
	if (!c)
		return;

	if (c->flags & CO_LIMIT) {
		/* key changed manually, restore it to the saved value */
		strncpy(newlimit, c->limit, LIMITLEN);
		newlimit[LIMITLEN] = '\0';
		irclog(DEBUG_ALL, "c_limit: restore to %s %s\n", c->channel, newlimit);
		addlimit_ch(c, newlimit, 1);
		return;
	}

	if (direction) {
	        if (!limit) {
		  irclog(DEBUG_ALL, "c_limit: limit is NULL [%s]\n", c->channel);
		  return;
		}
	        if (!*limit) {
		  irclog(DEBUG_ALL, "c_limit: limit is empty [%s]\n", c->channel);
		  return;
		}
		strncpy(newlimit, limit, LIMITLEN);
		newlimit[LIMITLEN] = '\0';
		c->modes |= (1 << ('l' - 'a'));
		strncpy(c->limit, newlimit, LIMITLEN);
		c->limit[LIMITLEN] = '\0';
		strcpy(c->cur_limit, c->limit);
		irclog(DEBUG_ALL, "c_limit: set %s %s\n", c->channel, c->limit);
	} else {
		irclog(DEBUG_ALL, "c_limit: unset %s\n", c->channel);
		c->modes &= (~0 - (1 << ('l' - 'a')));
		c->limit[0] = '\0';
		c->cur_limit[0] = '\0';
	}
	return;
}

void c_opertopic(int direction, char *channel)
{
	struct chanopts *c;
	if (!channel || !strlen(channel))
		return;

	c = findchannel(channel);
	if (!c)
		return;

	if (direction) {
		c->modes |= (1 << ('t' - 'a'));
	} else {
		c->modes &= (~0 - (1 << ('t' - 'a')));
	}
	return;
}

void c_nomsg(int direction, char *channel)
{
	struct chanopts *c;
	if (!channel || !strlen(channel))
		return;

	c = findchannel(channel);
	if (!c)
		return;

	if (direction)
		c->modes |= (1 << ('n' - 'a'));
	else
		c->modes &= (~0 - (1 << ('n' - 'a')));
	return;
}

void c_moderate(int direction, char *channel)
{
	struct chanopts *c;
	if (!channel || !strlen(channel))
		return;

	c = findchannel(channel);
	if (!c)
		return;

	if (direction)
		c->modes |= (1 << ('m' - 'a'));
	else
		c->modes &= (~0 - (1 << ('m' - 'a')));
	return;
}

void c_secret(int direction, char *channel)
{
	struct chanopts *c;
	if (!channel || !strlen(channel))
		return;

	c = findchannel(channel);
	if (!c)
		return;

	if (direction)
		c->modes |= (1 << ('s' - 'a'));
	else
		c->modes &= (~0 - (1 << ('s' - 'a')));
	return;
}

void c_invite(int direction, char *channel)
{
	struct chanopts *c;
	if (!channel || !strlen(channel))
		return;

	c = findchannel(channel);
	if (!c)
		return;

	if (direction)
		c->modes |= (1 << ('i' - 'a'));
	else
		c->modes &= (~0 - (1 << ('i' - 'a')));
	return;
}

void c_protect(int direction, char *channel)
{
	struct chanopts *c;
	if (!channel || !strlen(channel))
		return;

	c = findchannel(channel);
	if (!c)
		return;

	if (direction)
		c->modes |= (1 << ('p' - 'a'));
	else
		c->modes &= (~0 - (1 << ('p' - 'a')));
	return;
}
