/************************************************************************
 *   IRC - Internet Relay Chat, modules/m_nick.c
 *
 *   Copyright (C) 2000-2003 TR-IRCD Development
 *
 *   Copyright (C) 1990 Jarkko Oikarinen and
 *                      University of Oulu, Co Center
 *
 *   See file AUTHORS in IRC package for additional names of
 *   the programmers.
 *
 *   This program is free softwmare; 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, 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.
 */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include "chanmode.h"
#include "s_conf.h"
#include "throttle.h"
#include "usermode.h"
#include "h.h"

static char *n_token = TOK1_NICK;
static char *c_token = TOK1_CLIENT;

static int collide_nicknames(char *nick, int new, int newts,
			     char **parv, aClient *cptr, aClient *sptr);

static struct Message n_msgtab[] = {
    {MSG_NICK, 0, MAXPARA, M_SLOW, 0L,
     u_nick, u_nick, u_nick, s_nick, m_ignore}
};

static struct Message c_msgtab[] = {
    {MSG_CLIENT, 0, MAXPARA, M_SLOW, 0l,
     m_ignore, m_ignore, m_ignore, m_client, m_ignore}
};

#ifndef STATIC_MODULES

char *_version = "$Revision: 1.11 $";

void _modinit(void)
{
    mod_add_cmd(n_msgtab);
    tok1_msgtab[(u_char) *n_token].msg = n_msgtab;
    mod_add_cmd(c_msgtab);
    tok1_msgtab[(u_char) *c_token].msg = c_msgtab;
}

void _moddeinit(void)
{
    mod_del_cmd(n_msgtab);
    tok1_msgtab[(u_char) *n_token].msg = NULL;
    mod_del_cmd(c_msgtab);
    tok1_msgtab[(u_char) *c_token].msg = NULL;

}
#else
void m_nick_init(void)
{
    mod_add_cmd(n_msgtab);
    tok1_msgtab[(u_char) *n_token].msg = n_msgtab;
    mod_add_cmd(c_msgtab);
    tok1_msgtab[(u_char) *c_token].msg = c_msgtab;
}
#endif

/* clean_nick_name()
 * 
 * input        - nickname 
 * output       - none
 * side effects - walks through the nickname, returning 0 if erroneous
 */

static int clean_nick_name(char *nick)
{
    if (nick == NULL || *nick == '\0')
	return 0;

    /* nicks cant start with a digit or - */
    if (*nick == '-' || IsDigit(*nick))
	return 0;

    for (; *nick; nick++) {
	if (!IsNickChar(*nick))
	    return 0;
    }

    return 1;
}

/*
 *
 * m_nick
 * parv[0] = sender prefix
 * parv[1] = nickname
 * parv[2] = hopcount when new user; TS when nick change
 * parv[3] = TS
 * ---- new user only below ----
 * parv[4] = umode
 * parv[5] = username
 * parv[6] = hostname
 * parv[7] = server
 * parv[8] = serviceid
 * parv[9] = ircname
 * ---- DT1 protocol change ----
 * parv[4] = umode
 * parv[5] = username
 * parv[6] = hostname
 * parv[7] = fakehost
 * parv[8] = server 
 * parv[9] = serviceid
 * parv[10] = IP
 * parv[11] = ircname
 * -- endif
 */

int s_nick(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aClient *uplink;
    char nick[NICKLEN + 2];
    long newts = 0;
    int samenick = 0;
    int test = 0;

    /* We do not blame servers by sending them numerics. Since s_nick can
     * only result from a server, we silently return when parc < 2.
     * -TimeMr14C
     */

    if (parc < 3)
	return 0;

    /*
     * parc == 2 on a normal client sign on (local) and a normal client nick change
     * parc == 4 on a normal server-to-server client nick change
     * parc >= 10 on a normal TS style server-to-server NICK introduction
     */

    if (parc > 3 && parc < 10) {
	/*
	 * We got the wrong number of params. Someone is trying to trick
	 * us. Kill it. -ThemBones As discussed with ThemBones, not much
	 * point to this code now sending a whack of global kills would 
	 * also be more annoying then its worth, just note the problem, 
	 * and continue -Dianora
	 */
	sendto_lev(SNOTICE_LEV, "IGNORING BAD NICK: %s[%s@%s] on %s (from %s)",
		   parv[1], (parc >= 6) ? parv[5] : "-",
		   (parc >= 7) ? parv[6] : "-", (parc >= 8) ? parv[7] : "-", parv[0]);
	return 0;

    }

    /* Here we had a check after a dot in the hostname. This is removed
     * due to the fact that operators might have dotless hostnames due to
     * a DALnet like usage of oper hostmasking feature. 
     * That check did not do anything but crying out loud, so it was of no
     * effect anyway. 29.04.2002 -TimeMr14C
     */

    strlcpy_irc(nick, parv[1], NICKLEN);

    /*
     * if clean_nick_name() returns a null name OR if the server sent a
     * nick name and clean_nick_name() changed it in some way (due to rules
     * of nick creation) then reject it. If from a server and we reject 
     * it, and KILL it. -avalon 4/4/92
     */
    if (clean_nick_name(nick) == 0 || strcmp(nick, parv[1])) {
	send_me_numeric(cptr, ERR_ERRONEUSNICKNAME, parv[1]);

	ircstp->is_kill++;
	sendto_lev(DEBUG_LEV, "Bad Nick: %s From: %s %s",
		   parv[1], parv[0], get_client_name(cptr, FALSE));
	sendto_one(cptr, ":%C %s %s :Bad Nick", &me, MSG_KILL, parv[1]);
	if (sptr != cptr) {
	    sendto_serv_butone(cptr, &me, TOK1_KILL, "%~C :Incoherent Nickname", cptr);
	    sptr->flags |= FLAGS_KILLED;
	    return exit_client(sptr, &me, "Incoherent Nickname");
	}
	return 0;
    }

    /*
     * * Check against nick name collisions. *
     * 
     * We check against server name list before determining if the
     * nickname * is present in the nicklist (due to the way the below 
     * for loop is * constructed). -avalon
     */

    test = collide_nicknames(nick, 0, newts, parv, cptr, sptr);

    if (test != -1)
	return test;

    if (parc == 12) {
    aClient *acptr2;
    long flag;
    char *m;
    u_long newip = 0;

	uplink = find_server(parv[8]);
	if (!uplink) {
	    sendto_lev(SNOTICE_LEV, "Remote nick %s on UNKNOWN server %s", nick, parv[8]);
	    return 0;
	}

	acptr2 = make_client(cptr);
	acptr2->servptr = uplink;

	strcpy(acptr2->name, nick);

	add_client_to_list(acptr2);
	add_to_client_hash_table(nick, acptr2);

	if (IsULine(uplink))
	    acptr2->protoflags |= PFLAGS_ULINE;

	acptr2->hopcount = atoi(parv[2]);
	newts = atol(parv[3]);

	if (!newts)
	    newts = timeofday;

	acptr2->tsval = newts;

	m = &parv[4][1];
	while (*m) {
	    if (umodetab[(int) *m].in_use) {
		flag = umodetab[(int) *m].type;
		if ((flag == UMODE_o))
		    Count.oper++;
		if ((flag & UMODE_i))
		    Count.invisi++;
		acptr2->umode |= flag & SEND_UMODES;
	    }
	    m++;
	}

	newip = strtoul(parv[10], NULL, 0);
	IN4_ADDR(acptr2->ip) = newip;
	/* adding the remote client to the throttle hash.
	 * checking local clients takes place in s_bsd.c
	 * -epi */
	if (newip != 0)
	    throttle_check(inetntoa((char *) &acptr2->ip), -1, acptr2->tsval);

	return do_remote_user(nick, cptr, acptr2, parv[5], parv[6], parv[7],
			      parv[8], strtoul(parv[9], NULL, 0), parv[11]);
    }

    else if (sptr->name[0]) {

	/*
	 * * Client just changing his/her nick. If he/she is * on a
	 * channel, send note of change to all clients * on that channel.
	 * Propagate notice to other servers.
	 */

	newts = atol(parv[2]);

	/*
	 * if the nickname is different, set the TS
	 * AND set it -r. No need to propogate MODE -r and spam
	 * the network on registered nick changes. yuck. - lucas
	 */

	if (irc_strcmp(parv[0], nick)) {
	    sptr->tsval = newts ? newts : (long) timeofday;
	    sptr->umode &= ~UMODE_r;
	}

	sendto_common_channels(sptr, ":%C %s :%s", sptr, MSG_NICK, nick);
	if (sptr->user) {
	    add_history(sptr, 1);
	    sendto_serv_butone(cptr, sptr, TOK1_NICK, "%s :%T", nick, sptr);
	    sendto_service(SERVICE_SEE_NICKS, 0, sptr, NULL, TOK1_NICK, "%s :%T", nick, sptr);
	}

	del_from_client_hash_table(sptr->name, sptr);
	samenick = irc_strcmp(sptr->name, nick) ? 0 : 1;
	if (!samenick)
	    hash_check_watch(sptr, RPL_LOGOFF);
	strcpy(sptr->name, nick);
	add_to_client_hash_table(nick, sptr);
	if (!samenick)
	    hash_check_watch(sptr, RPL_LOGON);
	return 0;

    }
    return 0;
}

/* This function is a new version of the SERVER NICK line, 
 * it is only exchanged between nativemode servers, and does not
 * have too many case differentiations.
 */

/*              
 *                   
 * m_nick  
 * parv[0] = sender prefix
 * parv[1] = nickname
 * parv[2] = hopcount when new user; TS when nick change        
 * parv[3] = TS
 * parv[4] = umode         
 * parv[5] = username   
 * parv[6] = hostname
 * parv[7] = fakehost
 * parv[8] = language
 * parv[9] = server OR id/server-pair
 * parv[10] = serviceid                  
 * parv[11] = ircname
 * -- endif
 */

int m_client(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aClient *uplink;
    char nick[NICKLEN + 2];
    long newts = 0;
    int test = 0;

    if (parc < 2) {
	send_me_numeric(sptr, ERR_NONICKNAMEGIVEN);
	return 0;
    }

    newts = atol(parv[3]);

    strlcpy_irc(nick, parv[1], NICKLEN);

    /*
     * if clean_nick_name() returns a null name OR if the server sent a
     * nick name and clean_nick_name() changed it in some way (due to rules
     * of nick creation) then reject it. If from a server and we reject 
     * it, and KILL it. -avalon 4/4/92
     */
    if (clean_nick_name(nick) == 0 || strcmp(nick, parv[1])) {
	send_me_numeric(cptr, ERR_ERRONEUSNICKNAME, parv[1]);

	ircstp->is_kill++;
	sendto_lev(DEBUG_LEV, "Bad Nick: %s From: %s %s",
		   parv[1], parv[0], get_client_name(cptr, FALSE));
	sendto_one(cptr, ":%C %s %s :Bad Nick", &me, MSG_KILL, parv[1]);
	if (sptr != cptr) {
	    sendto_serv_butone(cptr, &me, TOK1_KILL, "%~C :Bad Nick", cptr);
	    sptr->flags |= FLAGS_KILLED;
	    return exit_client(sptr, &me, "BadNick");
	}
	return 0;
    }

    /*
     * * Check against nick name collisions. *
     * 
     * We check against server name list before determining if the
     * nickname * is present in the nicklist (due to the way the below 
     * for loop is * constructed). -avalon
     */

    test = collide_nicknames(nick, 0, newts, parv, cptr, sptr);

    if (test != -1)
	return test;

    if (parc == 12) {

    aClient *acptr2;
    long flag = 0;
    int pflags = 0;
    char *m;
    u_long newid, newip = 0;
    char *base64id = NULL;
    char *ipp;
    char *newhostip = NULL;
    aClient *bucptr;

	base64id = &parv[9][1];
	ipp = strchr(base64id, '%');
	if (ipp) {
	    *ipp++ = '\0';
	    newhostip = ipp;
	    pflags |= PFLAGS_IPV6HOST;
	} else {
	    ipp = strchr(base64id, ':');
	    if (ipp) {
		*ipp++ = '\0';
		newip = base64dec(ipp);
	    }
	}

	if ((bucptr = find_by_base64_id(base64id))) {
	    sendto_ops("IDENTITY COLLISION! (%s[%s][%s] <> %s[%s][%s])",
		       sptr->name, sptr->servptr->name, base64id,
		       bucptr->name, bucptr->servptr->name, bucptr->id.string);
	    return exit_client(bucptr, &me, "Identity collision!");
	}

	uplink = find_server_by_base64_id(base64id, &newid);

	if (uplink) {
	    parv[9] = uplink->name;
	} else {
	    sendto_lev(SNOTICE_LEV, "Remote nick %s on UNKNOWN server %s", nick, parv[9]);
	    return 0;
	}

	acptr2 = make_client(cptr);
	acptr2->servptr = uplink;

	strcpy(acptr2->name, nick);

	add_client_to_list(acptr2);
	add_to_client_hash_table(nick, acptr2);

	SetHasID(acptr2);	/* this client is identity capable */
	acptr2->id.id = newid;	/* new identity! */
	strlcpy_irc(acptr2->id.string, base64id, 8);
	add_userid_to_server(uplink, acptr2);

	if (IsULine(uplink))
	    pflags |= PFLAGS_ULINE;

	acptr2->hopcount = atoi(parv[2]);

	if (!newts)
	    newts = timeofday;

	acptr2->tsval = newts;

	m = &parv[4][1];
	while (*m) {
	    if (umodetab[(int) *m].in_use) {
		flag = umodetab[(int) *m].type;
		if ((flag == UMODE_o))
		    Count.oper++;
		if ((flag & UMODE_i))
		    Count.invisi++;
		acptr2->umode |= flag & SEND_UMODES;
	    }
	    m++;
	}

	if (newip) {
	    IN4_ADDR(acptr2->ip) = newip;
	    throttle_check(inetntoa((char *) &acptr2->ip), -1, acptr2->tsval);
	}
	if (newhostip) {
	    strlcpy_irc(acptr2->hostip, newhostip, HOSTIPLEN);
	    throttle_check(newhostip, -1, acptr2->tsval);
	}

	acptr2->lang = atoi(parv[8]);
	acptr2->protoflags |= pflags;

	return do_remote_user(nick, cptr, acptr2, parv[5], parv[6], parv[7],
			      parv[9], base64dec(parv[10]), parv[11]);
    }

    return 0;
}

    /* guaranteed sptr == cptr, !IsServer(cptr), !IsServer(sptr) */

int u_nick(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
    aMaskItem *ami;
    aChannel *chptr;
    dlink_node *lp;
    char nick[NICKLEN + 2];
    long newts = 0;
    int samenick = 0;
    int test = 0;

    if (parc < 2) {
	send_me_numeric(sptr, ERR_NONICKNAMEGIVEN);
	return 0;
    }

    strlcpy_irc(nick, parv[1], NICKLEN);
    /*
     * if clean_nick_name() returns a null name OR if the server sent a
     * nick name and clean_nick_name() changed it in some way (due to rules
     * of nick creation) then reject it. If from a server and we reject 
     * it, and KILL it. -avalon 4/4/92
     */

    if (clean_nick_name(nick) == 0) {
	send_me_numeric(cptr, ERR_ERRONEUSNICKNAME, parv[1]);
	return 0;
    }

    /*
     * * Check against nick name collisions. *
     * 
     * Put this 'if' here so that the nesting goes nicely on the screen
     * :) * We check against server name list before determining if the
     * nickname * is present in the nicklist (due to the way the below 
     * for loop is * constructed). -avalon
     */

    test = collide_nicknames(nick, 1, newts, parv, cptr, sptr);

    if (test != -1)
	return test;

    if (sptr->name[0]) {
	/*
	 * * Client just changing his/her nick. If he/she is * on a
	 * channel, send note of change to all clients * on that channel.
	 * Propagate notice to other servers.
	 */
	if ((ami = find_maskitem(nick, NULL, MASKITEM_QUARANTINE, 1)) && !IsAnOper(sptr)) {
	    send_me_numeric(sptr, ERR_ERRONEUSNICKNAME, nick, ami->reason);
	    send_me_numeric(sptr, ERR_EVALUATINGQLINE, ami->string, ami->reason);
	    sendto_lev(QLINE_LEV,
		       "Forbidding Quarantined nick %s from %s. Qline [%s] evaluates into %s", nick,
		       get_client_name(cptr, FALSE), ami->string, nick);
	    return 0;
	} else if ((ami = find_maskitem(nick, NULL, MASKITEM_QUARANTINE_CONFIG, 1)) && !IsAnOper(sptr)) {
            send_me_numeric(sptr, ERR_ERRONEUSNICKNAME, nick, ami->reason); 
            send_me_numeric(sptr, ERR_EVALUATINGQLINE, ami->string, ami->reason);
            sendto_lev(QLINE_LEV,
                       "Forbidding Quarantined nick %s from %s. Qline [%s] evaluates into %s", nick,
                       get_client_name(cptr, FALSE), ami->string, nick);
            return 0;
	} else if (ServerOpts.use_regex && !IsAnOper(sptr) &&
		   (ami = find_maskitem(nick, NULL, MASKITEM_QUARANTINE_REGEX, 1))) {
	    send_me_numeric(sptr, ERR_ERRONEUSNICKNAME, nick, ami->reason);
	    send_me_numeric(sptr, ERR_EVALUATINGQLINE, ami->string, ami->reason);
	    sendto_lev(QLINE_LEV,
		       "Forbidding Quarantined nick %s from %s. Qline [%s] evaluates into %s", nick,
		       get_client_name(cptr, FALSE), ami->string, nick);
	    return 0;
	}

	if (IsPerson(sptr)) {

	    for (lp = sptr->user->channel.head; lp; lp = lp->next) {
		chptr = lp->data;
		if (nick_is_nuhed(chptr, nick, sptr, &chptr->banlist) != NULL) {
		    send_me_numeric(sptr, ERR_BANONCHAN, nick, chptr->chname);
		    return 0;
		}
	    }

	    if (ServerOpts.anti_nick_flood) {
		if ((sptr->last_nick_time + ServerOpts.max_nick_time) < NOW)
		    sptr->count_nick = 0;
		sptr->last_nick_time = NOW;
		sptr->count_nick++;
	    }

	    if (sptr->count_nick <= ServerOpts.max_nick_changes || IsAnOper(sptr)) {
		/*
		 * if the nickname is different, set the TS
		 * AND set it -r. No need to propogate MODE -r and spam
		 * the network on registered nick changes. yuck. - lucas
		 */

		if (irc_strcmp(parv[0], nick)) {
		    sptr->tsval = timeofday;
		    sptr->umode &= ~UMODE_r;
		}

		sendto_common_channels(sptr, ":%C %s :%s", sptr, MSG_NICK, nick);
		if (sptr->user) {
		    add_history(sptr, 1);
		    sendto_serv_butone(cptr, sptr, TOK1_NICK, "%s :%T", nick, sptr);
		    sendto_service(SERVICE_SEE_NICKS, 0, sptr, NULL,
				   TOK1_NICK, "%s :%T", nick, sptr);
		}
	    } else {
		send_me_notice(sptr,
			       ":*** Notice -- Too many nick changes. Wait %d seconds"
			       "before trying again", ServerOpts.max_nick_time);
		return 0;
	    }
	}
	del_from_client_hash_table(sptr->name, sptr);
	samenick = irc_strcmp(sptr->name, nick) ? 0 : 1;
	if (IsPerson(sptr) && !samenick)
	    hash_check_watch(sptr, RPL_LOGOFF);
	strcpy(sptr->name, nick);
	add_to_client_hash_table(nick, sptr);
	if (IsPerson(sptr) && !samenick)
	    hash_check_watch(sptr, RPL_LOGON);

	fd_note(sptr->fd, "Nick %s", nick);
    }

    else {

	/* Client setting NICK the first time */
	if ((ami = find_maskitem(nick, NULL, MASKITEM_QUARANTINE, 1))) {
	    send_me_numeric(sptr, ERR_ERRONEUSNICKNAME, nick, ami->reason);
	    send_me_numeric(sptr, ERR_EVALUATINGQLINE, ami->string, ami->reason);
	    sendto_lev(QLINE_LEV,
		       "Forbidding Quarantined nick %s from %s. Qline [%s] evaluates into %s", nick,
		       get_client_name(cptr, FALSE), ami->string, nick);
	    return 0;
	} else if ((ami = find_maskitem(nick, NULL, MASKITEM_QUARANTINE_CONFIG, 1))) {
            send_me_numeric(sptr, ERR_ERRONEUSNICKNAME, nick, ami->reason); 
            send_me_numeric(sptr, ERR_EVALUATINGQLINE, ami->string, ami->reason);
            sendto_lev(QLINE_LEV,
                       "Forbidding Quarantined nick %s from %s. Qline [%s] evaluates into %s", nick,
                       get_client_name(cptr, FALSE), ami->string, nick);
            return 0;
	} else if (ServerOpts.use_regex &&
		   (ami = find_maskitem(nick, NULL, MASKITEM_QUARANTINE_REGEX, 1))) {
	    send_me_numeric(sptr, ERR_ERRONEUSNICKNAME, nick, ami->reason);
	    send_me_numeric(sptr, ERR_EVALUATINGQLINE, ami->string, ami->reason);
	    sendto_lev(QLINE_LEV,
		       "Forbidding Quarantined nick %s from %s. Qline [%s] evaluates into %s", nick,
		       get_client_name(cptr, FALSE), ami->string, nick);
	    return 0;
	}

	strcpy(sptr->name, nick);
	sptr->tsval = timeofday;
	add_to_client_hash_table(nick, sptr);
	fd_note(sptr->fd, "Nick %s", nick);
	if (sptr->user) {
	    /* USER already received, now we have NICK. */
	    return register_local_user(cptr, sptr, nick, sptr->user->username);
	}
    }

    return 0;

}

static int collide_nicknames(char *nick, int new, int newts, char **parv,
			     aClient *cptr, aClient *sptr)
{
    aClient *acptr;
    int sameuser = 0;

    if (new) {

	/* nothing else is using this nick, great! */
	if (!(acptr = find_client(nick)))
	    return -1;

	/*
	 * * If acptr == sptr, then we have a client doing a nick * change
	 * between *equivalent* nicknames as far as server * is concerned
	 * (user is changing the case of his/her * nickname or somesuch)
	 */
	if (acptr == sptr) {
	    if (strcmp(acptr->name, nick) != 0)
		return -1;
	    else
		return 0;
	}

	if (IsUnknown(acptr)) {
	    exit_client(acptr, &me, "Overridden by older signon");
	    return -1;
	}

	/*
	 * * NICK is coming from local client connection. Just * send
	 * error reply and ignore the command.
	 * parv[0] is empty on connecting clients
	 */
	send_me_numeric(cptr, ERR_NICKNAMEINUSE, nick);
	return 0;

    }

    if (!(acptr = find_client(nick)))
	return -1;

    /*
     * * If acptr == sptr, then we have a client doing a nick * change
     * between *equivalent* nicknames as far as server * is concerned 
     * (user is changing the case of his/her * nickname or somesuch)  
     */
    if (acptr == sptr) {
	if (strcmp(acptr->name, nick) != 0)
	    return -1;
	else
	    return 0;
    }

    /*
     * * If the older one is "non-person", the new entry is just *
     * allowed to overwrite it. Just silently drop non-person, * and
     * proceed with the nick. This should take care of the * "dormant
     * nick" way of generating collisions...
     */
    if (IsUnknown(acptr)) {
	exit_client(acptr, &me, "Overridden by older signon");
	return -1;
    }

    if (!newts || !acptr->tsval || (newts == acptr->tsval)) {
	sendto_lev(SKILL_LEV, "Nick collision on %s", acptr->name);
	ircstp->is_kill++;
	send_me_numeric(acptr, ERR_NICKCOLLISION, acptr->name);
	sendto_serv_butone(NULL, &me, TOK1_KILL, "%~C :Nick Collision", acptr);
	acptr->flags |= FLAGS_KILLED;
	exit_client(acptr, &me, "Nick collision");
	return 0;
    } else {
	sameuser = (acptr->user) &&
	    irc_strcmp(acptr->user->username, parv[5]) == 0 &&
	    irc_strcmp(acptr->user->host, parv[6]) == 0;
	if ((sameuser && newts < acptr->tsval)
	    || (!sameuser && newts > acptr->tsval))
	    return 0;
	else {
	    if (sameuser)
		sendto_lev(SKILL_LEV, "Nick collision on %s", acptr->name);
	    else
		sendto_lev(SKILL_LEV, "Nick collision on %s", acptr->name);
	    ircstp->is_kill++;
	    send_me_numeric(acptr, ERR_NICKCOLLISION, acptr->name);
	    sendto_serv_butone(sptr, &me, TOK1_KILL, "%~C :Nick Collision", acptr);
	    acptr->flags |= FLAGS_KILLED;
	    exit_client(acptr, &me, "Nick collision");
	    return -1;
	}
    }

    /*
     * * A NICK change has collided (e.g. message type * ":old NICK
     * new". This requires more complex cleanout. * Both clients must be
     * purged from this server, the "new" * must be killed from the
     * incoming connection, and "old" must * be purged from all outgoing
     * connections.
     */
    if (!newts || !acptr->tsval || (newts == acptr->tsval)
	|| !sptr->user) {
	sendto_lev(SKILL_LEV, "Nick change collision from %s to %s", sptr->name, acptr->name);
	ircstp->is_kill++;
	send_me_numeric(acptr, ERR_NICKCOLLISION, acptr->name);
	sendto_serv_butone(NULL, &me, TOK1_KILL, "%~C :Nick collision", sptr);
	ircstp->is_kill++;
	sendto_serv_butone(NULL, &me, TOK1_KILL, "%~C :Nick collision", acptr);
	acptr->flags |= FLAGS_KILLED;
	exit_client(acptr, &me, "Nick collision(new)");
	sptr->flags |= FLAGS_KILLED;
	exit_client(sptr, &me, "Nick collision(old)");
	return 0;
    } else {
	sameuser =
	    irc_strcmp(acptr->user->username, sptr->user->username) == 0
	    && irc_strcmp(acptr->user->host, sptr->user->host) == 0;
	if ((sameuser && newts < acptr->tsval)
	    || (!sameuser && newts > acptr->tsval)) {
	    if (sameuser)
		sendto_lev(SKILL_LEV,
			   "Nick change collision from %s to %s(older killed)",
			   sptr->name, acptr->name);
	    ircstp->is_kill++;
	    sendto_serv_butone(cptr, &me, TOK1_KILL, "%~C :Nick Collision", sptr);
	    sptr->flags |= FLAGS_KILLED;
	    if (sameuser)
		exit_client(sptr, &me, "Nick collision(old)");
	    else
		exit_client(sptr, &me, "Nick collision(new)");
	    return 0;
	} else {
	    sendto_lev(SKILL_LEV, "Nick collision on %s", acptr->name);
	    ircstp->is_kill++;
	    send_me_numeric(acptr, ERR_NICKCOLLISION, acptr->name);
	    sendto_serv_butone(sptr, &me, TOK1_KILL, "%~C :Nick Collision", acptr);
	    acptr->flags |= FLAGS_KILLED;
	    exit_client(acptr, &me, "Nick collision");
	    return 0;
	}
    }
}


syntax highlighted by Code2HTML, v. 0.9.1