/* NeoStats - IRC Statistical Services 
** Copyright (c) 1999-2004 Adam Rutter, Justin Hammond
** http://www.neostats.net/
**
**  Portions Copyright (c) 2000-2001 ^Enigma^
**
**  Portions Copyright (c) 1999 Johnathan George net@lite.net
**
**  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.
**
**  You should have received a copy of the GNU General Public License
**  along with this program; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
**  USA
**
** NeoStats CVS Identification
** $Id: ircd.c 2036 2004-08-19 09:54:18Z Mark $
*/
#include <setjmp.h>
#include "stats.h"
#include "ircd.h"
#include "dl.h"
#include "sock.h"
#include "log.h"
#include "users.h"
#include "chans.h"
#include "services.h"
#include "server.h"
#include "bans.h"
#include "dns.h"

ircd_server ircd_srv;

static char ircd_buf[BUFSIZE];
static char UmodeStringBuf[64];
#ifdef GOTUSERSMODES
static char SmodeStringBuf[64];
#endif
long services_bot_umode= 0;

/* Fully split buffer */
static char privmsgbuffer[BUFSIZE];

/* Temp flag for backward compatibility in new splitbuf system */
static int SkipModuleFunction = 0;

static int signon_newbot (const char *nick, const char *user, const char *host, const char *realname, long Umode);
#ifdef IRCU
int scmode_op (const char *who, const char *chan, const char *mode, const char *bot);
#endif

/** @brief init_ircd
 *
 *  ircd initialisation
 *
 * @return 
 */
void
init_ircd ()
{
#ifdef IRCU
	/* Temp: force tokens for IRCU */
	ircd_srv.token = 1;
#endif
	services_bot_umode = UmodeStringToMask(services_bot_modes, 0);
};

/** @brief UmodeMaskToString
 *
 *  Translate a mode mask to the string equivalent
 *
 * @return 
 */
char* 
UmodeMaskToString(const long Umode) 
{
	int i, j;

	UmodeStringBuf[0] = '+';
	j = 1;
	for (i = 0; i < ircd_umodecount; i++) {
		if (Umode & user_umodes[i].umode) {
			UmodeStringBuf[j] = user_umodes[i].mode;
			j++;
		}
	}
	UmodeStringBuf[j] = '\0';
	return(UmodeStringBuf);
}

/** @brief UmodeStringToMask
 *
 *  Translate a mode string to the mask equivalent
 *
 * @return 
 */
long
UmodeStringToMask(const char* UmodeString, long Umode)
{
	int i;
	int add = 0;
	char tmpmode;

	/* Walk through mode string and convert to umode */
	tmpmode = *(UmodeString);
	while (tmpmode) {
		switch (tmpmode) {
		case '+':
			add = 1;
			break;
		case '-':
			add = 0;
			break;
		default:
			for (i = 0; i < ircd_umodecount; i++) {
				if (user_umodes[i].mode == tmpmode) {
					if (add) {
						Umode |= user_umodes[i].umode;
						break;
					} else {
						Umode &= ~user_umodes[i].umode;
						break;
					}
				}
			}
		}
		tmpmode = *UmodeString++;
	}
	return(Umode);
}

#ifdef GOTUSERSMODES
/** @brief SmodeMaskToString
 *
 *  Translate a smode mask to the string equivalent
 *
 * @return 
 */
char* 
SmodeMaskToString(const long Smode) 
{
	int i, j;

	SmodeStringBuf[0] = '+';
	j = 1;
	for (i = 0; i < ircd_smodecount; i++) {
		if (Smode & user_smodes[i].umode) {
			SmodeStringBuf[j] = user_smodes[i].mode;
			j++;
		}
	}
	SmodeStringBuf[j] = '\0';
	return(SmodeStringBuf);
}

/** @brief SmodeStringToMask
 *
 *  Translate a smode string to the mask equivalent
 *
 * @return 
 */
long
SmodeStringToMask(const char* SmodeString, long Smode)
{
	int i;
	int add = 0;
	char tmpmode;

	/* Walk through mode string and convert to smode */
	tmpmode = *(SmodeString);
	while (tmpmode) {
		switch (tmpmode) {
		case '+':
			add = 1;
			break;
		case '-':
			add = 0;
			break;
		default:
			for (i = 0; i < ircd_smodecount; i++) {
				if (user_smodes[i].mode == tmpmode) {
					if (add) {
						Smode |= user_smodes[i].umode;
						break;
					} else {
						Smode &= ~user_smodes[i].umode;
						break;
					}
				}
			}
		}
		tmpmode = *SmodeString++;
	}
	return(Smode);
}
#endif

/** @brief join_bot_to_chan
 *
 * 
 *
 * @return NS_SUCCESS if suceeds, NS_FAILURE if not 
 */
int 
join_bot_to_chan (const char *who, const char *chan, unsigned long chflag)
{
	char savemod[SEGV_INMODULE_BUFSIZE];

	strlcpy(savemod, segv_inmodule, SEGV_INMODULE_BUFSIZE);
#ifdef GOTSJOIN
	ssjoin_cmd(who, chan, chflag);
#else
	sjoin_cmd(who, chan);
	if(chflag == CMODE_CHANOP || chflag == CMODE_CHANADMIN)
#if defined(IRCU)
		scmode_op(who, chan, "+o", who);
#else
		schmode_cmd(who, chan, "+o", who);
#endif

#endif
	SET_SEGV_INMODULE(savemod);
	return NS_SUCCESS;
}

/** @brief signon_newbot
 *
 *  work in progress to replace ircd specific routines
 *  needs sjoin fix to complete
 *
 *  @return NS_SUCCESS if suceeds, NS_FAILURE if not 
 */
int
signon_newbot (const char *nick, const char *user, const char *host, const char *realname, long Umode)
{
	snewnick_cmd (nick, user, host, realname, Umode);
	if ((me.allbots > 0) || (Umode & services_bot_umode)) {
#ifdef GOTSJOIN
		ssjoin_cmd (nick, me.chan, CMODE_CHANADMIN);
#else
		sjoin_cmd (nick, me.chan);
#if defined(IRCU)
		scmode_op(nick, me.chan, "+o", nick);
#else
		schmode_cmd(nick, me.chan, "+o", nick);
#endif

#endif
	}
	return NS_SUCCESS;
}

/** @brief init_bot
 *
 * 
 *
 * @return NS_SUCCESS if suceeds, NS_FAILURE if not 
 */
int
init_bot (char *nick, char *user, char *host, char *realname, const char *modes, char *mod_name)
{
	User *u;
	long Umode;

	SET_SEGV_LOCATION();
	u = finduser (nick);
	if (u) {
		nlog (LOG_WARNING, LOG_CORE, "Attempting to Login with a Nickname that already Exists: %s", nick);
		return NS_FAILURE;
	}
	if(!add_mod_user (nick, mod_name)) {
		nlog (LOG_WARNING, LOG_CORE, "add_mod_user failed for module %s bot %s", mod_name, nick);
		return NS_FAILURE;
	}
	Umode = UmodeStringToMask(modes, 0);
	signon_newbot (nick, user, host, realname, Umode);
	/* restore segv_inmodule from SIGNON */
	SET_SEGV_INMODULE(mod_name);
	return NS_SUCCESS;
}

/** @brief init_mod_bot
 *
 *  replacement for init_bot - work in progress
 *
 * @return NS_SUCCESS if suceeds, NS_FAILURE if not 
 */
ModUser * init_mod_bot (char * nick, char * user, char * host, char * realname, 
						const char *modes, unsigned int flags, bot_cmd *bot_cmd_list, 
						bot_setting *bot_setting_list, char * mod_name)
{
	ModUser * bot_ptr; 
	User *u;
	long Umode;

	SET_SEGV_LOCATION();
	u = finduser (nick);
	if (u) {
		nlog (LOG_WARNING, LOG_CORE, "Attempting to Login with a Nickname that already Exists: %s", nick);
		return NULL;
	}
	bot_ptr = add_mod_user (nick, mod_name);
	if(!bot_ptr) {
		nlog (LOG_WARNING, LOG_CORE, "add_mod_user failed for module %s bot %s", mod_name, nick);
		return NULL;
	}
	Umode = UmodeStringToMask(modes, 0);
	signon_newbot (nick, user, host, realname, Umode);
#ifdef UMODE_DEAF
	if(flags&BOT_FLAG_DEAF) {
		sumode_cmd (nick, nick, UMODE_DEAF);
	}
#endif
	/* restore segv_inmodule from SIGNON */
	SET_SEGV_INMODULE(mod_name);
	bot_ptr->flags = flags;
	add_bot_cmd_list(bot_ptr, bot_cmd_list);
	if (bot_setting_list != NULL) {
		bot_ptr->bot_settings = bot_setting_list;	
		/* Default SET to ROOT only */
		bot_ptr->set_ulevel = NS_ULEVEL_ROOT;
		/* Now calculate minimum defined user level */
		while(bot_setting_list->option != NULL) {
			if(bot_setting_list->ulevel < bot_ptr->set_ulevel)
				bot_ptr->set_ulevel = bot_setting_list->ulevel;
			bot_setting_list++;
		}
	} else {
		bot_ptr->bot_settings = NULL;	
		bot_ptr->set_ulevel = NS_ULEVEL_ROOT;
	}
	return bot_ptr;
}

/** @brief del_bot
 *
 * 
 *
 * @return NS_SUCCESS if suceeds, NS_FAILURE if not 
 */
int
del_bot (char *nick, char *reason)
{
	User *u;

	SET_SEGV_LOCATION();
	u = finduser (nick);
	if (!u) {
		nlog (LOG_WARNING, LOG_CORE, "Attempting to Logoff with a Nickname that does not Exists: %s", nick);
		return NS_FAILURE;
	}
	nlog (LOG_DEBUG1, LOG_CORE, "Deleting bot %s for %s", nick, reason);
	//XXXX TODO: need to free the channel list hash. We dont according to valgrind
	squit_cmd (nick, reason);
	del_mod_user (nick);
	return NS_SUCCESS;
}

/** @brief CloakBotHost
 *
 *  Create a hidden hostmask for the bot 
 *  Currently only Unreal support via UMODE auto cloaking
 *  but function created for future use and propogation to
 *  external modules to avoid a future joint release.
 *
 *  @return NS_SUCCESS if suceeds, NS_FAILURE if not 
 */
int 
CloakHost (ModUser *bot_ptr)
{
#ifdef GOTUMODECLOAKING
	sumode_cmd (bot_ptr->nick, bot_ptr->nick, UMODE_HIDE);
	return NS_SUCCESS;	
#else
	return NS_FAILURE;	
#endif
}

/** @brief split_buf
 * Taken from Epona - Thanks! 
 * Split a buffer into arguments and store the arguments in an
 * argument vector pointed to by argv (which will be malloc'd
 * as necessary); return the argument count.  If colon_special
 * is non-zero, then treat a parameter with a leading ':' as
 * the last parameter of the line, per the IRC RFC.  Destroys
 * the buffer by side effect. 
 *
 * @return 
 */
#ifndef IRCD_SPLITBUF
int
splitbuf (char *buf, char ***argv, int colon_special)
{
	int argvsize = 8;
	int argc;
	char *s;
	int colcount = 0;
	SET_SEGV_LOCATION();
	*argv = calloc (sizeof (char *) * argvsize, 1);
	argc = 0;
	/*if (*buf == ':')
		buf++;*/
	while (*buf) {
		if (argc == argvsize) {
			argvsize += 8;
			*argv = realloc (*argv, sizeof (char *) * argvsize);
		}
		if ((*buf == ':') && (colcount < 1)) {
			buf++;
			colcount++;
			if(colon_special) {
				(*argv)[argc++] = buf;
				break;
			}
		}
		s = strpbrk (buf, " ");
		if (s) {
			*s++ = 0;
			while (isspace (*s))
				s++;
		} else {
			s = buf + strnlen (buf, BUFSIZE);
		}
		if (*buf == 0) {
			buf++;
		}
		(*argv)[argc++] = buf;
		buf = s;
	}
	return argc;
}
#endif

int
split_buf (char *buf, char ***argv, int colon_special)
{
	int argvsize = 8;
	int argc;
	char *s;
	int colcount = 0;
	SET_SEGV_LOCATION();
	*argv = calloc (sizeof (char *) * argvsize, 1);
	argc = 0;
	if (*buf == ':')
		buf++;
	while (*buf) {
		if (argc == argvsize) {
			argvsize += 8;
			*argv = realloc (*argv, sizeof (char *) * argvsize);
		}
		if ((*buf == ':') && (colcount < 1)) {
			buf++;
			colcount++;
		}
		s = strpbrk (buf, " ");
		if (s) {
			*s++ = 0;
			while (isspace (*s))
				s++;
		} else {
			s = buf + strnlen (buf, BUFSIZE);
		}
		if (*buf == 0) {
			buf++;
		}
		(*argv)[argc++] = buf;
		buf = s;
	}
	return argc;
}

/** @brief joinbuf 
 *
 * 
 *
 * @return 
 */
char *
joinbuf (char **av, int ac, int from)
{
	int i;
	char *buf;

	buf = malloc (BUFSIZE);
	/* from is zero based while ac has base of 1. 
	 * Therefore we need to check >= before trying to perform
	 * the join.
	 * The current (null) string we return may not be needed
	 * so should be removed once all joinbuf calls are checked.
	 * Maybe we should just return NULL if we fail and let
	 * the caller handle that case. 
	 */
	if(from >= ac) {
		nlog (LOG_DEBUG1, LOG_CORE, "joinbuf: from (%d) >= ac (%d)", from, ac);
		strlcpy (buf, "(null)", BUFSIZE);
	}
	else {
		strlcpy (buf, av[from], BUFSIZE);
		for (i = from + 1; i < ac; i++) {
			strlcat (buf, " ", BUFSIZE);
			strlcat (buf, av[i], BUFSIZE);
		}
	}
	return (char *) buf;
}

/** @brief process privmsg
 *
 * 
 *
 * @return none
 */
void 
m_notice (char* origin, char **av, int ac, int cmdptr)
{
	int i;
	int splitargc;
	char **splitargv;
	int argc;
	char **argv;

	SET_SEGV_LOCATION();
	if( av[0] == NULL) {
		nlog (LOG_DEBUG1, LOG_CORE, "m_notice: dropping unknown privmsg from %s, to %s : %s", origin, av[0], av[ac-1]);
		return;
	}
#if 0
	if( ircstrcasecmp(av[0], "AUTH")) {
		nlog (LOG_DEBUG1, LOG_CORE, "m_notice: dropping server notice from %s, to %s : %s", origin, av[0], av[ac-1]);
		return;
	}
#endif
	nlog (LOG_DEBUG1, LOG_CORE, "m_notice: from %s, to %s : %s", origin, av[0], av[ac-1]);
	strlcpy (privmsgbuffer, av[ac-1], BUFSIZE);
	splitargc = split_buf (av[ac-1], &splitargv, 0);
	argc = 0;
	AddStringToList (&argv, av[0], &argc);
	for( i = 0 ; i < splitargc ; i++ ) {
		AddStringToList (&argv, splitargv[i], &argc);
	}
	ModuleFunction (cmdptr, MSG_NOTICE, origin, argv, argc);
	free (argv);
	argc = 0;
	AddStringToList (&argv, origin, &argc);
	AddStringToList (&argv, av[0], &argc);
	AddStringToList (&argv, privmsgbuffer, &argc);
	if(av[0][0] == '#') {
		ModuleEvent (EVENT_CNOTICE, argv, argc);
	} else {
		ModuleEvent (EVENT_NOTICE, argv, argc);
	}
	free (argv);
	SkipModuleFunction = 1;
}

/** @brief process privmsg
 *
 * 
 *
 * @return none
 */
void
m_private (char* origin, char **av, int ac, int cmdptr)
{
	int i;
	int ret = NS_SUCCESS;
	int splitargc;
	char **splitargv;
	int argc;
	char **argv;
	char target[64];

	SET_SEGV_LOCATION();
	if( av[0] == NULL) {
		nlog (LOG_DEBUG1, LOG_CORE, "m_private: dropping unknown privmsg from %s, to %s : %s", origin, av[0], av[ac-1]);
		return;
	}
	nlog (LOG_DEBUG1, LOG_CORE, "m_private: from %s, to %s : %s", origin, av[0], av[ac-1]);
	/* its a privmsg, now lets see who too... */
	if (strstr (av[0], "!")) {
		strlcpy (target, av[0], 64);
		av[0] = strtok (target, "!");
	} else if (strstr (av[0], "@")) {
		strlcpy (target, av[0], 64);
		av[0] = strtok (target, "@");
	}
	strlcpy (privmsgbuffer, av[ac-1], BUFSIZE);
	splitargc = split_buf (av[ac-1], &splitargv, 0);
	argc = 0;
	AddStringToList (&argv, av[0], &argc);
	for( i = 0 ; i < splitargc ; i++ ) {
		AddStringToList (&argv, splitargv[i], &argc);
	}
	if(av[0][0] == '#') {
		bot_chan_message (origin, argv, argc);
		/* we need this to fool the check below */
		ret = NS_FAILURE;
	} else {
		ret = bot_message (origin, argv, argc);
	}
	free (argv);
	free (splitargv);
	if(ret == NS_FAILURE) {
		argc = 0;
		AddStringToList (&argv, origin, &argc);
		AddStringToList (&argv, av[0], &argc);
		AddStringToList (&argv, privmsgbuffer, &argc);
		if(av[0][0] == '#') {
			ModuleEvent (EVENT_CPRIVATE, argv, argc);
		} else {
			ModuleEvent (EVENT_PRIVATE, argv, argc);
		}
		free (argv);
	}
	return;
}

/** @brief process ircd commands
 *
 * 
 *
 * @return none
 */
#ifdef IRCU
void 
#else
static void 
#endif
process_ircd_cmd (int cmdptr, char *cmd, char* origin, char **av, int ac)
{
	int i;

	SET_SEGV_LOCATION();

	for (i = 0; i < ircd_cmdcount; i++) {
		if (!strcmp (cmd_list[i].name, cmd)
#ifdef GOTTOKENSUPPORT
			||(ircd_srv.token && cmd_list[i].token && !strcmp (cmd_list[i].token, cmd))
#endif
			) {
			if(cmd_list[i].function) {
				nlog (LOG_DEBUG3, LOG_CORE, "process_ircd_cmd: running command %s", cmd_list[i].name);
				cmd_list[i].function (origin, av, ac, cmdptr);
			} else {
				nlog (LOG_DEBUG3, LOG_CORE, "process_ircd_cmd: ignoring command %s", cmd);
			}
			cmd_list[i].usage++;
			break;
		}
	}
#if 0
	if(i >= ircd_cmdcount) {
		nlog (LOG_INFO, LOG_CORE, "No support for %s", cmd);
	}
#endif
	/* Send to modules */
	if(!SkipModuleFunction) {
		ModuleFunction (cmdptr, cmd, origin, av, ac);
	}
	SkipModuleFunction = 0;
}

/** @brief parse
 *
 * 
 *
 * @return none
 */
#ifndef IRCD_PARSE
void
parse (char *line)
{
	char origin[64], cmd[64], *coreLine;
	int cmdptr = 0;
	int ac;
	char **av;

	SET_SEGV_LOCATION();

	strip (line);
	strlcpy (recbuf, line, BUFSIZE);
	if (!(*line))
		return;
	nlog (LOG_DEBUG1, LOG_CORE, "--------------------------BEGIN PARSE---------------------------");
	nlog (LOG_DEBUG1, LOG_CORE, "R: %s", line);
	if (*line == ':') {
		coreLine = strpbrk (line, " ");
		if (!coreLine)
			return;
		*coreLine = 0;
		while (isspace (*++coreLine));
		strlcpy (origin, line + 1, sizeof (origin));
		memmove (line, coreLine, strnlen (coreLine, BUFSIZE) + 1);
		cmdptr = 1;
	} else {
		cmdptr = 0;
		*origin = 0;
	}
	if (!*line)
		return;
	coreLine = strpbrk (line, " ");
	if (coreLine) {
		*coreLine = 0;
		while (isspace (*++coreLine));
	} else {
		coreLine = line + strlen (line);
	}
	strlcpy (cmd, line, sizeof (cmd)); 
	nlog (LOG_DEBUG1, LOG_CORE, "origin: %s", origin);
	nlog (LOG_DEBUG1, LOG_CORE, "cmd   : %s", cmd);
	nlog (LOG_DEBUG1, LOG_CORE, "args  : %s", coreLine);
	ac = splitbuf (coreLine, &av, 1);
	process_ircd_cmd (cmdptr, cmd, origin, av, ac);
	nlog (LOG_DEBUG1, LOG_CORE, "---------------------------END PARSE----------------------------");
	free (av);
}
#endif

/** @brief init_services_bot
 *
 * 
 *
 * @return none
 */
int 
init_services_bot (void)
{
	char **av;
	int ac = 0;
	long Umode;

	SET_SEGV_LOCATION();
	if (finduser (s_Services)) {
		/* nick already exists on the network */
		strlcat (s_Services, "1", MAXNICK);
	}
	ircsnprintf (me.realname, MAXREALNAME, "/msg %s \2HELP\2", s_Services);
	Umode = UmodeStringToMask(services_bot_modes, 0);
	signon_newbot (s_Services, me.user, me.host, me.realname, Umode);
#ifdef UMODE_DEAF
	sumode_cmd (s_Services, s_Services, UMODE_DEAF);
#endif
	me.onchan = 1;
	AddStringToList (&av, me.uplink, &ac);
	ModuleEvent (EVENT_ONLINE, av, ac);
	free (av);
	return NS_SUCCESS;
}

/** @brief do_ping
 *
 * 
 *
 * @return none
 */
void
do_ping (const char* origin, const char* destination)
{
	send_pong (origin);
#ifdef MSG_BURST
	if (ircd_srv.burst) {
		send_ping (me.name, origin, origin);
	}
#endif
}

/** @brief do_pong
 *
 * 
 *
 * @return none
 */
void
do_pong (const char* origin, const char* destination)
{
	Server *s;
	char **av;
	int ac = 0;

	s = findserver (origin);
	if (s) {
		s->ping = me.now - ping.last_sent;
		if (ping.ulag > 1)
			s->ping -= (float) ping.ulag;
		if (!strcmp (me.s->name, s->name))
			ping.ulag = me.s->ping;
		AddStringToList (&av, s->name, &ac);
		ModuleEvent (EVENT_PONG, av, ac);
		free (av);
	} else {
		nlog (LOG_NOTICE, LOG_CORE, "Received PONG from unknown server: %s", origin);
	}
}

/** @brief flood
 *
 * 
 *
 * @return 
 */
int
flood (User * u)
{
	time_t current = me.now;

	if (!u) {
		nlog (LOG_WARNING, LOG_CORE, "flood: can't find user");
		return 0;
	}
	if (UserLevel (u) >= NS_ULEVEL_OPER)	/* locop or higher */
		return 0;
	if (current - u->t_flood > 10) {
		u->t_flood = me.now;
		u->flood = 0;
		return 0;
	}
	if (u->flood >= 5) {
		nlog (LOG_NORMAL, LOG_CORE, "FLOODING: %s!%s@%s", u->nick, u->username, u->hostname);
		ssvskill_cmd (u->nick, "%s!%s (Flooding Services.)", me.host, s_Services);
		return 1;
	} else {
		u->flood++;
	}
	return 0;
}

/** @brief Display NeoStats version info
 *
 * 
 *
 * @return 
 */
void
do_version (const char* nick, const char *remoteserver)
{
	SET_SEGV_LOCATION();
	numeric (RPL_VERSION, nick, "%s :%s -> %s %s", me.versionfull, me.name, version_date, version_time);
	ModulesVersion (nick, remoteserver);
}

/** @brief Display our MOTD Message of the Day from the external neostats.motd file 
 *
 * 
 *
 * @return 
 */
void
do_motd (const char* nick, const char *remoteserver)
{
	FILE *fp;
	char buf[BUFSIZE];

	SET_SEGV_LOCATION();
	fp = fopen (MOTD_FILENAME, "r");
	if(!fp) {
		numeric (ERR_NOMOTD, nick, "MOTD File is missing");
	} else {
		numeric (RPL_MOTDSTART, nick, ":- %s Message of the Day -", me.name);
		numeric (RPL_MOTD, nick, ":- %s. Copyright (c) 1999 - 2004 The NeoStats Group", me.versionfull);
		numeric (RPL_MOTD, nick, ":-");

		if (fp) {
			while (fgets (buf, sizeof (buf), fp)) {
				buf[strnlen (buf, BUFSIZE) - 1] = 0;
				numeric (RPL_MOTD, nick, ":- %s", buf);
			}
			fclose (fp);
		} else {
			numeric (RPL_MOTD, nick, ":- MOTD file Missing");
		}
		numeric (RPL_ENDOFMOTD, nick, ":End of MOTD command.");
	}
}

/** @brief Display the ADMIN Message from the external stats.admin file
 *
 * 
 *
 * @return 
 */
void
do_admin (const char* nick, const char *remoteserver)
{
	FILE *fp;
	char buf[BUFSIZE];
	SET_SEGV_LOCATION();

	fp = fopen (ADMIN_FILENAME, "r");
	if(!fp) {
		numeric (ERR_NOADMININFO, nick, "%s :No administrative info available", me.name);
	} else {
		numeric (RPL_ADMINME, nick, ":%s :Administrative info", me.name);
		numeric (RPL_ADMINME, nick, ":%s.  Copyright (c) 1999 - 2004 The NeoStats Group", me.versionfull);
		if (fp) {
			while (fgets (buf, sizeof (buf), fp)) {
				buf[strnlen (buf, BUFSIZE) - 1] = 0;
				numeric (RPL_ADMINLOC1, nick, ":- %s", buf);
			}
			fclose (fp);
		}
		numeric (RPL_ADMINLOC2, nick, "End of /ADMIN command.");
	}
}

/** @brief 
 *
 * 
 *
 * @return 
 */
void
do_credits (const char* nick, const char *remoteserver)
{
	SET_SEGV_LOCATION();
	numeric (RPL_VERSION, nick, ":- NeoStats %s Credits ", me.versionfull);
	numeric (RPL_VERSION, nick, ":- Now Maintained by Shmad (shmad@neostats.net) and ^Enigma^ (enigma@neostats.net)");
	numeric (RPL_VERSION, nick, ":- For Support, you can find ^Enigma^ or Shmad at");
	numeric (RPL_VERSION, nick, ":- irc.irc-chat.net #NeoStats");
	numeric (RPL_VERSION, nick, ":- Thanks to:");
	numeric (RPL_VERSION, nick, ":- Enigma for being part of the dev team");
	numeric (RPL_VERSION, nick, ":- Stskeeps for writing the best IRCD ever!");
	numeric (RPL_VERSION, nick, ":- chrisv@b0rked.dhs.org for the Code for Dynamically Loading Modules (Hurrican IRCD)");
	numeric (RPL_VERSION, nick, ":- monkeyIRCD for the Module Segv Catching code");
	numeric (RPL_VERSION, nick, ":- the Users of Global-irc.net and Dreaming.org for being our Guinea Pigs!");
	numeric (RPL_VERSION, nick, ":- Andy For Ideas");
	numeric (RPL_VERSION, nick, ":- HeadBang for BetaTesting, and Ideas, And Hassling us for Beta Copies");
	numeric (RPL_VERSION, nick, ":- sre and Jacob for development systems and access");
	numeric (RPL_VERSION, nick, ":- Error51 for Translating our FAQ and README files");
	numeric (RPL_VERSION, nick, ":- users and opers of irc.irc-chat.net/org for putting up with our constant coding crashes!");
	numeric (RPL_VERSION, nick, ":- Eggy for proving to use our code still had bugs when we thought it didn't (and all the bug reports!)");
	numeric (RPL_VERSION, nick, ":- Hwy - Helping us even though he also has a similar project, and providing solaris porting tips :)");
	numeric (RPL_VERSION, nick, ":- M - Updating lots of Doco and code and providing lots of great feedback");
	numeric (RPL_VERSION, nick, ":- J Michael Jones - Giving us Patches to support QuantumIRCd");
	numeric (RPL_VERSION, nick, ":- Blud - Giving us patches for Mystic IRCd");
	numeric (RPL_VERSION, nick, ":- herrohr - Giving us patches for Liquid IRCd support");
	numeric (RPL_VERSION, nick, ":- OvErRiTe - Giving us patches for Viagra IRCd support");
	numeric (RPL_VERSION, nick, ":- Reed Loden - Contributions to IRCu support");
}

/** @brief 
 *
 * 
 *
 * @return 
 */
void
do_stats (const char* nick, const char *what)
{
	time_t tmp;
	time_t tmp2;
	int i;
#ifdef EXTAUTH
	int dl;
	int (*listauth) (User * u);
#endif
	User *u;

	SET_SEGV_LOCATION();
	u = finduser (nick);
	if (!u) {
		nlog (LOG_WARNING, LOG_CORE, "do_stats: message from unknown user %s", nick);
		return;
	}
	if (!ircstrcasecmp (what, "u")) {
		/* server uptime - Shmad */
		int uptime = me.now - me.t_start;
		numeric (RPL_STATSUPTIME, u->nick, "Statistical Server up %d days, %d:%02d:%02d", uptime / 86400, (uptime / 3600) % 24, (uptime / 60) % 60, uptime % 60);
	} else if (!ircstrcasecmp (what, "c")) {
		/* Connections */
		numeric (RPL_STATSNLINE, u->nick, "N *@%s * * %d 50", me.uplink, me.port);
		numeric (RPL_STATSCLINE, u->nick, "C *@%s * * %d 50", me.uplink, me.port);
	} else if (!ircstrcasecmp (what, "o")) {
		/* Operators */
#ifdef EXTAUTH
		dl = get_dl_handle ("extauth");
		if (dl > 0) {
			listauth = ns_dlsym ((int *) dl, "__list_auth");
			if (listauth)
				(*listauth) (u);
		} else
#endif
			numeric (RPL_STATSOLINE, u->nick, "Operators think they are God, but you and I know they are not!");
	} else if (!ircstrcasecmp (what, "l")) {
		/* Port Lists */
		tmp = me.now - me.lastmsg;
		tmp2 = me.now - me.t_start;
		numeric (RPL_STATSLINKINFO, u->nick, "l SendQ SendM SendBytes RcveM RcveBytes Open_Since CPU :IDLE");
		numeric (RPL_STATSLLINE, u->nick, "%s 0 %d %d %d %d %d 0 :%d", me.uplink, (int)me.SendM, (int)me.SendBytes, (int)me.RcveM, (int)me.RcveBytes, (int)tmp2, (int)tmp);
	} else if (!ircstrcasecmp (what, "M")) {
		for (i = 0; i < ircd_cmdcount; i++) {
			if (cmd_list[i].usage > 0)
				numeric (RPL_STATSCOMMANDS, u->nick, "Command %s Usage %d", cmd_list[i].name, cmd_list[i].usage);
		}
	} else if (!ircstrcasecmp(what, "Z")) {
		if (UserLevel(u) >= NS_ULEVEL_ADMIN) {
			do_dns_stats_Z(u);
		}
	}
	numeric (RPL_ENDOFSTATS, u->nick, "%s :End of /STATS report", what);
	chanalert (s_Services, "%s Requested Stats %s", u->nick, what);
};

void
do_protocol (char *origin, char **argv, int argc)
{
	int i;

	ircd_srv.unkline = 0;
	for (i = 0; i < argc; i++) {
#ifdef GOTTOKENSUPPORT
		if (!ircstrcasecmp ("TOKEN", argv[i])) {
			ircd_srv.token = 1;
		}
#endif
#ifdef GOTCLIENTSUPPORT
		if (!ircstrcasecmp ("CLIENT", argv[i])) {
			me.client = 1;
		}
#endif
#if defined(HYBRID7)
		if (!ircstrcasecmp ("UNKLN", argv[i])) {
			ircd_srv.unkline = 1;
		}
#endif
#ifdef GOTNOQUITSUPPORT
		if (!ircstrcasecmp ("NOQUIT", argv[i])) {
			ircd_srv.noquit = 1;
		}
#endif
#ifdef UNREAL32
		if (!ircstrcasecmp ("NICKIP", argv[i])) {
			ircd_srv.nickip = 1;
		}
#endif
	}
}

void
privmsg_list (char *to, char *from, const char **text)
{
	while (*text) {
		if (**text)
			prefmsg (to, from, (char*)*text);
		else
			prefmsg (to, from, " ");
		text++;
	}
}

void
chanalert (char *from, char *fmt, ...)
{
	va_list ap;

	if (!me.onchan)
		return;

	va_start (ap, fmt);
	ircvsnprintf (ircd_buf, BUFSIZE, fmt, ap);
	va_end (ap);
	send_privmsg (from, me.chan, ircd_buf);
}

void
prefmsg (char *to, const char *from, char *fmt, ...)
{
	va_list ap;

	if (findbot (to)) {
		chanalert (s_Services, "Message From our Bot(%s) to Our Bot(%s), Dropping Message", from, to);
		return;
	}

	va_start (ap, fmt);
	ircvsnprintf (ircd_buf, BUFSIZE, fmt, ap);
	va_end (ap);
	if (me.want_privmsg) {
		send_privmsg (from, to, ircd_buf);
	} else {
		send_notice (from, to, ircd_buf);
	}
}

void
privmsg (char *to, const char *from, char *fmt, ...)
{
	va_list ap;

	if (findbot (to)) {
		chanalert (s_Services, "Message From our Bot(%s) to Our Bot(%s), Dropping Message", from, to);
		return;
	}

	va_start (ap, fmt);
	ircvsnprintf (ircd_buf, BUFSIZE, fmt, ap);
	va_end (ap);
	send_privmsg (from, to, ircd_buf);
}

void
notice (char *to, const char *from, char *fmt, ...)
{
	va_list ap;

	if (findbot (to)) {
		chanalert (s_Services, "Message From our Bot(%s) to Our Bot(%s), Dropping Message", from, to);
		return;
	}

	va_start (ap, fmt);
	ircvsnprintf (ircd_buf, BUFSIZE, fmt, ap);
	va_end (ap);
	send_notice (from, to, ircd_buf);
}

void
globops (char *from, char *fmt, ...)
{
	va_list ap;

	va_start (ap, fmt);
	ircvsnprintf (ircd_buf, BUFSIZE, fmt, ap);
	va_end (ap);

	if (me.onchan) {
		send_globops(from, ircd_buf);
	} else {
		nlog (LOG_NORMAL, LOG_CORE, ircd_buf);
	}
}

int
wallops (const char *from, const char *msg, ...)
{
	va_list ap;

	va_start (ap, msg);
	ircvsnprintf (ircd_buf, BUFSIZE, msg, ap);
	va_end (ap);
	send_wallops ((char*)from, (char*)ircd_buf);
	return NS_SUCCESS;
}

int
numeric (const int numeric, const char *target, const char *data, ...)
{
	va_list ap;

	va_start (ap, data);
	ircvsnprintf (ircd_buf, BUFSIZE, data, ap);
	va_end (ap);
	send_numeric (me.name, numeric, target, ircd_buf);
	return NS_SUCCESS;
}

void
unsupported_cmd(const char* cmd)
{
	chanalert (s_Services, "Warning, %s tried to %s which is not supported", ((segvinmodule[0] != 0)? segvinmodule : ""), cmd);
	nlog (LOG_NOTICE, LOG_CORE, "Warning, %s tried to %s, which is not supported", ((segvinmodule[0] != 0)? segvinmodule : ""), cmd);
}

int
sumode_cmd (const char *who, const char *target, long mode)
{
	char* newmode;
	
	newmode = UmodeMaskToString(mode);
	send_umode (who, target, newmode);
	UserMode (target, newmode);
	return NS_SUCCESS;
}

int
spart_cmd (const char *who, const char *chan)
{
	send_part(who, chan);
	part_chan (finduser (who), (char *) chan, NULL);
	return NS_SUCCESS;
}

int
snick_cmd (const char *oldnick, const char *newnick)
{
	UserNick (oldnick, newnick, NULL);
	send_nickchange (oldnick, newnick, me.now);
	return NS_SUCCESS;
}

#ifdef IRCU
int 
scmode_op (const char *who, const char *chan, const char *mode, const char *bot)
{
	char **av;
	int ac;

	send_cmode (me.name, who, chan, mode, nicktobase64 (bot), me.now);
	ircsnprintf (ircd_buf, BUFSIZE, "%s %s %s", chan, mode, bot);
	ac = split_buf (ircd_buf, &av, 0);
	ChanMode (me.name, av, ac);
	free (av);
	return NS_SUCCESS;
}
#endif

int
schmode_cmd (const char *who, const char *chan, const char *mode, const char *args)
{
	char **av;
	int ac;

	send_cmode (me.name, who, chan, mode, args, me.now);
	ircsnprintf (ircd_buf, BUFSIZE, "%s %s %s", chan, mode, args);
	ac = split_buf (ircd_buf, &av, 0);
	ChanMode (me.name, av, ac);
	free (av);
	return NS_SUCCESS;
}

int
squit_cmd (const char *who, const char *quitmsg)
{
	send_quit (who, quitmsg);
	do_quit (who, quitmsg);
	return NS_SUCCESS;
}

int
skill_cmd (const char *from, const char *target, const char *reason, ...)
{
	va_list ap;

	va_start (ap, reason);
	ircvsnprintf (ircd_buf, BUFSIZE, reason, ap);
	va_end (ap);
	send_kill (from, target, ircd_buf);
	do_quit (target, ircd_buf);
	return NS_SUCCESS;
}

int
ssvskill_cmd (const char *target, const char *reason, ...)
{
	va_list ap;

	va_start (ap, reason);
	ircvsnprintf (ircd_buf, BUFSIZE, reason, ap);
	va_end (ap);
#ifdef GOTSVSKILL
	send_svskill (me.name, target, ircd_buf);
#else
	send_kill (me.name, target, ircd_buf);
	do_quit (target, ircd_buf);
#endif
	return NS_SUCCESS;
}

#ifdef GOTSVSTIME
int 
ssvstime_cmd (const time_t ts)
{
	send_svstime(me.name, (unsigned long)ts);
	nlog (LOG_NOTICE, LOG_CORE, "ssvstime_cmd: synching server times to %lu", ts);
	return NS_SUCCESS;
}
#endif

int
skick_cmd (const char *who, const char *chan, const char *target, const char *reason)
{
	send_kick (who, chan, target, reason);
	part_chan (finduser (target), (char *) chan, reason[0] != 0 ? (char *)reason : NULL);
	return NS_SUCCESS;
}

int 
sinvite_cmd (const char *from, const char *to, const char *chan) 
{
	send_invite(from, to, chan);
	return NS_SUCCESS;
}

int
ssvsmode_cmd (const char *target, const char *modes)
{
#ifdef GOTSVSMODE
	User *u;

	u = finduser (target);
	if (!u) {
		nlog (LOG_WARNING, LOG_CORE, "ssvsmode_cmd: can't find user %s", target);
		return 0;
	}
	send_svsmode(me.name, target, modes);
	UserMode (target, modes);
#else
	unsupported_cmd("SVSMODE");
#endif
	return NS_SUCCESS;
}

int
ssvshost_cmd (const char *who, const char *vhost)
{
#ifdef GOTSVSHOST 
	User *u;

	u = finduser (who);
	if (!u) {
		nlog (LOG_WARNING, LOG_CORE, "ssvshost_cmd: can't find user %s", who);
		return 0;
	}

	strlcpy (u->vhost, vhost, MAXHOST);
	send_svshost(me.name, who, vhost);
#else
	unsupported_cmd("SVSHOST");
#endif
	return NS_SUCCESS;
}

int
ssvsjoin_cmd (const char *target, const char *chan)
{
#ifdef GOTSVSJOIN 
	send_svsjoin (me.name, target, chan);
#else
	unsupported_cmd("SVSJOIN");
#endif
	return NS_SUCCESS;
}

int
ssvspart_cmd (const char *target, const char *chan)
{
#ifdef GOTSVSPART
	send_svspart (me.name, target, chan);
#else
	unsupported_cmd("SVSPART");
#endif
	return NS_SUCCESS;
}

int
sswhois_cmd (const char *target, const char *swhois)
{
#ifdef GOTSWHOIS
	send_swhois (me.name, target, swhois);
#else
	unsupported_cmd("SWHOIS");
#endif
	return NS_SUCCESS;
}

int
ssvsnick_cmd (const char *target, const char *newnick)
{
#ifdef GOTSVSNICK
	send_svsnick (me.name, target, newnick, me.now);
#else
	notice (s_Services, "Warning Module %s tried to SVSNICK, which is not supported", segvinmodule);
	nlog (LOG_NOTICE, LOG_CORE, "Warning. Module %s tried to SVSNICK, which is not supported", segvinmodule);
#endif
	return NS_SUCCESS;
}

int
ssmo_cmd (const char *from, const char *umodetarget, const char *msg)
{
#ifdef GOTSMO
	send_smo (from, umodetarget, msg);
#else
	unsupported_cmd("SMO");
#endif
	return NS_SUCCESS;
}

int
sakill_cmd (const char *host, const char *ident, const char *setby, const int length, const char *reason, ...)
{
	va_list ap;

	va_start (ap, reason);
	ircvsnprintf (ircd_buf, BUFSIZE, reason, ap);
	va_end (ap);
	send_akill(me.name, host, ident, setby, length, ircd_buf, me.now);
	return NS_SUCCESS;
}

int
srakill_cmd (const char *host, const char *ident)
{
	send_rakill (me.name, host, ident);
	return NS_SUCCESS;
}

int
ssjoin_cmd (const char *who, const char *chan, unsigned long chflag)
{
	char flag;
	char mode;
	char **av;
	int ac;
	time_t ts;
	Chans *c;

	c = findchan ((char *) chan);
	if (!c) {
		ts = me.now;
	} else {
		ts = c->tstime;
	}
	switch (chflag) {
#ifdef CMODE_FL_CHANOP
	case CMODE_CHANOP:
		flag = CMODE_FL_CHANOP;
		mode= CMODE_CH_CHANOP;
		break;
#endif
#ifdef CMODE_FL_VOICE
	case CMODE_VOICE:
		flag = CMODE_FL_VOICE;
		mode= CMODE_CH_VOICE;
		break;
#endif
#ifdef CMODE_FL_CHANOWNER
    case CMODE_CHANOWNER:
        flag = CMODE_FL_CHANOWNER;
        mode= CMODE_CH_CHANOWNER;
        break;
#endif
#ifdef CMODE_FL_CHANPROT
    case CMODE_CHANPROT:
        flag = CMODE_FL_CHANPROT;
        mode= CMODE_CH_CHANPROT;
        break;
#endif
#ifdef CMODE_FL_VIP
    case CMODE_VIP:
		flag = CMODE_FL_VIP;
		mode= CMODE_CH_VIP;
		break;
#endif
#ifdef CMODE_FL_HALFOP
    case CMODE_HALFOP:
        flag = CMODE_FL_HALFOP;
        mode= CMODE_CH_HALFOP;
        break;
#endif
#ifdef CMODE_FL_UOP
    case CMODE_UOP:
        flag = CMODE_FL_UOP;
        mode= CMODE_CH_UOP;
        break;
#endif
#ifndef FAKE_CMODE_CHANADMIN
#ifdef CMODE_FL_CHANADMIN
	case CMODE_CHANADMIN:
		flag = CMODE_FL_CHANADMIN;
		mode= CMODE_CH_CHANADMIN;
		break;
#endif
#endif
#ifdef CMODE_SILENCE
	case CMODE_FL_SILENCE:
		flag = CMODE_FL_SILENCE;
		mode= CMODE_CH_SILENCE;
		break;
#endif
	default:
		flag = ' ';
		mode= '\0';
	}
	if (mode == 0) {
		ircsnprintf (ircd_buf, BUFSIZE, "%s", who);
	} else {
		ircsnprintf (ircd_buf, BUFSIZE, "%c%s", flag, who);
	}
	send_sjoin (me.name, ircd_buf, chan, (unsigned long)ts);
	join_chan (who, chan);
	ircsnprintf (ircd_buf, BUFSIZE, "%s +%c %s", chan, mode, who);
	ac = split_buf (ircd_buf, &av, 0);
	ChanMode (me.name, av, ac);
	free (av);
	return NS_SUCCESS;
}

int
sjoin_cmd (const char *who, const char *chan)
{
	send_join (me.name, who, chan, me.now);
	join_chan (who, chan);
	return NS_SUCCESS;
}

int
sping_cmd (const char *from, const char *reply, const char *to)
{
	send_ping (from, reply, to);
	return NS_SUCCESS;
}

int
spong_cmd (const char *reply)
{
	send_pong (reply);
	return NS_SUCCESS;
}

int
sserver_cmd (const char *name, const int numeric, const char *infoline)
{
	send_server (me.name, name, numeric, infoline);
	return NS_SUCCESS;
}

int
ssquit_cmd (const char *server, const char *quitmsg)
{
	send_squit (server, quitmsg);
	return NS_SUCCESS;
}

int
snewnick_cmd (const char *nick, const char *ident, const char *host, const char *realname, long mode)
{
	char* newmode;
	
	newmode = UmodeMaskToString(mode);
	AddUser (nick, ident, host, realname, me.name, NULL, NULL, NULL);
	send_nick (nick, (unsigned long)me.now, newmode, ident, host, me.name, realname);
#if defined(ULTIMATE3) || defined(BAHAMUT) || defined(HYBRID7) || defined(IRCU) || defined(NEOIRCD) || defined(QUANTUM) || defined(LIQUID)
	UserMode (nick, newmode);
#else
	sumode_cmd (nick, nick, mode);
#endif
	return NS_SUCCESS;
}

/* SJOIN <TS> #<channel> <modes> :[@][+]<nick_1> ...  [@][+]<nick_n> */
void 
do_sjoin (char* tstime, char* channame, char *modes, char *sjoinnick, char **argv, int argc)
{
	char nick[MAXNICK];
	char* nicklist;
	int modeexists;
	long mode = 0;
	int ok = 1, i, j = 3;
	ModesParm *m;
	Chans *c;
	lnode_t *mn = NULL;
	char **param;
	int paramcnt = 0;
	int paramidx = 0;

	if (*modes == '#') {
		join_chan (sjoinnick, modes);
		return;
	}

	paramcnt = split_buf(argv[argc-1], &param, 0);
		   
	while (paramcnt > paramidx) {
		nicklist = param[paramidx];
#ifdef UNREAL
		/* Unreal passes +b(&) and +e(") via SJ3 so skip them for now */	
		if(*nicklist == '&' || *nicklist == '"') {
			nlog (LOG_DEBUG1, LOG_CORE, "Skipping %s", nicklist);
			paramidx++;
			continue;
		}
#endif
		mode = 0;
		while (ok == 1) {
			for (i = 0; i < ircd_cmodecount; i++) {
				if (chan_modes[i].sjoin != 0) {
					if (*nicklist == chan_modes[i].sjoin) {
						mode |= chan_modes[i].mode;
						nicklist++;
						i = -1;
					}
				} else {
					/* sjoin's should be at the top of the list */
					ok = 0;
					strlcpy (nick, nicklist, MAXNICK);
					break;
				}
			}
		}
		join_chan (nick, channame); 
		ChanUserMode (channame, nick, 1, mode);
		paramidx++;
		ok = 1;
	}
	c = findchan (channame);
	if(c) {
		/* update the TS time */
		SetChanTS (c, atoi (tstime)); 
		if (*modes == '+') {
			while (*modes) {
				for (i = 0; i < ircd_cmodecount; i++) {
					if (*modes == chan_modes[i].flag) {
						if (chan_modes[i].parameters) {
							mn = list_first (c->modeparms);
							modeexists = 0;
							while (mn) {
								m = lnode_get (mn);
								/* mode limit and mode key replace current values */
								if ((m->mode == CMODE_LIMIT) && (chan_modes[i].mode == CMODE_LIMIT)) {
									strlcpy (m->param, argv[j], PARAMSIZE);
									j++;
									modeexists = 1;
									break;
								} else if ((m->mode == CMODE_KEY) && (chan_modes[i].mode == CMODE_KEY)) {
									strlcpy (m->param, argv[j], PARAMSIZE);
									j++;
									modeexists = 1;
									break;
								} else if (((int *) m->mode == (int *) chan_modes[i].mode) && !ircstrcasecmp (m->param, argv[j])) {
									nlog (LOG_INFO, LOG_CORE, "ChanMode: Mode %c (%s) already exists, not adding again", chan_modes[i].flag, argv[j]);
									j++;
									modeexists = 1;
									break;
								}
								mn = list_next (c->modeparms, mn);
							}
							if (modeexists != 1) {
								m = smalloc (sizeof (ModesParm));
								m->mode = chan_modes[i].mode;
								strlcpy (m->param, argv[j], PARAMSIZE);
								mn = lnode_create (m);
								if (list_isfull (c->modeparms)) {
									nlog (LOG_CRITICAL, LOG_CORE, "ChanMode: modelist is full adding to channel %s", c->name);
									do_exit (NS_EXIT_ERROR, "List full - see log file");
								} else {
									list_append (c->modeparms, mn);
								}
								j++;
							}
						} else {
							c->modes |= chan_modes[i].mode;
						}
					}
				}
				modes++;
			}
		}
	}
	free(param);
}

#ifdef MSG_NETINFO
void 
do_netinfo(const char* maxglobalcnt, const char* tsendsync, const char* prot, const char* cloak, const char* netname)
{
	ircd_srv.maxglobalcnt = atoi (maxglobalcnt);
	ircd_srv.tsendsync = atoi (tsendsync);
	ircd_srv.uprot = atoi (prot);
	strlcpy (ircd_srv.cloak, cloak, CLOAKKEYLEN);
	strlcpy (me.netname, netname, MAXPASS);
	send_netinfo (me.name, ircd_srv.uprot, ircd_srv.cloak, me.netname, me.now);
	init_services_bot ();
	globops (me.name, "Link with Network \2Complete!\2");
	ModuleEvent (EVENT_NETINFO, NULL, 0);
	me.synced = 1;
}
#endif

#ifdef MSG_SNETINFO
void 
do_snetinfo(const char* maxglobalcnt, const char* tsendsync, const char* prot, const char* cloak, const char* netname)
{
	ircd_srv.uprot = atoi (prot);
	strlcpy (ircd_srv.cloak, cloak, CLOAKKEYLEN);
	strlcpy (me.netname, netname, MAXPASS);
	send_snetinfo (me.name, ircd_srv.uprot, ircd_srv.cloak, me.netname, me.now);
	init_services_bot ();
	globops (me.name, "Link with Network \2Complete!\2");
	ModuleEvent (EVENT_NETINFO, NULL, 0);
	me.synced = 1;
}
#endif

void
do_join (const char* nick, const char* chanlist, const char* keys)
{
	char *s, *t;
	t = (char*)chanlist;
	while (*(s = t)) {
		t = s + strcspn (s, ",");
		if (*t)
			*t++ = 0;
		join_chan (nick, s);
	}
}

void 
do_part (const char* nick, const char* chan, const char* reason)
{
	part_chan (finduser (nick), chan, reason);
}

void 
do_nick (const char *nick, const char *hopcount, const char* TS, 
		 const char *user, const char *host, const char *server, 
		 const char *ip, const char *servicestamp, const char *modes, 
		 const char *vhost, const char *realname, const char *numeric
#ifdef GOTUSERSMODES
		 , const char *smodes
#endif
		 )
{
	AddUser (nick, user, host, realname, server, ip, TS, numeric);
	if(modes) {
		UserMode (nick, modes);
	}
	if(vhost) {
		SetUserVhost(nick, vhost);
	}
#ifdef GOTUSERSMODES
	if(smodes) {
		UserSMode (nick, smodes);
	}
#endif		
}

void 
do_client (const char *nick, const char *arg1, const char *TS, 
		const char *modes, const char *smodes, 
		const char *user, const char *host, const char *vhost, 
		const char *server, const char *arg9, 
		const char *ip, const char *realname)
{
	AddUser (nick, user, host, realname, server, ip, TS, NULL);
	if(modes) {
		UserMode (nick, modes);
	}
	if(vhost) {
		SetUserVhost(nick, vhost);
	}
#ifdef GOTUSERSMODES
	if(smodes) {
		UserSMode (nick, smodes);
	}
#endif		
}

void
do_kill (const char *nick, const char *reason)
{
	DelUser (nick, 1, reason);
}

void
do_quit (const char *nick, const char *quitmsg)
{
	DelUser (nick, 0, quitmsg);
}

void 
do_squit(const char *name, const char* reason)
{
	DelServer (name, reason);
}

void
do_kick (const char *kickby, const char *chan, const char *kicked, const char *kickreason)
{
	kick_chan (kickby, chan, kicked, kickreason);
}

#ifdef MSG_SVINFO
void 
do_svinfo (void)
{
	send_svinfo (TS_CURRENT, TS_MIN, (unsigned long)me.now);
}
#endif

#ifdef MSG_VCTRL
void 
do_vctrl (const char* uprot, const char* nicklen, const char* modex, const char* gc, const char* netname)
{
	ircd_srv.uprot = atoi(uprot);
	ircd_srv.nicklen = atoi(nicklen);
	ircd_srv.modex = atoi(modex);
	ircd_srv.gc = atoi(gc);
	strlcpy (me.netname, netname, MAXPASS);
	send_vctrl (ircd_srv.uprot, ircd_srv.nicklen, ircd_srv.modex, ircd_srv.gc, me.netname);
}
#endif

#ifdef GOTUSERSMODES
void 
do_smode (const char* nick, const char* modes)
{
	UserSMode (nick, modes);
}
#endif

void 
do_mode_user (const char* nick, const char* modes)
{
	UserMode (nick, modes);
}

void 
do_svsmode_user (const char* nick, const char* modes, const char* ts)
{
	char modebuf[MODESIZE];
	
	if (ts && isdigit(*ts)) {
		const char* pModes;	
		char* pNewModes;	

		SetUserServicesTS (nick, ts);
		/* If only setting TS, we do not need further mode processing */
		if(strcasecmp(modes, "+d") == 0) {
			nlog (LOG_DEBUG3, LOG_CORE, "dropping modes since this is a services TS %s", modes);
			return;
		}
		/* We need to strip the d from the mode string */
		pNewModes = modebuf;
		pModes = modes;
		while(*pModes) {
			if(*pModes != 'd') {
				*pNewModes = *pModes;
			}
			pModes++;
			pNewModes++;			
		}
		/* NULL terminate */
		*pNewModes = 0;
		UserMode (nick, modebuf);
	} else {
		UserMode (nick, modes);
	}
}

void 
do_mode_channel (char *origin, char **argv, int argc)
{
	ChanMode (origin, argv, argc);
}

void 
do_away (const char* nick, const char *reason)
{
	UserAway (nick, reason);
}

void 
do_vhost (const char* nick, const char *vhost)
{
	SetUserVhost(nick, vhost);
}

void
do_nickchange (const char * oldnick, const char *newnick, const char * ts)
{
	UserNick (oldnick, newnick, ts);
}

void 
do_topic (const char* chan, const char *owner, const char* ts, const char *topic)
{
	ChanTopic (chan, owner, ts, topic);
}

void 
do_server (const char *name, const char *uplink, const char* hops, const char *numeric, const char *infoline, int srv)
{
	if(srv == 0) {
		if (uplink == NULL || *uplink == 0) {
			me.s = AddServer (name, me.name, hops, numeric, infoline);
		} else {
			me.s = AddServer (name, uplink, hops, numeric, infoline);
		}
	} else {
		AddServer (name, uplink, hops, numeric, infoline);
	}
	
}

#ifndef IRCU
#ifdef MSG_BURST
void 
do_burst (char *origin, char **argv, int argc)
{
	if (argc > 0) {
		if (ircd_srv.burst == 1) {
			send_burst (0);
			ircd_srv.burst = 0;
			me.synced = 1;
			init_services_bot ();
		}
	} else {
		ircd_srv.burst = 1;
	}
}
#endif
#endif

#ifdef MSG_SWHOIS
void 
do_swhois (char *who, char *swhois)
{
	User* u;
	u = finduser(who);
	if(u) {
		strlcpy(u->swhois, swhois, MAXHOST);
	}
}
#endif

#ifdef MSG_TKL
void 
do_tkl(const char *add, const char *type, const char *user, const char *host, const char *setby, const char *tsexpire, const char *tsset, const char *reason)
{
	char mask[MAXHOST];
	ircsnprintf(mask, MAXHOST, "%s@%s", user, host);
	if(add[0] == '+') {
		AddBan(type, user, host, mask, reason, setby, tsset, tsexpire);
	} else {
		DelBan(type, user, host, mask, reason, setby, tsset, tsexpire);
	}
}
#endif

#ifdef MSG_EOS
void 
do_eos (const char *name)
{
	Server *s;

	s = findserver (name);
	if(s) {
		SynchServer(s);
		nlog (LOG_DEBUG1, LOG_CORE, "do_eos: server %s is now synched", name);
	} else {
		nlog (LOG_WARNING, LOG_CORE, "do_eos: server %s not found", name);
	}
}
#endif

void
send_cmd (char *fmt, ...)
{
	va_list ap;
	char buf[BUFSIZE];
	int buflen;
	
	va_start (ap, fmt);
	ircvsnprintf (buf, BUFSIZE, fmt, ap);
	va_end (ap);

	nlog (LOG_DEBUG2, LOG_CORE, "SENT: %s", buf);
	if(strnlen (buf, BUFSIZE) < BUFSIZE - 2) {
		strlcat (buf, "\n", BUFSIZE);
	} else {
		buf[BUFSIZE - 1] = 0;
		buf[BUFSIZE - 2] = '\n';
	}
	buflen = strnlen (buf, BUFSIZE);
	sts (buf, buflen);
}

#ifdef BASE64SERVERNAME

void
setserverbase64 (const char *name, const char* num)
{
	Server *s;

	s = findserver(name);
	if(s) {
		nlog (LOG_DEBUG1, LOG_CORE, "setserverbase64: setting %s to %s", name, num);
		strlcpy(s->name64, num, 6);
	} else {
		nlog (LOG_DEBUG1, LOG_CORE, "setserverbase64: cannot find %s for %s", name, num);
	}
}

char* 
servertobase64 (const char* name)
{
	Server *s;

	nlog (LOG_DEBUG1, LOG_CORE, "servertobase64: scanning for %s", name);
	s = findserver(name);
	if(s) {
		return s->name64;
	} else {
		nlog (LOG_DEBUG1, LOG_CORE, "servertobase64: cannot find %s", name);
	}
	return NULL;
}

char* 
base64toserver (const char* num)
{
	Server *s;

	nlog (LOG_DEBUG1, LOG_CORE, "base64toserver: scanning for %s", num);
	s = findserverbase64(num);
	if(s) {
		return s->name;
	} else {
		nlog (LOG_DEBUG1, LOG_CORE, "base64toserver: cannot find %s", num);
	}
	return NULL;
}

#endif

#ifdef BASE64NICKNAME
void
setnickbase64 (const char *nick, const char* num)
{
	User *u;

	u = finduser(nick);
	if(u) {
		nlog (LOG_DEBUG1, LOG_CORE, "setnickbase64: setting %s to %s", nick, num);
		strlcpy(u->nick64, num, B64SIZE);
	} else {
		nlog (LOG_DEBUG1, LOG_CORE, "setnickbase64: cannot find %s for %s", nick, num);
	}
}

char* 
nicktobase64 (const char* nick)
{
	User *u;

	nlog (LOG_DEBUG1, LOG_CORE, "nicktobase64: scanning for %s", nick);
	u = finduser(nick);
	if(u) {
		return u->nick64;
	} else {
		nlog (LOG_DEBUG1, LOG_CORE, "nicktobase64: cannot find %s", nick);
	}
	return NULL;
}

char* 
base64tonick (const char* num)
{
	User *u;

	nlog (LOG_DEBUG1, LOG_CORE, "base64tonick: scanning for %s", num);
	u = finduserbase64(num);
	if(u) {
		return u->nick;
	} else {
		nlog (LOG_DEBUG1, LOG_CORE, "base64tonick: cannot find %s", num);
	}
	return NULL;
}

#endif

