/************************************************************************
 *   IRC - Internet Relay Chat, common/parse.c
 *   Copyright (C) 1990 Jarkko Oikarinen and
 *                      University of Oulu, Computing Center
 *
 *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef lint
static  char rcsid[] = "@(#)$Id: parse.c,v 1.12 2004/12/08 00:23:30 skold Exp $";
#endif

#include "os.h"
#ifndef CLIENT_COMPILE
# include "s_defines.h"
#else
# include "c_defines.h"
#endif
#define PARSE_C
#ifndef CLIENT_COMPILE
# include "s_externs.h"
#else
# include "c_externs.h"
#endif
#undef PARSE_C

#ifndef CLIENT_COMPILE
#ifdef RUSNET_IRCD
#include "rusnet_cmds_ext.h"
#endif
#endif

struct Message msgtab[] = {
  { MSG_PRIVATE, m_private,  MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
#ifndef CLIENT_COMPILE
  { MSG_NJOIN,   m_njoin,    MAXPARA, MSG_LAG|MSG_REG|MSG_NOU, 0, 0, 0L},
#endif
  { MSG_JOIN,    m_join,     MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_MODE,    m_mode,     MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_NICK,    m_nick,     MAXPARA, MSG_LAG, 0, 0, 0L},
  { MSG_PART,    m_part,     MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_QUIT,    m_quit,     MAXPARA, MSG_LAG, 0, 0, 0L},
  { MSG_NOTICE,  m_notice,   MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_KICK,    m_kick,     MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_SERVER,  m_server,   MAXPARA, MSG_LAG|MSG_NOU, 0, 0, 0L},
#ifndef CLIENT_COMPILE
  { MSG_TRACE,   m_trace,    MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
#endif
  { MSG_TOPIC,   m_topic,    MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_INVITE,  m_invite,   MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_WALLOPS, m_wallops,  MAXPARA, MSG_LAG|MSG_REG|MSG_NOU, 0, 0, 0L},
  { MSG_PING,    m_ping,     MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_PONG,    m_pong,     MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_ERROR,   m_error,    MAXPARA, MSG_LAG|MSG_REG|MSG_NOU, 0, 0, 0L},
#ifndef CLIENT_COMPILE
#ifdef RUSNET_IRCD
  { MSG_CODEPAGE,m_codepage, MAXPARA, MSG_LAG, 0, 0, 0L},
  { MSG_FORCE,   m_force,    MAXPARA, MSG_LAG|MSG_REG|MSG_NOU, 0, 0, 0L},
  { MSG_RMODE,   m_rmode,    MAXPARA, MSG_LAG|MSG_REG|MSG_NOU, 0, 0, 0L},
#ifdef	OPER_RCPAGE
  { MSG_RCPAGE,  m_rcpage,   MAXPARA, MSG_LAG|MSG_REG|MSG_OP, 0, 0, 0L},
#else
  { MSG_RCPAGE,  m_rcpage,   MAXPARA, MSG_LAG|MSG_REG|MSG_NOU, 0, 0, 0L},
#endif
  { MSG_KLINE,   m_kline,    7, MSG_LAG|MSG_REG|MSG_NOU, 0, 0, 0L},
  { MSG_NICKSERV,m_nickserv, 1, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_CHANSERV,m_chanserv, 1, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_MEMOSERV,m_memoserv, 1, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_INFOSERV,m_infoserv, 1, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_STATSERV,m_statserv, 1, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_OPERSERV,m_operserv, 1, MSG_LAG|MSG_REG|MSG_OP, 0, 0, 0L},
#endif
#endif
#ifdef	OPER_KILL
  { MSG_KILL,    m_kill,     MAXPARA, MSG_LAG|MSG_REG|MSG_OP|MSG_LOP, 0,0, 0L},
#else
  { MSG_KILL,    m_kill,     MAXPARA, MSG_LAG|MSG_REG|MSG_NOU, 0, 0, 0L},
#endif
#ifndef CLIENT_COMPILE
  { MSG_USER,    m_user,     MAXPARA, MSG_LAG|MSG_NOU, 0, 0, 0L},
  { MSG_AWAY,    m_away,     MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_UMODE,   m_umode,    MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_ISON,    m_ison,     1,	 MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_SQUIT,   m_squit,    MAXPARA, MSG_LAG|MSG_REG|MSG_OP|MSG_LOP, 0,0, 0L},
  { MSG_WHOIS,   m_whois,    MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_WHO,     m_who,      MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_WHOWAS,  m_whowas,   MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_LIST,    m_list,     MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_NAMES,   m_names,    MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_USERHOST,m_userhost, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_PASS,    m_pass,     MAXPARA, MSG_LAG|MSG_NOU, 0, 0, 0L},
  { MSG_LUSERS,  m_lusers,   MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_TIME,    m_time,     MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_OPER,    m_oper,     MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_CONNECT, m_connect,  MAXPARA,
				MSG_LAG|MSG_REGU|MSG_OP|MSG_LOP, 0, 0, 0L},
  { MSG_VERSION, m_version,  MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_STATS,   m_stats,    MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_LINKS,   m_links,    MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_ADMIN,   m_admin,    MAXPARA, MSG_LAG, 0, 0, 0L},
  { MSG_USERS,   m_users,    MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_SUMMON,  m_summon,   MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_HELP,    m_help,     MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_INFO,    m_info,     MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_MOTD,    m_motd,     MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_CLOSE,   m_close,    MAXPARA, MSG_LAG|MSG_REGU|MSG_OP, 0, 0, 0L},
  { MSG_RECONECT,m_reconnect,MAXPARA, MSG_LAG|MSG_NOU, 0, 0, 0L},
  { MSG_SERVICE, m_service,  MAXPARA, MSG_LAG|MSG_NOU, 0, 0, 0L},
#ifdef	USE_SERVICES
  { MSG_SERVSET, m_servset,  MAXPARA, MSG_LAG|MSG_SVC, 0, 0, 0L},
#endif
  { MSG_SQUERY,  m_squery,   MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L},
  { MSG_SERVLIST,m_servlist, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_HASH,    m_hash,     MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
  { MSG_DNS,     m_dns,      MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L},
#ifdef	OPER_REHASH
  { MSG_REHASH,  m_rehash,   MAXPARA, MSG_REGU|MSG_OP
# ifdef	LOCOP_REHASH
					 |MSG_LOP
# endif
					, 0, 0, 0L},
#endif
#ifdef	OPER_RESTART
  { MSG_RESTART,  m_restart,   MAXPARA, MSG_REGU|MSG_OP
# ifdef	LOCOP_RESTART
					 |MSG_LOP
# endif
					, 0, 0, 0L},
#endif
#ifdef	OPER_DIE
  { MSG_DIE,  m_die,   MAXPARA, MSG_REGU|MSG_OP
# ifdef	LOCOP_DIE
					 |MSG_LOP
# endif
					, 0, 0, 0L},
#endif
#endif /* !CLIENT_COMPILE */
  { (char *) 0, (int (*)()) 0, 0, 0, 0, 0, 0L}
};

/*
 * NOTE: parse() should not be called recursively by other functions!
 */
static	char	*para[MAXPARA+1];

#ifdef	CLIENT_COMPILE
static	char	sender[NICKLEN+USERLEN+HOSTLEN+3];
char	userhost[USERLEN+HOSTLEN+2];
#define	timeofday	time(NULL)
#else
static	char	sender[HOSTLEN+1];
static	int	cancel_clients __P((aClient *, aClient *, char *));
static	void	remove_unknown __P((aClient *, char *));
#endif

/*
**  Find a client (server or user) by name.
**
**  *Note*
**	Semantics of this function has been changed from
**	the old. 'name' is now assumed to be a null terminated
**	string and the search is the for server and user.
*/
#ifndef CLIENT_COMPILE
aClient *find_client(name, cptr)
char	*name;
Reg	aClient *cptr;
    {
	aClient *acptr = cptr;

	if (name && *name)
		acptr = hash_find_client(name, cptr);

	return acptr;
    }

aClient *find_service(name, cptr)
char	*name;
Reg	aClient *cptr;
    {
	aClient *acptr = cptr;

	if (index(name, '@'))
		acptr = hash_find_client(name, cptr);
	return acptr;
    }

#else /* CLIENT_COMPILE */

aClient *find_client(name, cptr)
char *name;
aClient *cptr;
    {
	Reg	aClient	*c2ptr = cptr;

	if (!name || !*name)
		return c2ptr;

	for (c2ptr = client; c2ptr; c2ptr = c2ptr->next) 
		if (strcasecmp(name, c2ptr->name) == 0)
			return c2ptr;
	return cptr;
    }
#endif /* CLIENT_COMPILE */

/*
**  Find a user@host (server or user).
**
**  *Note*
**	Semantics of this function has been changed from
**	the old. 'name' is now assumed to be a null terminated
**	string and the search is the for server and user.
*/
aClient *find_userhost(user, host, cptr, count)
char	*user, *host;
aClient *cptr;
int	*count;
    {
	Reg	aClient	*c2ptr;
	Reg	aClient	*res = cptr;

	*count = 0;
	if (user)
		for (c2ptr = client; c2ptr; c2ptr = c2ptr->next) 
		    {
			if (!MyClient(c2ptr)) /* implies mine and a user */
				continue;
			if ((!host || !match(host, c2ptr->user->host)) &&
			     strcasecmp(user, c2ptr->user->username) == 0)
			    {
				(*count)++;
				res = c2ptr;
			    }
		    }
	return res;
    }

/*
**  Find server by name.
**
**	This implementation assumes that server and user names
**	are unique, no user can have a server name and vice versa.
**	One should maintain separate lists for users and servers,
**	if this restriction is removed.
**
**  *Note*
**	Semantics of this function has been changed from
**	the old. 'name' is now assumed to be a null terminated
**	string.
*/
#ifndef CLIENT_COMPILE
/*
** Find a server from hash table, given its name
*/
aClient *find_server(name, cptr)
char	*name;
Reg	aClient *cptr;
{
	aClient *acptr = cptr;

	if (name && *name)
		acptr = hash_find_server(name, cptr);
	return acptr;
}

/*
** Given a server name, find the server mask behind which the server
** is hidden.
*/
aClient *find_mask(name, cptr)
char	*name;
aClient *cptr;
{
	static	char	servermask[HOSTLEN+1];
	Reg	aClient	*c2ptr = cptr;
	Reg	char	*mask = servermask;

	if (!name || !*name)
		return c2ptr;
	if ((c2ptr = hash_find_server(name, cptr)))
		return (c2ptr);
	if (index(name, '*'))
		return c2ptr;
	strcpy (servermask, name);
	while (*mask)
	{
		if (*(mask+1) == '.')
		{
			*mask = '*';
			if ((c2ptr = hash_find_server(mask, cptr)))
				return (c2ptr);
		}
		mask++;
	}
	return (c2ptr ? c2ptr : cptr);
}

/*
** Find a server from hash table, given its token
*/
aServer	*find_tokserver(token, cptr, c2ptr)
int	token;
aClient	*cptr, *c2ptr;
{
	return hash_find_stoken(token, cptr, c2ptr);
}

/*
** Find a server, given its name (which might contain *'s, in which case
** the first match will be return [not the best one])
*/
aClient *find_name(name, cptr)
char	*name;
aClient *cptr;
{
	Reg	aClient	*c2ptr = cptr;
	Reg	aServer	*sp = NULL;

	if (!name || !*name)
		return c2ptr;

	if ((c2ptr = hash_find_server(name, cptr)))
		return (c2ptr);
	if (!index(name, '*'))
		return c2ptr;
	for (sp = svrtop; sp; sp = sp->nexts)
	    {
		/*
		** A server present in the list necessarily has a non NULL
		** bcptr pointer.
		*/
		if (match(name, sp->bcptr->name) == 0)
			break;
		if (index(sp->bcptr->name, '*'))
			if (match(sp->bcptr->name, name) == 0)
					break;
	    }
	return (sp ? sp->bcptr : cptr);
}
#else
aClient	*find_server(name, cptr)
char	*name;
aClient	*cptr;
{
	Reg	aClient *c2ptr = cptr;

	if (!name || !*name)
		return c2ptr;

	for (c2ptr = client; c2ptr; c2ptr = c2ptr->next)
	    {
		if (!IsServer(c2ptr) && !IsMe(c2ptr))
			continue;
		if (match(c2ptr->name, name) == 0 ||
		    match(name, c2ptr->name) == 0)
			break;
	    }
	return (c2ptr ? c2ptr : cptr);
}
#endif /* CLIENT_COMPILE */

/*
**  Find person by (nick)name.
*/
aClient *find_person(name, cptr)
char	*name;
aClient *cptr;
    {
	Reg	aClient	*c2ptr = cptr;

	c2ptr = find_client(name, c2ptr);

	if (c2ptr && IsClient(c2ptr) && c2ptr->user)
		return c2ptr;
	else
		return cptr;
    }

/*
 * parse a buffer.
 * Return values:
 *  errors: -3 for unknown origin/sender, -2 for FLUSH_BUFFER, -1 for bad cptr
 *
 * NOTE: parse() should not be called recusively by any other fucntions!
 */
int	parse(cptr, buffer, bufend)
aClient *cptr;
char	*buffer, *bufend;
    {
	Reg	aClient *from = cptr;
	Reg	char	*ch, *s;
	Reg	int	len, i, numeric = 0, paramcount;
	Reg	struct	Message *mptr = NULL;
	int	ret;

#ifndef	CLIENT_COMPILE
	Debug((DEBUG_DEBUG, "Parsing %s: %s",
		get_client_name(cptr, FALSE), buffer));
#ifdef DEBUGMODE
	sendto_flag(SCH_LOCAL,
			"Parsing %s: %s",
		get_client_name(cptr, FALSE), buffer);
#endif /* DEBUG_MODE */
	if (IsDead(cptr))
		return -1;
#endif

	s = sender;
	*s = '\0';
	for (ch = buffer; *ch == ' '; ch++)
		;
	para[0] = from->name;
	if (*ch == ':')
	    {
		/*
		** Copy the prefix to 'sender' assuming it terminates
		** with SPACE (or NULL, which is an error, though).
		*/
		for (++ch, i = 0; *ch && *ch != ' '; ++ch )
			if (s < (sender + sizeof(sender)-1))
				*s++ = *ch; /* leave room for NULL */
		*s = '\0';
#ifdef CLIENT_COMPILE
		if ((s = index(sender, '!')))
		    {
			*s++ = '\0';
			strncpyzt(userhost, s, sizeof(userhost));
		    }
		else if ((s = index(sender, '@')))
		    {
			*s++ = '\0';
			strncpyzt(userhost, s, sizeof(userhost));
		    }
#endif
		/*
		** Actually, only messages coming from servers can have
		** the prefix--prefix silently ignored, if coming from
		** a user client...
		**
		** ...sigh, the current release "v2.2PL1" generates also
		** null prefixes, at least to NOTIFY messages (e.g. it
		** puts "sptr->nickname" as prefix from server structures
		** where it's null--the following will handle this case
		** as "no prefix" at all --msa  (": NOTICE nick ...")
		*/
		if (*sender && IsServer(cptr))
		    {
#ifndef	CLIENT_COMPILE
#ifdef RUSNET_IRCD
			   /**
			    ** it's time to look into collision maps
			    ** and find it there. -erra
			    ** Let's worry about collisions before any other
			    ** actions, or we can catch Fake Prefix otherwise -skold
			    */
			    char *z = find_collision(sender, cptr->serv->crc);
			    if (z)
			    {
				    from = find_client(z, (aClient *) NULL);
					if (!from)
					{
                				sendto_flag(SCH_HASH,
							"Collided client for nick %s not found, prefix not changed", z);
						from = find_client(sender, (aClient *) NULL);
					}
					else
					    if(! (from->flags & FLAGS_COLLMAP))
					    {
						    sendto_flag(SCH_HASH,
							    "FLAGS_COLLMAP for collided nick %s is not set", z);
						    /* 
						    ** Better to get rid of this here. -skold
						    **/
						    del_from_collision_map(sender, cptr->serv->crc);
					    }
			    }
			    else
#endif /* RUSNET_IRCD */
#endif /* CLIENT_COMPILE */
				from = find_client(sender, (aClient *) NULL);
			if (!from ||
			    /*
			    ** I really believe that the followin line is 
			    ** useless.  What a waste, especially with 2.9
			    ** hostmasks.. at least the test on from->name
			    ** will make it a bit better. -krys
			    */
			    (*from->name == '*' && match(from->name, sender)))
				from = find_server(sender, (aClient *)NULL);
#ifndef CLIENT_COMPILE
			/* Is there svc@server prefix ever? -Vesa */
			/* every time a service talks -krys */
			if (!from && index(sender, '@'))
				from = find_service(sender, (aClient *)NULL);
			if (!from)
				from = find_mask(sender, (aClient *) NULL);
#endif

			para[0] = sender;

			/* Hmm! If the client corresponding to the
			 * prefix is not found--what is the correct
			 * action??? Now, I will ignore the message
			 * (old IRC just let it through as if the
			 * prefix just wasn't there...) --msa
			 * Since 2.9 we pick them up and .. --Vesa
			 */
			if (!from)
			    {
				Debug((DEBUG_ERROR,
					"Unknown prefix (%s)(%s) from (%s)",
					sender, buffer, cptr->name));
				ircstp->is_unpf++;
#ifndef	CLIENT_COMPILE
				remove_unknown(cptr, sender);
#endif
				return -3;	/* Grab it in read_message() */
			    }
			if (from->from != cptr)
			    {
#if defined(RUSNET_IRCD) && !defined(CLIENT_COMPILE)
				/*
				** We need this kludge in order to avoid 
				** Fake Prefix for "invincible" clients.
				** Usually these fake messages are coming
				** for collided clients already killed at
				** the moment.  -slcio
				*/
				if (IsClient(from) && from->user &&
					(from->user->servp->crc == invincible)) {
					sendto_flag(SCH_HASH, 
					    "Ignoring Fake Prefix for %s: %s", 
					    from->name, buffer);
					return 1;
				}
#endif
				ircstp->is_wrdi++;
				Debug((DEBUG_ERROR,
					"Message (%s) coming from (%s)",
					buffer, cptr->name));
#ifndef	CLIENT_COMPILE
				return cancel_clients(cptr, from, buffer);
#else
				return -1;
#endif
			    }
		    }
		while (*ch == ' ')
			ch++;
	    }
	if (*ch == '\0')
	    {
		ircstp->is_empt++;
		Debug((DEBUG_NOTICE, "Empty message from host %s:%s",
		      cptr->name, from->name));
		return -1;
	    }
	/*
	** Extract the command code from the packet.  Point s to the end
	** of the command code and calculate the length using pointer
	** arithmetic.  Note: only need length for numerics and *all*
	** numerics must have paramters and thus a space after the command
	** code. -avalon
	*/
	s = (char *)index(ch, ' '); /* s -> End of the command code */
	len = (s) ? (s - ch) : 0;
	if (len == 3 &&
	    isdigit(*ch) && isdigit(*(ch + 1)) && isdigit(*(ch + 2)))
	    {
		numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') * 10
			+ (*(ch + 2) - '0');
		paramcount = MAXPARA;
		ircstp->is_num++;
	    }
	else
	    {
		if (s)
			*s++ = '\0';
		for (mptr = msgtab; mptr->cmd; mptr++) 
			if (strcasecmp(mptr->cmd, ch) == 0)
				break;

		if (!mptr->cmd)
		    {
			/*
			** Note: Give error message *only* to recognized
			** persons. It's a nightmare situation to have
			** two programs sending "Unknown command"'s or
			** equivalent to each other at full blast....
			** If it has got to person state, it at least
			** seems to be well behaving. Perhaps this message
			** should never be generated, though...  --msa
			** Hm, when is the buffer empty -- if a command
			** code has been found ?? -Armin
			*/
			if (buffer[0] != '\0')
			    {
				cptr->flags |= FLAGS_UNKCMD;
				if (IsPerson(from))
					sendto_one(from,
					    ":%s %d %s %s :Unknown command",
					    me.name, ERR_UNKNOWNCOMMAND,
					    from->name, ch);
#ifdef	CLIENT_COMPILE
				Debug((DEBUG_ERROR,"Unknown (%s) from %s[%s]",
					ch, cptr->name, cptr->sockhost));
#else
				else if (IsServer(cptr))
					sendto_flag(SCH_ERROR,
					    "Unknown command from %s:%s",
					    get_client_name(cptr, TRUE), ch);
				Debug((DEBUG_ERROR,"Unknown (%s) from %s",
					ch, get_client_name(cptr, TRUE)));
#endif
			    }
			ircstp->is_unco++;
			return -1;
		    }
		paramcount = mptr->parameters;
		i = bufend - ((s) ? s : ch);
		mptr->bytes += i;
#ifndef	CLIENT_COMPILE
		if ((mptr->flags & MSG_LAG) &&
		    !(IsServer(cptr) || IsService(cptr)))
		    {	/* Flood control partly migrated into penalty */
			if (bootopt & BOOT_PROT)
				cptr->since += (1 + i / 100);
			else
				cptr->since = timeofday;
			/* Allow only 1 msg per 2 seconds
			 * (on average) to prevent dumping.
			 * to keep the response rate up,
			 * bursts of up to 5 msgs are allowed
			 * -SRB
			 */
		    }
#endif
	    }
	/*
	** Must the following loop really be so devious? On
	** surface it splits the message to parameters from
	** blank spaces. But, if paramcount has been reached,
	** the rest of the message goes into this last parameter
	** (about same effect as ":" has...) --msa
	*/

	/* Note initially true: s==NULL || *(s-1) == '\0' !! */

#ifdef	CLIENT_COMPILE
	if (me.user)
		para[0] = sender;
#endif
	i = 0;
	if (s)
	    {
		if (paramcount > MAXPARA)
			paramcount = MAXPARA;
		for (;;)
		    {
			/*
			** Never "FRANCE " again!! ;-) Clean
			** out *all* blanks.. --msa
			*/
			while (*s == ' ')
				*s++ = '\0';

			if (*s == '\0')
				break;
			if (*s == ':')
			    {
				/*
				** The rest is single parameter--can
				** include blanks also.
				*/
				para[++i] = s + 1;
				break;
			    }
			para[++i] = s;
			if (i >= paramcount-1)
				break;
			for (; *s != ' ' && *s; s++)
				;
		    }
	    }
	para[++i] = NULL; /* at worst, ++i is paramcount (MAXPARA) */
	if (mptr == NULL)
		return (do_numeric(numeric, cptr, from, i, para));
	mptr->count++;
	if (!MyConnect(from))
		mptr->rcount++;
	if (IsRegisteredUser(cptr) &&
#ifdef	IDLE_FROM_MSG
	    mptr->func == m_private)
#else
	    mptr->func != m_ping && mptr->func != m_pong)
#endif
		from->user->last = timeofday;
	Debug((DEBUG_DEBUG, "Function: %#x = %s parc %d parv %#x",
		mptr->func, mptr->cmd, i, para));
#ifndef	CLIENT_COMPILE
	if ((mptr->flags & MSG_REGU) && check_registered_user(from))
		return -1;
	if ((mptr->flags & MSG_SVC) && check_registered_service(from))
		return -1;
	if ((mptr->flags & MSG_REG) && check_registered(from))
		return -1;
	if ((mptr->flags & MSG_NOU) && (MyPerson(from) || MyService(from)))
	    {
		sendto_one(from, err_str(ERR_ALREADYREGISTRED, para[0]));
		return-1;
	    }
	if (MyConnect(from) && !IsServer(from) &&
	    (mptr->flags & (MSG_LOP|MSG_OP)) &&
	    !((mptr->flags & MSG_OP) && (IsOper(from))) &&
	    !((mptr->flags & MSG_LOP) && (IsLocOp(from))))
		    {
			sendto_one(from, err_str(ERR_NOPRIVILEGES, para[0]));
			return -1;
		    }
#endif
	/*
	** ALL m_functions return now UNIFORMLY:
	**   -2  old FLUSH_BUFFER return value (unchanged).
	**   -1  if parsing of a protocol message leads in a syntactic/semantic
	**       error and NO penalty scoring should be applied.
	**   >=0 if protocol message processing was successful. The return
	**       value indicates the penalty score.
	*/
	ret = (*mptr->func)(cptr, from, i, para);

#ifndef       CLIENT_COMPILE
	/*
        ** Add penalty score for sucessfully parsed command if issued by
	** a LOCAL user client.
	*/
	if ((ret > 0) && IsRegisteredUser(cptr) && (bootopt & BOOT_PROT))
	    {
		cptr->since += ret;
/* only to lurk
		sendto_one(cptr,
			   ":%s NOTICE %s :*** Penalty INCR [%s] +%d",
			   me.name, cptr->name, ch, ret);
*/
	    }
#endif
	return (ret != FLUSH_BUFFER) ? 2 : FLUSH_BUFFER;
}

/*
 * field breakup for ircd.conf file.
 */
char	*getfield(irc_newline)
char	*irc_newline;
{
	static	char *line = NULL;
	char	*end, *field;
	
	if (irc_newline)
		line = irc_newline;
	if (line == NULL)
		return(NULL);

	field = line;
	if ((end = (char *)index(line, IRCDCONF_DELIMITER)) == NULL)
	    {
		line = NULL;
		if ((end = (char *)index(field,'\n')) == NULL)
			end = field + strlen(field);
	    }
	else
		line = end + 1;
	*end = '\0';
	return(field);
}

#ifndef	CLIENT_COMPILE
static	int	cancel_clients(cptr, sptr, cmd)
aClient	*cptr, *sptr;
char	*cmd;
{
	/*
	 * kill all possible points that are causing confusion here,
	 * I'm not sure I've got this all right...
	 * - avalon
	 */
	sendto_flag(SCH_NOTICE, "Message (%s) for %s[%s!%s@%s] from %s",
		    cmd, sptr->name, sptr->from->name, sptr->from->username,
		    sptr->from->sockhost, get_client_name(cptr, TRUE));
	/*
	 * Incorrect prefix for a server from some connection.  If it is a
	 * client trying to be annoying, just QUIT them, if it is a server
	 * then the same deal.
	 */
	if (IsServer(sptr) || IsMe(sptr))
	    {
		sendto_flag(SCH_NOTICE, "Dropping server %s",cptr->name);
		return exit_client(cptr, cptr, &me, "Fake Direction");
	    }
	/*
	 * Ok, someone is trying to impose as a client and things are
	 * confused.  If we got the wrong prefix from a server, send out a
	 * kill, else just exit the lame client.
	 */
	if (IsServer(cptr))
	    {
		sendto_serv_butone(NULL, ":%s KILL %s :%s (%s[%s] != %s)",
				   me.name, sptr->name, me.name,
				   sptr->name, sptr->from->name,
				   get_client_name(cptr, TRUE));
		sptr->flags |= FLAGS_KILLED;
		return exit_client(cptr, sptr, &me, "Fake Prefix");
	    }
	return exit_client(cptr, cptr, &me, "Fake prefix");
}

static	void	remove_unknown(cptr, sender)
aClient	*cptr;
char	*sender;
{
	if (!IsRegistered(cptr) || IsClient(cptr))
		return;
	/*
	 * Not from a server so don't need to worry about it.
	 */
	if (!IsServer(cptr))
		return;
	/*
	 * squit if it is a server because it means something is really
	 * wrong.
	 */
	if (index(sender, '.') /* <- buggy, it could be a service! */
	    && !index(sender, '@')) /* better.. */
	    {
		sendto_flag(SCH_LOCAL, "Squitting unknown %s brought by %s.",
			    sender, get_client_name(cptr, FALSE));
		sendto_one(cptr, ":%s SQUIT %s :(Unknown from %s)",
			   me.name, sender, get_client_name(cptr, FALSE));
	    }
	else
	/*
	 * Do kill if it came from a server because it means there is a ghost
	 * user on the other server which needs to be removed. -avalon
	 * it can simply be caused by lag (among other things), so just
	 * drop it if it is not a server. -krys
	 * services aren't prone to collisions, so lag shouldn't be responsible
	 * if we get here and sender is a service, we should probably issue
	 * a kill in this case! -krys
	 */
		sendto_flag(SCH_LOCAL, "Dropping unknown %s brought by %s.",
			    sender, get_client_name(cptr, FALSE));
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1