/******************************************************************************
 * This file is part of a software distribution, which is furnished under the *
 * terms of a license.  Use of this software  by any means is subject to this *
 * license  and  signifies  the  acceptance of  the  licensing  terms  stated *
 * therein. Please see  the file LICENSE in the  top-level directory  of this *
 * software  distribution  for detailed copyright  disclaimers  and licensing *
 * terms.                                                                     *
 ******************************************************************************
 * Copryight (c) by Andreas S. Wetzel - All rights reserved.                  *
 ******************************************************************************/

/* $Id: commands.c,v 1.5 2002/06/23 23:29:37 mickey Exp $ */

#include <vchat.h>
#include <proto_common.h>

#include <netdb.h>

#if HAVE_SYS_UIO_H
#  include <sys/uio.h>
#endif

#include <ctype.h>
#include <arpa/inet.h>

/*** Externals ***/

extern VCONN sconn;
extern VP vp;
extern ED *ed;

extern int uchannel;

extern int	input_line(char *prompt, size_t maxlen, u_short flags);
extern void	vquit(char *fmt, ...);

/*** Code ***/

void aclcmd(char *dat)
{
	VACL		acl;
	char		*cmdargv[4];
	char		**aptr;
	char		*mask;
	char		*s;
	char		cmd;
	char		act;

	int		i;
	int		nextarg	= 0;
	int		cmdargc	= 0;
	int		mask_bits;

	u_short		rule;

	struct in_addr	address;
	struct in_addr	netmask;

	u_int32_t	ttl;

	/*
	 * If no arguments are given, request ACL command.
	 */

	if(dat != NULL)
	{
		while(isspace((int)*dat))
			++dat;
	}

	if(dat == NULL || *dat == '\0')
	{
		char prompt[64];

		sprintf(prompt, "%sACL command%s%s:%s ",
			S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 64, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		dat = ed->buffer;
	}

	/*
	 * Split input line into argument vector
	 */

#if HAVE_STRSEP
	for(aptr = cmdargv; (*aptr = (char *) strsep(&dat, " \t")) != NULL && cmdargc < 5;)
	{
		if(**aptr != '\0')
		{
			++aptr;
			++cmdargc;
		}
	}
#else
	aptr = cmdargv;
	*aptr = (char *)strtok(dat, " \t");

	while(*aptr != NULL && cmdargc < 4)
	{
		++aptr;
		++cmdargc;

		*aptr = (char *)strtok(NULL, " \t");
	}
#endif

	/*
	 * No arguments - no action
	 */

	if(cmdargc <= 0)
	{
		cprintf(chat, "* No ACL command specified - type '.A ?' for a list of ACL commands");
		return;
	}

	/*
	 * Process the arguments contained in the command vector.
	 * The first argument is either one of the commands "add",
	 * "delete" or "flush". Otherwise the "add" command is
	 * assumed, and the first argument to be a valid rule number.
	 */

	str_lower(cmdargv[0]);

	if(!strcmp(cmdargv[0], "add"))
	{
		cmd = 'a';
		nextarg = 1;
	}
	else if(!strcmp(cmdargv[0], "delete"))
	{
		cmd = 'd';
		nextarg = 1;
	}
	else if(!strcmp(cmdargv[0], "flush"))
	{
		cmd = 'f';
		nextarg = 1;
	}
	else if(!strcmp(cmdargv[0], "list"))
	{
		cmd = 'l';
		nextarg = 1;
	}
	else if(!strcmp(cmdargv[0], "help") || !strcmp(cmdargv[0], "?"))
	{
		/*
		 * Display ACL help
		 */

		cprintf(chat, "\n* %sV%sChat%s -- Interactive TCP/IP conference system -- %sACL COMMAND HELP%s:",
			S_FG_MAG, S_FG_YEL, S_OFF, S_FG_CYN, S_OFF);

		cprintf(chat, "*\n* ACL commands modify the contents of the servers ACL database and");
		cprintf(chat, "* may be used by users with the %sACL%s permission only.", S_BOLD, S_OFF);

		cprintf(chat, "*\n*  %sAvailable ACL commands are:%s\n*",
			S_UL, S_OFF);

		cprintf(chat, "*  %s.A%s [add] <rule> <action> <address> [ttl] - add an ACL rule to the database", S_FG_GRN, S_OFF);
		cprintf(chat, "*  %s.A%s delete <rule>                         - delete ACL rule from database", S_FG_GRN, S_OFF);
		cprintf(chat, "*  %s.A%s flush                                 - flush all rules in ACL database", S_FG_GRN, S_OFF);
		cprintf(chat, "*  %s.A%s list                                  - list all rules in ACL database\n*", S_FG_GRN, S_OFF);
		cprintf(chat, "*  %sWhere:%s\n*", S_UL, S_OFF);
		cprintf(chat, "*  %s<rule>%s     - a rule number in the range 0..65534", S_BOLD, S_OFF);
		cprintf(chat, "*  %s<action>%s   - action to be taken by this rule (accept|reject)", S_BOLD, S_OFF);
		cprintf(chat, "*  %s<address>%s  - the internet name or address of a host or subnet", S_BOLD, S_OFF);
		cprintf(chat, "*               subnets are specified with a '/' followed by the netmask");
		cprintf(chat, "*               or number of netmask bits appended to the address i.e.:\n*");
		cprintf(chat, "*                    %s127.0.0.0/8%s", S_BOLD, S_OFF);
		cprintf(chat, "*                    %s10.0.0.0/255.0.0.0%s\n*", S_BOLD, S_OFF);
		cprintf(chat, "*  %s[ttl]%s      - optional time to live specification in seconds", S_BOLD, S_OFF);

		return;
	}
	else
	{
		cmd = 'a';
		nextarg = 0;

		i = strtol(cmdargv[nextarg], &s, 0);

		if(*cmdargv[nextarg] == '\0' || *s != '\0')
		{
			cprintf(chat, "* Unknown ACL command '%s' - type '.A ?' for a list of ACL commands", cmdargv[nextarg]);
			return;
		}
	}

	/*
	 * Check for correct number of arguments
	 */

	switch(cmd)
	{
		case 'a':	if((cmdargc - nextarg) != 3 && (cmdargc - nextarg) != 4)
				{
					cprintf(chat, "* The ACL 'add' command expects 3 or 4 arguments, not %d", (cmdargc - nextarg));
					return;
				}
				break;

		case 'd':	if((cmdargc - nextarg) != 1)
				{
					cprintf(chat, "* The ACL 'delete' command expects 1 argument, not %d", (cmdargc - nextarg));
					return;
				}
				break;

		case 'f':	if((cmdargc - nextarg) != 0)
				{
					cprintf(chat, "* The ACL 'flush' command expects no arguments");
					return;
				}

				/*
				 * SEND ACL FLUSH COMMAND!
				 */

				snd_serv(CMD_ACL_FLUSH, NULL, 0);

				return;

		case 'l':	if((cmdargc - nextarg) != 0)
				{
					cprintf(chat, "* The ACL 'list' command expects no arguments");
					return;
				}

				/*
				 * SEND ACL LIST COMMAND!
				 */

				snd_serv(CMD_ACL_LIST, NULL, 0);

				return;
	}

	/*
	 * For the "add" and delete commands, parse the rule number
	 */

	i = strtol(cmdargv[nextarg], &s, 0);

	if(*cmdargv[nextarg] == '\0' || *s != '\0')
	{
		cprintf(chat, "* Numerical value expected in field %d of ACL command", nextarg + 1);
		return;
	}

	if(i < 0 || i > 65534)
	{
		cprintf(chat, "* Rule number out of range - use a value between 0 and 65534");
		return;
	}

	rule = (u_short) i;

	++nextarg;

	/*
	 * Delete command needs no further args, so do it now.
	 */

	if(cmd == 'd')
	{
		u_short	rlnum = htons(rule);

		/*
		 * SEND ACL DELETE COMMAND
		 */

		snd_serv(CMD_ACL_DELETE, (u_char *)&rlnum, sizeof(u_short));

		return;
	}

	/*
	 * Next argument for an "add" command is the action to be taken
	 */

	str_lower(cmdargv[nextarg]);

	if(!strcmp(cmdargv[nextarg], "allow")
	|| !strcmp(cmdargv[nextarg], "pass")
	|| !strcmp(cmdargv[nextarg], "accept")
	|| !strcmp(cmdargv[nextarg], "grant"))
	{
		act = 'a';	/*** ACCEPT ***/
	}
	else if(!strcmp(cmdargv[nextarg], "deny")
	|| !strcmp(cmdargv[nextarg], "reject")
	|| !strcmp(cmdargv[nextarg], "refuse")
	|| !strcmp(cmdargv[nextarg], "intercept")
	|| !strcmp(cmdargv[nextarg], "void"))
	{
		act = 'r';	/*** REJECT ***/
	}
	else
	{
		cprintf(chat, "* Syntax error - '%s' unexpected", cmdargv[nextarg]);
		return;
	}

	++nextarg;

	/*
	 * The next argument to the "add" command is an address/netmask pair
	 * or the word "any" or "all"
	 */

	str_lower(cmdargv[nextarg]);

	if(!strcmp(cmdargv[nextarg], "any") || !strcmp(cmdargv[nextarg], "all"))
	{
		address.s_addr = INADDR_ANY;
		netmask.s_addr = INADDR_ANY;
		mask_bits = 0;
	}
	else if((mask = (char *)strchr(cmdargv[nextarg], '/')) != NULL)
	{
		*mask++ = '\0';

		address.s_addr = inet_addr(cmdargv[nextarg]);

		if(address.s_addr == INADDR_NONE)
		{
			/*
			 * MALFORMED ADDRESS
			 * See if DNS lookup succeeds
			 */

			struct hostent *ht;

			if((ht = gethostbyname(cmdargv[nextarg])) == NULL)
			{
				cprintf(chat, "* No address associated with '%s'", cmdargv[nextarg]);
				return;
			}

			BCOPY(ht->h_addr, &address, sizeof(address));
		}

		if((s = (char *)strchr(mask, '.')) != NULL)
		{
			netmask.s_addr = inet_addr(mask);
			mask_bits = netmask_bits(netmask);
		}
		else
		{
			mask_bits = atoi(mask);
			netmask_create(mask_bits, &netmask);
		}
	}
	else
	{
		if((address.s_addr = inet_addr(cmdargv[nextarg])) == INADDR_NONE)
		{
			/*
			 * MALFORMED ADDRESS
			 * See if DNS lookup succeeds on the argument
			 */

			struct hostent *ht;

			if((ht = gethostbyname(cmdargv[nextarg])) == NULL)
			{
				cprintf(chat, "* No address associated with '%s'", cmdargv[nextarg]);
				return;
			}

			BCOPY(ht->h_addr, &address, sizeof(address));
		}

		netmask.s_addr = htonl(0xffffffff);
		mask_bits = netmask_bits(netmask);
	}

	++nextarg;

	/*
	 * Look for optional TTL argument to ADD command.
	 */

	ttl = (cmdargc > nextarg) ? strtol(cmdargv[nextarg], NULL, 0) : 0;

	/*
	 * SEND ACL ADD COMMAND
	 */

	acl.address = address;
	acl.netmask = netmask;
	acl.action = htons((act == 'a') ? ACL_ACT_ACCEPT : ACL_ACT_REJECT);
	acl.rule = htons(rule);
	acl.ttl	= htonl(ttl);

	snd_serv(CMD_ACL_ADD, (u_char *)&acl, sizeof(VACL));
}

void action(char *dat)
{
	char *arg = dat;

	if(arg == NULL || *arg == '\0')
	{
		char prompt[64];

		sprintf(prompt, "%sAction message%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		arg = ed->buffer;
	}

	snd_serv(CMD_ACTION, arg, strlen(arg));
}

void addnick(char *dat)
{
	u_char	snd_buf[VPNICKSIZE+VPPASSWDSIZE+2];
	u_char	nick_buf[VPNICKSIZE+1];
	u_char	pwd_buf[VPPASSWDSIZE+1];
	u_char	prompt[64];
	size_t	sndlen;

	/*
	 * Request nickname if not already given on the commandline
	 */

	BZERO(nick_buf, VPNICKSIZE+1);

	if(dat == NULL || *dat == '\0')
	{
		sprintf(prompt, "%sAdd nickname%s%s:%s ",
			S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, VPNICKSIZE, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		strncpy(nick_buf, ed->buffer, VPNICKSIZE);
	}
	else
	{
		if(strlen(dat) > VPNICKSIZE)
		{
			cprintf(chat, "* Please choose a nickname with at most %d characters",
				VPNICKSIZE);
			return;
		}

		strncpy(nick_buf, dat, VPNICKSIZE);
	}

	/*
	 * Request password for new account
	 */

	BZERO(pwd_buf, VPPASSWDSIZE+1);

	sprintf(prompt, "%sPassword%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

	if(!input_line(prompt, VPPASSWDSIZE, (ED_RST_CTX | ED_NO_ECHO)))
	{
		cprintf(chat, "* Command aborted");
		clear_input();
		return;
	}

	clear_input();

	strncpy(pwd_buf, ed->buffer, VPPASSWDSIZE);

	sprintf(prompt, "%sVerification%s%s:%s ",
		S_BOLD, S_OFF, S_BLINK, S_OFF);

	input_line(prompt, VPPASSWDSIZE, (ED_RST_CTX | ED_NO_ECHO));

	clear_input();

	if(strncmp(pwd_buf, ed->buffer, VPPASSWDSIZE))
	{
		cprintf(chat, "* Verification failed");
		return;
	}

	clear_ed(ed);

	/*
	 * Send the ADDNICK command to the server
	 */

	sprintf(snd_buf, "%s %s", nick_buf, pwd_buf);
	sndlen = strlen(snd_buf);

	BZERO(pwd_buf, VPPASSWDSIZE+1);

	snd_serv(CMD_ADDNICK, snd_buf, sndlen);

	BZERO(snd_buf, (VPNICKSIZE+VPPASSWDSIZE+2));
}

void broadcast(char *dat)
{
	char	*arg = dat;

	if(dat == NULL || *dat == '\0')
	{
		char	prompt[64];

		sprintf(prompt, "%sBroadcast message%s%s:%s ",
			S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		arg = ed->buffer;
	}

	snd_serv(CMD_BROADCAST, arg, strlen(arg));
}

void chchan(char *chan)
{
	char *arg = chan;
	signed long channel;

	if(arg == NULL || *arg == '\0')
	{
		char prompt[64];

		sprintf(prompt, "%sEnter channel%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 16, ED_RST_CTX))
		{
			cprintf(chat, "* Command aborted\n");
			clear_input();
			return;
		}

		clear_input();

		arg = ed->buffer;
	}

	channel = htonl(strtol(arg, NULL, 0));

	snd_serv(CMD_CHCHAN, (char *)&channel, sizeof(long));
}

void clientopts(char *dat)
{
	GSOPT		opt;
	char		*giveargv[32];
	char		*takeargv[32];
	u_char		giveargc	= 0;
	u_char		takeargc	= 0;
	u_int32_t	givemask	= 0;
	u_int32_t	takemask	= 0;
	u_int32_t	md;
	char		*arg		= dat;
	register int	i;

#if HAVE_SYS_UIO_H
	struct iovec	iov[4];
	VMSG		vmsg;
#else
	u_char		*sndbuf;
	VMSG		*vmsg;
#endif

	/*
	 * Parse client option operators with leading '-' or '+' character
	 * into takeargv[] and giveargv[] respectively.
	 */

	while(*arg != '\0')
	{
		if(*arg == '+')
		{
			*arg++ = '\0';

			if(*arg == '\0' || *arg == ' ' || *arg == '\t')
			{
				cprintf(chat, "* Missing option name for '+' operator");
				return;
			}

			giveargv[giveargc++] = arg;

			while(*arg != '+' && *arg != '-' && *arg != ' '
				&& *arg != '\t' && *arg != '\0')
			{
				++arg;
			}
		}
		else if(*arg == '-')
		{
			*arg++ = '\0';

			if(*arg == '\0' || *arg == ' ' || *arg == '\t')
			{
				cprintf(chat, "* Missing option name for '-' operator");
				return;
			}

			takeargv[takeargc++] = arg;

			while(*arg != '+' && *arg != '-' && *arg != ' '
				&& *arg != '\t' && *arg != '\0')
			{
				++arg;
			}
		}
		else if(*arg == ' ' || *arg == '\t')
		{
			*arg++ = '\0';
		}
		else
		{
			cprintf(chat, "* Invalid statement '%s' encountered",
				arg);
			return;
		}
	}

	/*
	 * Create givemask and takemask corresponding to the
	 * option names specified in giveargv[] and takeargv[]
	 */

	for(i = 0; i < giveargc; i++)
	{
		if(clopt_mask(giveargv[i], &md) == -1)
		{
			cprintf(chat, "* Unknown client option name '%s'", giveargv[i]);
			return;
		}

		givemask |= md;
	}

	for(i = 0; i < takeargc; i++)
	{
		if(clopt_mask(takeargv[i], &md) == -1)
		{
			cprintf(chat, "* Unknown client option name '%s'", takeargv[i]);
			return;
		}

		takemask |= md;
	}

	/*
	 * Check for options that will compensate each other
	 */

	md = (givemask ^ takemask);
	givemask &= md;
	takemask &= md;

	/*
	 * Fill in GSOPT structure
	 */

	opt.givemask = htonl(givemask);
	opt.takemask = htonl(takemask);
	opt.control = 0;

#if HAVE_SYS_UIO_H

	/*
	 * Prepare scatter/gather array
	 */

	vmsg.cmd = htonl(CMD_CLOPT);
	vmsg.len = htonl(sizeof(GSOPT));

	iov[0].iov_base = (char *)&vmsg;
	iov[0].iov_len = sizeof(vmsg);
	iov[1].iov_base = (char *)&opt;
	iov[1].iov_len = sizeof(GSOPT);

	/*
	 * Send command
	 */

	writev(sconn.fd, (struct iovec *)&iov, 2);
#else

	/*
	 * Prepare send buffer
	 */

	md = (sizeof(VMSG) + sizeof(GSOPT));

	if((sndbuf = (u_char *)malloc(md + 1)) == NULL)
	{
		cprintf(chat, "* Memory allocation error (%s%s%s)",
			S_FG_RED, strerror(errno), S_OFF);
		return;
	}

	vmsg = (VMSG *)sndbuf;

	vmsg->cmd = htonl(CMD_CLOPT);
	vmsg->len = htonl(md - sizeof(VMSG));

	BCOPY(&opt, (u_char *)(sndbuf + sizeof(VMSG)), sizeof(GSOPT));

	/*
	 * Send command
	 */

	write(sconn.fd, sndbuf, md);

	free(sndbuf);
#endif
}

void fixchan(char *dat)
{
	char	*tp;

	if(dat == NULL || *dat == '\0')
	{
		cprintf(chat, "* Command aborted");
		return;
	}

	if((tp = (char *)strchr(dat, ':')) != NULL)
	{
		size_t len;

		/*
		 * Create a fixed channel
		 */

		*tp++ = '\0';

		if((len = strlen(tp)) <= 0)
		{
			cprintf(chat, "* No topic specified");
		}
		else
		{
			u_char		sndbuf[64];
			int32_t		*lp		= (int32_t *)&sndbuf;
			u_char		*topic		= &sndbuf[sizeof(signed long)];

			BZERO(sndbuf, sizeof(sndbuf));

			*lp = htonl(strtol(dat, NULL, 0));

			strncpy(topic, tp, VPTOPICSIZE);

			snd_serv(CMD_FIXCHAN, sndbuf, len + sizeof(int32_t));
		}
	}
	else
	{
		int32_t	chan	= htonl(strtol(dat, NULL, 0));

		snd_serv(CMD_FIXCHAN, (void *)&chan, sizeof(chan));
	}
}

void lastlog(char *dat)
{
	char		*arg;
	char		*nicklist;
	u_int32_t	num;
	u_int32_t	*sndnum;
	char		*sndlst;
	size_t		size;

	if(dat == NULL || *dat == '\0')
	{
		char prompt[64];

		sprintf(prompt, "%sList how many entries%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		num = htonl(strtol(ed->buffer, NULL, 0));

		sprintf(prompt, "%sShow which nickname(s) [blank=all]%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
			nicklist = "";
		else
			nicklist = ed->buffer;
	}
	else if((arg = (char *)strchr(dat, ' ')) != NULL)
	{
		*arg++ = '\0';

		num = htonl(strtoul(dat, NULL, 0));
		nicklist = arg;
	}
	else
	{
		char prompt[64];

		num = htonl(strtoul(dat, NULL, 0));

		sprintf(prompt, "%sShow which nickname(s) [blank=all]%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
			nicklist = "";
		else
			nicklist = ed->buffer;
	}

	/*
	 * Construct request
	 */

	size = (sizeof(num) + strlen(nicklist) + 1);

	if((arg = calloc(1, size)) == NULL)
	{
		cprintf(chat, "* Memory allocation error - cannot send message");
		return;
	}

	sndnum = (u_int32_t *)arg;
	sndlst = (char *)(arg + sizeof(num));

	*sndnum = num;
	strcpy(sndlst, nicklist);

	/*
 	 * Send request
	 */

	snd_serv(CMD_LASTLOG, arg, size);

	free(arg);
}

void modify_permissions(char *dat)
{
	char		*giveargv[32];
	char		*takeargv[32];
	u_char		giveargc	= 0;
	u_char		takeargc	= 0;
	u_int32_t	givemask	= 0;
	u_int32_t	takemask	= 0;
	u_int32_t	md;
	u_char		permset		= 'a';
	char		*arg		= dat;
	char		*nicklist;
	register int	i;

#if HAVE_SYS_UIO_H
	struct iovec	iov[4];
	VMSG		vmsg;
#else
	u_char		*sndbuf;
	VMSG		*vmsg;
#endif

	/*
	 * See which permission set is to be modified (access|modify)
	 */

	if(dat == NULL || *dat == '\0')
	{
		char	prompt[64];

		sprintf(prompt, "%sModify command (? for help)%s%s:%s ",
			S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		arg = ed->buffer;
	}

	switch(tolower(*arg))
	{
		case '\0':	cprintf(chat, "* Permission set specifier missing");
				return;
		case 'a':	permset = 'a';
				break;
		case 'm':	permset = 'm';
				break;

		case 'h':
		case '?':	/*** DISPLAY HELP ***/

				cprintf(chat, "\n* %sV%sChat%s -- Interactive TCP/IP conference system -- %sMODIFY COMMAND HELP%s:",
					S_FG_MAG, S_FG_YEL, S_OFF, S_FG_CYN, S_OFF);
				cprintf(chat, "*\n* The %s.M%s command modifies the %saccess%s or %smodification%s permissions of",
					S_FG_GRN, S_OFF, S_BOLD, S_OFF,
					S_BOLD, S_OFF);
				cprintf(chat, "* registered users and may be used with the %sCHACC%s or %sCHMOD%s access",
					S_BOLD, S_OFF, S_BOLD, S_OFF);
				cprintf(chat, "* permission only. Permissions may be modified only in accordance");
				cprintf(chat, "* to your own %smodification%s permissions.",
					S_BOLD, S_OFF);

				cprintf(chat, "*\n*  %sSynopsis:%s", S_UL, S_OFF);
				cprintf(chat, "*\n*  %s.M%s <a|m|?> <-/+perm> ... <nick> ...",
					S_FG_GRN, S_OFF);

				cprintf(chat, "*\n*  %sWhere:%s", S_UL, S_OFF);
				cprintf(chat, "*\n*  <a|m|?>    - Specifies if you want to modify %sa%sccess or %sm%sodify permissions",
					S_BOLD, S_OFF, S_BOLD, S_OFF);
				cprintf(chat, "*  <-/+perm>  - The permissions you want to deny or grant which can be any of:");
				cprintf(chat, "*               ADD, DELETE, CHACC, CHMOD, KILL, BCAST or ACL");
				cprintf(chat, "*  <nick>     - One or more nickname(s) whose permissions will be modified");

				return;
		default:	cprintf(chat, "* Invalid permission set specifier '%c'", *arg);
				return;
	}

	if(*(++arg) == ' ')
		*arg++ = '\0';

	/*
	 * Parse mode change operators with leading '-' or '+' character
	 * into takeargv[] and giveargv[] respectively. Find the start of
	 * the specified nicknamelist (if any)
	 */

	nicklist = arg;

	while(*arg != '\0')
	{
		if(*arg == '+')
		{
			nicklist = NULL;

			*arg++ = '\0';

			if(*arg == '\0' || *arg == ' ' || *arg == '\t')
			{
				cprintf(chat, "* Missing permission name for '+' operator");
				return;
			}

			giveargv[giveargc++] = arg;
		}
		else if(*arg == '-')
		{
			nicklist = NULL;

			*arg++ = '\0';

			if(*arg == '\0' || *arg == ' ' || *arg == '\t')
			{
				cprintf(chat, "* Missing permission name for '-' operator");
				return;
			}

			takeargv[takeargc++] = arg;
		}
		else if(*arg == ' ')
		{
			*arg++ = '\0';
			nicklist = arg;
			continue;
		}
		else if(nicklist)
		{
			break;
		}

		++arg;
	}

	/*
	 * Create givemask and takemask corresponding to the
	 * permission names specified in giveargv[] and takeargv[]
	 */

	for(i = 0; i < giveargc; i++)
	{
		if(permission_mask(giveargv[i], &md) == -1)
		{
			cprintf(chat, "* Unknown permission name '%s'", giveargv[i]);
			return;
		}

		givemask |= md;
	}

	for(i = 0; i < takeargc; i++)
	{
		if(permission_mask(takeargv[i], &md) == -1)
		{
			cprintf(chat, "* Unknown permission name '%s'", takeargv[i]);
			return;
		}

		takemask |= md;
	}

	/*
	 * Check for modes that will compensate each other
	 */

	md = (givemask ^ takemask);
	givemask &= md;
	takemask &= md;

	/*
	 * Check if any mode change would take place
	 */

	if(givemask == 0 && takemask == 0)
	{
		cprintf(chat, "* Null permission change specified");
		return;		
	}

	/*
	 * Check if any nicknames were specified
	 */

	if(nicklist == NULL || *nicklist == '\0')
	{
		cprintf(chat, "* No nickname(s) specified");
		return;
	}

#if HAVE_SYS_UIO_H

	/*
	 * Prepare scatter/gather array
	 */

	givemask = htonl(givemask);
	takemask = htonl(takemask);

	md = strlen(nicklist);

	vmsg.cmd = htonl((permset == 'a') ? CMD_CHACCPERM : CMD_CHMODPERM);
	vmsg.len = htonl((2 * sizeof(u_int32_t) + md));

	iov[0].iov_base = (char *)&vmsg;
	iov[0].iov_len = sizeof(vmsg);
	iov[1].iov_base = (char *)&givemask;
	iov[1].iov_len = sizeof(givemask);
	iov[2].iov_base = (char *)&takemask;
	iov[2].iov_len = sizeof(takemask);
	iov[3].iov_base = nicklist;
	iov[3].iov_len = md;

	/*
	 * Send command
	 */

	writev(sconn.fd, (struct iovec *)&iov, 4);
#else
	md = (sizeof(VMSG) + 2 * sizeof(u_int32_t) + strlen(nicklist));

	if((sndbuf = (u_char *)malloc(md + 1)) == NULL)
	{
		cprintf(chat, "* Memory allocation error (%s%s%s)",
			S_FG_RED, strerror(errno), S_OFF);
		return;
	}

	vmsg = (VMSG *)sndbuf;

	vmsg->cmd = htonl((permset == 'a') ? CMD_CHACCPERM : CMD_CHMODPERM);
	vmsg->len = htonl(md - sizeof(VMSG));

	*((u_int32_t *)(sndbuf + sizeof(VMSG))) = htonl(givemask);
	*((u_int32_t *)(sndbuf + sizeof(VMSG) + sizeof(u_int32_t))) = htonl(takemask);

	arg = sndbuf + (sizeof(VMSG) + 2 * sizeof(u_int32_t));

	sprintf(arg, "%s", nicklist);

	write(sconn.fd, sndbuf, md);

	free(sndbuf);
#endif
}

void chnick(char *newnick)
{
	char	prompt[64];
	u_char	nickbuf[VPNICKSIZE+1];
	u_char	sndbuf[VPNICKSIZE+VPPASSWDSIZE+2];

	BZERO(nickbuf, VPNICKSIZE+1);

	/*
	 * Request nickname if not already given on the commandline
	 */

	if(*newnick == '\0')
	{
		sprintf(prompt, "%sNew nickname%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, VPNICKSIZE, ED_RST_CTX))
		{
			cprintf(chat, "* Command aborted\n");
			clear_input();
			return;
		}

		clear_input();

		strncpy(nickbuf, ed->buffer, VPNICKSIZE);
	}
	else
	{
		strncpy(nickbuf, newnick, VPNICKSIZE);
	}

	/*
	 * Check for space and colon character in nickname
	 */

	if(strchr(nickbuf, ' ') != NULL || strchr(nickbuf, ':') != NULL)
	{
		cprintf(chat, "* Nickname '%s' is invalid", nickbuf);
		return;
	}

	/*
	 * Request password for new nickname
	 */

	sprintf(prompt, "%sPassword%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

	input_line(prompt, VPPASSWDSIZE, (ED_RST_CTX | ED_NO_ECHO));

	clear_input();

	/*
	 * Save the nickname, so that we can update the statusbar
	 * accordingly when we receive the NICK_ACK message later.
	 */

	strncpy(vp.tmpnick, nickbuf, VPNICKSIZE);
	vp.tmpnick[VPNICKSIZE] = '\0';

	/*
	 * Send the command
	 */

	BZERO(sndbuf, VPNICKSIZE+VPPASSWDSIZE+2);

	if(ed->buffer == ed->endpos)
		sprintf(sndbuf, "%s", nickbuf);
	else
		sprintf(sndbuf, "%s %s", nickbuf, ed->buffer);

	clear_ed(ed);

	snd_serv(CMD_CHNICK, sndbuf, strlen(sndbuf));

	BZERO(sndbuf, VPNICKSIZE+VPPASSWDSIZE+2);
}

void chpasswd(u_char *nick)
{
	char	prompt[64];
	u_char	new_passwd[VPPASSWDSIZE+1];

	/*
	 * If a nickname is given then the password of this
	 * nickname will be changed via the SETPASSWD function
	 * instead of changing the own password via CHPASSWD.
	 */

	if(nick != NULL && *nick != '\0')
	{
		/*
		 * Set password
		 */

		u_char	nickname[VPNICKSIZE+1];
		u_char	snd_buf[VPNICKSIZE+VPPASSWDSIZE+2];

		if(strlen(nick) > VPNICKSIZE)
		{
			cprintf(chat, "* Nickname '%s' is too long", nick);
			return;
		}

		BZERO(nickname, VPNICKSIZE+1);
		strncpy(nickname, nick, VPNICKSIZE);

		sprintf(prompt, "%sNew password for '%s'%s%s:%s ",
			S_BOLD, nickname, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, VPPASSWDSIZE, (ED_RST_CTX | ED_NO_ECHO)))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		BZERO(new_passwd, VPPASSWDSIZE+1);
		strncpy(new_passwd, ed->buffer, VPPASSWDSIZE);

		sprintf(prompt, "%sVerification%s%s:%s ",
			S_BOLD, S_OFF, S_BLINK, S_OFF);

		input_line(prompt, VPPASSWDSIZE, (ED_RST_CTX | ED_NO_ECHO));

		if(strncmp(new_passwd, ed->buffer, VPPASSWDSIZE))
		{
			cprintf(chat, "* Verification failed");
			return;
		}

		/*
		 * Send SETPASSWD command
		 */

		sprintf(snd_buf, "%s %s", nickname, new_passwd);
		snd_serv(CMD_SETPASSWD, snd_buf, strlen(snd_buf));

		BZERO(snd_buf, (VPNICKSIZE+VPPASSWDSIZE+2));
		BZERO(new_passwd, VPPASSWDSIZE+1);
	}
	else
	{
		/*
		 * Change own password
		 */

		u_char	cur_passwd[2*VPPASSWDSIZE+1];
		u_char	snd_buf[3*VPPASSWDSIZE+2];

		sprintf(prompt, "%sCurrent password%s%s:%s ",
			S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, VPPASSWDSIZE, (ED_RST_CTX | ED_NO_ECHO)))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		escape_str(ed->buffer, cur_passwd, 2*VPPASSWDSIZE+1);

		sprintf(prompt, "%sNew password%s%s:%s ",
			S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, VPPASSWDSIZE, (ED_RST_CTX | ED_NO_ECHO)))
		{
			cprintf(chat, "* Aborted");
			return;
		}

		BZERO(new_passwd, VPPASSWDSIZE+1);
		strncpy(new_passwd, ed->buffer, VPPASSWDSIZE);

		sprintf(prompt, "%sVerification%s%s:%s ",
			S_BOLD, S_OFF, S_BLINK, S_OFF);

		input_line(prompt, VPPASSWDSIZE, (ED_RST_CTX | ED_NO_ECHO));

		clear_input();

		if(strncmp(new_passwd, ed->buffer, VPPASSWDSIZE))
		{
			cprintf(chat, "* Verification failed");
			return;
		}

		/*
		 * Send the CHPASSWD command to the server
		 */

		sprintf(snd_buf, "%s %s", cur_passwd, new_passwd);

		snd_serv(CMD_CHPASSWD, snd_buf, strlen(snd_buf));

		BZERO(snd_buf, 3*VPPASSWDSIZE+2);
		BZERO(cur_passwd, VPPASSWDSIZE+1);
		BZERO(new_passwd, VPPASSWDSIZE+1);
	}
}

void chtopic(char *dat)
{
	char *arg = dat;

	if(arg == NULL || *arg == '\0')
	{
		char prompt[64];

		sprintf(prompt, "%sEnter new topic %s%s=>%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, VPTOPICSIZE, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		arg = ed->buffer;
	}

	snd_serv(CMD_CHTOPIC, arg, (strlen(arg) > VPTOPICSIZE) ? VPTOPICSIZE : strlen(arg));
}

void clientinfo(char *user)
{
	char *arg = user;

	if(arg == NULL || *arg == '\0')
	{
		char prompt[64];

		sprintf(prompt, "%sInfo about what nickname(s)%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		arg = ed->buffer;
	}

	snd_serv(CMD_CLIENTINFO, arg, strlen(arg));
}

void delnick(char *dat)
{
	u_char	*nicklist = dat;

	/*
	 * Request list of nicknames to delete if not specified
	 */

	if(dat == NULL || *dat == '\0')
	{
		u_char	prompt[64];

		sprintf(prompt, "%sDelete nickname(s)%s%s:%s ",
			S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		nicklist = ed->buffer;
	}

	/*
	 * Send the DELNICK command to the server
	 */

	snd_serv(CMD_DELNICK, nicklist, strlen(nicklist));
}

void invite(char *user)
{
	char *arg = user;

	if(arg == NULL || *arg == '\0')
	{
		char prompt[64];

		sprintf(prompt, "%sInvite nickname(s)%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		arg = ed->buffer;
	}

	snd_serv(CMD_INVITE, arg, strlen(arg));
}

void killclient(char *dat)
{
	char	*arg = dat;

	if(arg == NULL || *arg == '\0')
	{
		char	prompt[64];

		sprintf(prompt, "%sKill nickname(s)%s%s:%s ",
			S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		arg = ed->buffer;
	}

	snd_serv(CMD_KILL, arg, strlen(arg));
}

void prv_msg(char *dat)
{
	char prompt[64];
	char msgrcp[VPNICKSIZE+1];
	char *arg;

#if HAVE_SYS_UIO_H
	char space = ' ';
	struct iovec iov[4];
	VMSG svmsg;
#else
	int	bufsize;
	VMSG	*svmsg;
#endif

	if(dat == NULL || *dat == '\0')
	{
		sprintf(prompt, "%sEnter recipient%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, VPNICKSIZE, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		strcpy(msgrcp, ed->buffer);

		sprintf(prompt, "%sMessage %s%s%s=>%s ", S_FG_MAG, S_OFF, S_BOLD, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
		{
			cprintf(chat, "* No message?");
			clear_input();
			return;
		}

		clear_input();

		arg = ed->buffer;
	}
	else if((char *)strchr(dat, ' ') == NULL)
	{
		strncpy(msgrcp, dat, 15);
		msgrcp[15] = '\0';
	
		sprintf(prompt, "%sMessage %s%s%s=>%s ", S_FG_MAG, S_OFF, S_BOLD, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
		{
			cprintf(chat, "* No message?");
			clear_input();
			return;
		}

		clear_input();

		arg = ed->buffer;
	}
	else
	{
		if((arg = (char *)strchr(dat, ' ')) == NULL)
		{
			cprintf(chat, "* No message?");
			return;
		}

		*arg++ = '\0';
		strncpy(msgrcp, dat, 15);
		msgrcp[15] = '\0';
	}

#if HAVE_SYS_UIO_H
	svmsg.cmd = htonl(CMD_PRVMSG);
	svmsg.len = htonl(strlen(msgrcp) + strlen(arg) + 1);

	iov[0].iov_base = (char *)&svmsg;
	iov[1].iov_base = (char *)&msgrcp;
	iov[2].iov_base = (char *)&space;
	iov[3].iov_base = arg;

	iov[0].iov_len = sizeof(VMSG);
	iov[1].iov_len = strlen(msgrcp);
	iov[2].iov_len = 1;
	iov[3].iov_len = strlen(arg);

	writev(sconn.fd, (struct iovec *)&iov, 4);
#else
	bufsize = (sizeof(VMSG) + strlen(msgrcp) + strlen(arg) + 1);

	{
		u_char	sndbuf[bufsize + 1];
		char	*sptr;

		svmsg = (VMSG *)&sndbuf;
		sptr = (char *)&sndbuf[sizeof(VMSG)];

		sprintf(sptr, "%s %s", msgrcp, arg);

		svmsg->cmd = htonl(CMD_PRVMSG);
		svmsg->len = htonl(bufsize - sizeof(VMSG));

		write(sconn.fd, &sndbuf, bufsize);
	}
#endif

}

void querynick(char *dat)
{
	char *arg = dat;

	if(arg == NULL || *arg == '\0')
	{
		char prompt[64];

		clear_ed(ed);

		sprintf(ed->buffer, "%s", vp.nick);
		ed->endpos += strlen(ed->buffer);
		ed->curpos = ed->endpos;

		sprintf(prompt, "%sQuery nickname(s)%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, 0))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		arg = ed->buffer;
	}

	if(!strcmp(arg, vp.nick))
		snd_serv(CMD_QUERY, NULL, 0);
	else
		snd_serv(CMD_QUERY, arg, strlen(arg));
}

void rtcmp(char *dat)
{
#if HAVE_SYS_UIO_H
	struct iovec	iov[2];
	VMSG		svmsg;
	RTCMP		rt;

	sprintf(rt.dummy, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopq");
	rt.seq = NUMSTATS;

	svmsg.cmd = htonl(CMD_RTCMP);
	svmsg.len = htonl(sizeof(RTCMP));

	iov[0].iov_base = (char *)&svmsg;
	iov[1].iov_base = (char *)&rt;
	iov[0].iov_len = sizeof(VMSG);
	iov[1].iov_len = sizeof(RTCMP);

	timestamp(&rt.stamp);

	if(writev(sconn.fd, (struct iovec *)&iov, 2) == -1)
#else
	u_char		sndbuf[sizeof(VMSG) + sizeof(RTCMP)];
	VMSG		*svmsg;
	RTCMP		*rt;

	svmsg = (VMSG *)&sndbuf;
	rt = (RTCMP *)&sndbuf[sizeof(VMSG)];

	sprintf(rt->dummy, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopq");
	rt->seq = NUMSTATS;

	svmsg->cmd = htonl(CMD_RTCMP);
	svmsg->len = htonl(sizeof(RTCMP));

	timestamp(&rt->stamp);

	if(write(sconn.fd, (char *)&sndbuf, sizeof(VMSG) + sizeof(RTCMP)) == -1)
#endif
		vquit(strerror(errno));
}

void serveropts(char *dat)
{
	GSOPT		opt;
	char		*giveargv[32];
	char		*takeargv[32];
	u_char		giveargc	= 0;
	u_char		takeargc	= 0;
	u_int32_t	givemask	= 0;
	u_int32_t	takemask	= 0;
	u_int32_t	md;
	char		*arg		= dat;
	register int	i;

#if HAVE_SYS_UIO_H
	struct iovec	iov[4];
	VMSG		vmsg;
#else
	u_char		*sndbuf;
	VMSG		*vmsg;
#endif

	/*
	 * Parse server option operators with leading '-' or '+' character
	 * into takeargv[] and giveargv[] respectively.
	 */

	while(*arg != '\0')
	{
		if(*arg == '+')
		{
			*arg++ = '\0';

			if(*arg == '\0' || *arg == ' ' || *arg == '\t')
			{
				cprintf(chat, "* Missing option name for '+' operator");
				return;
			}

			giveargv[giveargc++] = arg;

			while(*arg != '+' && *arg != '-' && *arg != ' '
				&& *arg != '\t' && *arg != '\0')
			{
				++arg;
			}
		}
		else if(*arg == '-')
		{
			*arg++ = '\0';

			if(*arg == '\0' || *arg == ' ' || *arg == '\t')
			{
				cprintf(chat, "* Missing option name for '-' operator");
				return;
			}

			takeargv[takeargc++] = arg;

			while(*arg != '+' && *arg != '-' && *arg != ' '
				&& *arg != '\t' && *arg != '\0')
			{
				++arg;
			}
		}
		else if(*arg == ' ' || *arg == '\t')
		{
			*arg++ = '\0';
		}
		else
		{
			cprintf(chat, "* Invalid statement '%s' encountered",
				arg);
			return;
		}
	}

	/*
	 * Create givemask and takemask corresponding to the
	 * option names specified in giveargv[] and takeargv[]
	 */

	for(i = 0; i < giveargc; i++)
	{
		if(svopt_mask(giveargv[i], &md) == -1)
		{
			cprintf(chat, "* Unknown server option name '%s'", giveargv[i]);
			return;
		}

		givemask |= md;
	}

	for(i = 0; i < takeargc; i++)
	{
		if(svopt_mask(takeargv[i], &md) == -1)
		{
			cprintf(chat, "* Unknown server option name '%s'", takeargv[i]);
			return;
		}

		takemask |= md;
	}

	/*
	 * Check for options that will compensate each other
	 */

	md = (givemask ^ takemask);
	givemask &= md;
	takemask &= md;

	/*
	 * Fill in GSOPT structure
	 */

	opt.givemask = htonl(givemask);
	opt.takemask = htonl(takemask);
	opt.control = 0;

#if HAVE_SYS_UIO_H

	/*
	 * Prepare scatter/gather array
	 */

	vmsg.cmd = htonl(CMD_SVOPT);
	vmsg.len = htonl(sizeof(GSOPT));

	iov[0].iov_base = (char *)&vmsg;
	iov[0].iov_len = sizeof(vmsg);
	iov[1].iov_base = (char *)&opt;
	iov[1].iov_len = sizeof(GSOPT);

	/*
	 * Send command
	 */

	writev(sconn.fd, (struct iovec *)&iov, 2);
#else

	/*
	 * Prepare send buffer
	 */

	md = (sizeof(VMSG) + sizeof(GSOPT));

	if((sndbuf = (u_char *)malloc(md + 1)) == NULL)
	{
		cprintf(chat, "* Memory allocation error (%s%s%s)",
			S_FG_RED, strerror(errno), S_OFF);
		return;
	}

	vmsg = (VMSG *)sndbuf;

	vmsg->cmd = htonl(CMD_SVOPT);
	vmsg->len = htonl(md - sizeof(VMSG));

	BCOPY(&opt, (u_char *)(sndbuf + sizeof(VMSG)), sizeof(GSOPT));

	/*
	 * Send command
	 */

	write(sconn.fd, sndbuf, md);

	free(sndbuf);
#endif
}

void serverstatus(char *dat)
{
	u_int32_t	page	= 0;

	errno = 0;

	if(dat != NULL && *dat != '\0')
	{
		page = htonl(strtol(dat, NULL, 0));
	}

	if(page == LONG_MIN || page == LONG_MAX || errno == ERANGE)
		page = 0;

	snd_serv(CMD_STATUS, (void *)&page, sizeof(page));
}

void submit_topic(void)
{
	u_long	len;
	char	prompt[64];
	ED	topic_ed;
	ED	*saved_ed;

	/*** save current input context and construct new one ***/

	saved_ed = ed;
	ed = &topic_ed;

	/*** Construct prompt and get input ***/

	sprintf(prompt, "%s%sEnter topic%s %s=>%s ", S_OFF, S_BOLD, S_OFF, S_BLINK, S_OFF);

	while(!(len = input_line(prompt, 45, ED_RST_CTX)));

	/*** Restore old input context ***/

	ed = saved_ed;
	line_update();

	snd_serv(CMD_ASSTOPIC, topic_ed.buffer, len);
}

void ulist(char *dat)
{
	struct list lt;

	if(dat == NULL || *dat == '\0')	/* default: all channels */
	{
		lt.cmd = htonl(ALL_CHAN);
		snd_serv(CMD_ULIST, (char *)&lt, sizeof(struct list));
	}
	else if(*dat == '*')	/* my channel */
	{
		lt.cmd = htonl(MY_CHAN);
		snd_serv(CMD_ULIST, (char *)&lt, sizeof(struct list));		
	}
	else				/* Channel <chan> */
	{
		lt.cmd = htonl(CHAN);
		lt.chan = htonl(strtol(dat, 0, 0));
		snd_serv(CMD_ULIST, (char *)&lt, sizeof(struct list)); 
	}
}

void wakeup(char *dat)
{
	char *arg = dat;

	if(arg == NULL || *arg == '\0')
	{
		char prompt[64];

		sprintf(prompt, "%sSend wakeup signal to%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		arg = ed->buffer;
	}

	snd_serv(CMD_WAKEUP, arg, strlen(arg));
}

void svdnsopt(char *dat)
{
	u_char lvl;

	if(dat == NULL || *dat == '\0')
	{
		/*
		 * Display current DNS checking level only.
		 */

		snd_serv(CMD_SVDNSOPT, NULL, 0);
		return;
	}

	if(!strcasecmp(dat, "none"))
		lvl = SV_DNS_CHK_NONE;
	else if(!strcasecmp(dat, "fwd") || !strcasecmp(dat, "forward"))
		lvl = SV_DNS_CHK_FWD;
	else if(!strcasecmp(dat, "rev") || !strcasecmp(dat, "reverse"))
		lvl = SV_DNS_CHK_REV;
	else if(!strcasecmp(dat, "strict"))
		lvl = SV_DNS_CHK_STRICT;
	else
	{
		cprintf(chat, "* Invalid argument");
		return;
	}

	snd_serv(CMD_SVDNSOPT, &lvl, 1);
}

void exclude(char *dat)
{
	char *arg = dat;

	if(arg == NULL || *arg == '\0')
	{
		char prompt[64];

		sprintf(prompt, "%sExclude nickname(s)%s%s:%s ", S_BOLD, S_OFF, S_BLINK, S_OFF);

		if(!input_line(prompt, 0, ED_RST_CTX))
		{
			cprintf(chat, "* Aborted");
			clear_input();
			return;
		}

		clear_input();

		arg = ed->buffer;
	}

	snd_serv(CMD_EXCLUDE, arg, strlen(arg));
}


syntax highlighted by Code2HTML, v. 0.9.1