/* $Id: automode.c 389 2006-09-12 14:51:32Z tsaviran $
 * -------------------------------------------------------
 * Copyright (C) 2002-2006 Tommi Saviranta <wnd@iki.fi>
 * -------------------------------------------------------
 * 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */



#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* ifdef HAVE_CONFIG_H */

#ifdef AUTOMODE

#include "automode.h"
#include "common.h"
#include "llist.h"
#include "perm.h"
#include "irc.h"
#include "miau.h"
/* vsnprintf */
#include "tools.h"

#include <string.h>

#if HAVE_STRINGS_H
#include <strings.h>
#endif



permlist_type	automodelist;



static inline void
automode_drop(automode_type *line, llist_node *node, llist_list *list)
{
	xfree(line->nick);
	xfree(node->data);
	llist_delete(node, list);
} /* static inline void automode_drop(automode_type *line, llist_node *node,
		llist_list *list) */



/*
 * Process mode queues.
 */
void
automode_do(void)
{
	channel_type *channel;
	char modes[4];
	char *nicks;
	size_t size;

	size = 1;
	nicks = (char *) xmalloc(size);

	LLIST_WALK_H(active_channels.head, channel_type *);
		channel = data;

		if (channel->oper == 1) {
			int count;
			size_t nlen, tlen;

			nicks[0] = '\0'; /* clear nicks */
			tlen = 1;
			memset(modes, 0, sizeof(modes)); /* clear modes */
			count = 0;
		
			/* Commit three modes at a time. */
			LLIST_WALK_H(channel->mode_queue.head, automode_type *);
				modes[count] = data->mode;
				/* paranoid */
				nlen = strlen(data->nick);
				tlen += nlen + 1;
				if (tlen > size) {
					size = tlen;
					nicks = (char *) xrealloc(nicks, size);
				}
				strcat(nicks, " ");
				strncat(nicks, data->nick, nlen);
				count++;
				if (count == 3) {
					irc_write_head(&c_server,
							"MODE %s +%s%s",
							channel->name,
							modes,
							nicks);
					/* clear nicks and modes */
					nicks[0] = '\0';
					memset(modes, 0, 4);
					count = 0;
					tlen = 1;
				}
			LLIST_WALK_F; /* walk queue */

			/* Commit remaining modes. */
			if (count > 0) {
				irc_write_head(&c_server,
						"MODE %s +%s%s",
						channel->name,
						modes,
						nicks);
			}
		
			/* Clear mode-queue as there are all now processed. */
			automode_clear(&data->mode_queue);
		} /* if (channel->oper == 1) */
	LLIST_WALK_F;	/* walk channels */

	xfree(nicks);
} /* void automode_do(void) */



/*
 * Add nick to be auto-opped.
 */
void
automode_queue(const char *nick, const char *hostname, channel_type *channel)
{
	automode_type	*modeact;
	char		*mask;
	size_t		msize;

	char		modes[] = "ov";
	int		mode_c;

	/* termination and validity guaranteed */
	msize = strlen(nick) + strlen(hostname) + strlen(channel->name) + 5;
	mask = (char *) xmalloc(msize);

	for (mode_c = 0; mode_c < 2; mode_c++) { /* strlen(ov) - 1 */
		/* generate mask and see if any automode should take place */
		snprintf(mask, msize, "%c:%s!%s/%s",
				modes[mode_c], nick, hostname,
				channel->name);
		mask[msize - 1] = '\0';
		if (is_perm(&automodelist, mask) && automode_lookup(nick,
					channel, modes[mode_c]) == NULL) {
			modeact = (automode_type *)
				xmalloc(sizeof(automode_type));
			modeact->nick = xstrdup(nick);
			modeact->mode = modes[mode_c];
			llist_add_tail(llist_create(modeact),
					&channel->mode_queue);
			status.automodes++;
		}
	}
	
	xfree(mask);
} /* void automode_queue(const char *, const char *, channel_type *) */



void
automode_clear(llist_list *queue)
{
	LLIST_WALK_H(queue->head, automode_type *);
		automode_drop(data, node, queue);
		status.automodes--;
	LLIST_WALK_F;
} /* void automode_clear(llist_list *queue) */



/*
 * Drop automode action from automode -queue.
 *
 * If mode == NULL, don't care about mode.
 * If channel == NULL, don't care about channel.
 */
void
automode_drop_nick(const char *nick, const char mode)
{
	LLIST_WALK_H(active_channels.head, channel_type *);
		automode_drop_channel(data, nick, mode);
	LLIST_WALK_F;
} /* void automode_drop_nick(const char *, const char) */



void
automode_drop_channel(channel_type *channel, const char *nick, const char mode)
{
	int nick_ok, mode_ok;
	LLIST_WALK_H(channel->mode_queue.head, automode_type *);
		nick_ok = (nick == NULL) || (nick != NULL
				&& xstrcasecmp(nick, data->nick) == 0);
		mode_ok = (mode == '\0') || (mode == data->mode);
		if (nick_ok == 1 && mode_ok == 1) {
			automode_drop(data, node, &channel->mode_queue);
		}
	LLIST_WALK_F;
} /* void automode_drop_channel(channel_type *, const char *, const char) */



/*
 * Look up for an auto-op action in queue.
 *
 * Returns pointer to automode_type if found, otherwise NULL:
 */
llist_node *
automode_lookup(const char *nick, channel_type *channel, const char mode)
{
	llist_node	*ptr;
	automode_type	*modeact;

	for (ptr = channel->mode_queue.head; ptr != NULL; ptr = ptr->next) {
		modeact = (automode_type *) ptr->data;
		if (xstrcasecmp(nick, modeact->nick) == 0
				&& modeact->mode == mode) {
			return ptr;
		}
	}

	return NULL;
} /* llist_nost *automode_lookup(channel_type *, const char *, const char) */



#endif /* ifdef AUTOMODE */


syntax highlighted by Code2HTML, v. 0.9.1