Add a parameter to IPcheck_connect_fail() to support IAuth IP spoofing.
[ircu2.10.12-pk.git] / ircd / m_nick.c
index b8cf167b593f2bee876ef5eb352c5d49893f520d..b612f8672b9e0cd42c4002b41c81c69589d04663 100644 (file)
  *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
  *                    non-NULL pointers.
  */
-#if 0
-/*
- * No need to include handlers.h here the signatures must match
- * and we don't need to force a rebuild of all the handlers everytime
- * we add a new one to the list. --Bleep
- */
-#include "handlers.h"
-#endif /* 0 */
+#include "config.h"
+
 #include "IPcheck.h"
 #include "client.h"
 #include "hash.h"
 #include "ircd.h"
 #include "ircd_chattr.h"
+#include "ircd_features.h"
+#include "ircd_log.h"
 #include "ircd_reply.h"
 #include "ircd_string.h"
 #include "msg.h"
 #include "s_misc.h"
 #include "s_user.h"
 #include "send.h"
+#include "sys.h"
 
-#include <assert.h>
+/* #include <assert.h> -- Now using assert in ircd_log.h */
 #include <stdlib.h>
 #include <string.h>
 
+ /*
+* 'do_nick_name' ensures that the given parameter (nick) is really a proper
+* string for a nickname (note, the 'nick' may be modified in the process...)
+*
+* RETURNS the length of the final NICKNAME (0, if nickname is invalid)
+*
+* Nickname characters are in range 'A'..'}', '_', '-', '0'..'9'
+*  anything outside the above set will terminate nickname.
+* In addition, the first character cannot be '-' or a Digit.
+*
+* Note:
+*  The '~'-character should be allowed, but a change should be global,
+*  some confusion would result if only few servers allowed it...
+*/
+static int do_nick_name(char* nick)
+{
+  char* ch  = nick;
+  char* end = ch + NICKLEN;
+  assert(0 != ch);
+  
+  /* first character in [0..9-] */
+  if (*ch == '-' || IsDigit(*ch))
+    return 0;
+  for ( ; (ch < end) && *ch; ++ch)
+    if (!IsNickChar(*ch))
+      break;
+
+  *ch = '\0';
+
+  return (ch - nick);
+}
+
 /*
  * m_nick - message handler for local clients
  * parv[0] = sender prefix
@@ -117,27 +146,25 @@ int m_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   char           nick[NICKLEN + 2];
   char*          arg;
   char*          s;
-  const char*    client_name;
 
   assert(0 != cptr);
   assert(cptr == sptr);
 
-  /*
-   * parv[0] will be empty for clients connecting for the first time
-   */
-  client_name = (*sptr->name) ? sptr->name : "*";
+  if (IsServerPort(cptr))
+    return exit_client(cptr, cptr, &me, "Use a different port");
 
   if (parc < 2) {
     send_reply(sptr, ERR_NONICKNAMEGIVEN);
     return 0;
   }
+
   /*
    * Don't let them send make us send back a really long string of
    * garbage
    */
   arg = parv[1];
-  if (strlen(arg) > NICKLEN)
-    arg[NICKLEN] = '\0';
+  if (strlen(arg) > IRCD_MIN(NICKLEN, feature_int(FEAT_NICKLEN)))
+    arg[IRCD_MIN(NICKLEN, feature_int(FEAT_NICKLEN))] = '\0';
 
   if ((s = strchr(arg, '~')))
     *s = '\0';
@@ -145,10 +172,7 @@ int m_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   strcpy(nick, arg);
 
   /*
-   * If do_nick_name() returns a null name OR if the server sent a nick
-   * name and do_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 do_nick_name() returns a null name then reject it.
    */
   if (0 == do_nick_name(nick)) {
     send_reply(sptr, ERR_ERRONEUSNICKNAME, arg);
@@ -187,7 +211,7 @@ int m_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
      * is concerned (user is changing the case of his/her
      * nickname or somesuch)
      */
-    if (0 != strcmp(acptr->name, nick)) {
+    if (0 != strcmp(cli_name(acptr), nick)) {
       /*
        * Allows change of case in his/her nick
        */
@@ -214,9 +238,15 @@ int m_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
    * "dormant nick" way of generating collisions...
    *
    * XXX - hmmm can this happen after one is registered?
+   *
+   * Yes, client 1 connects to IRC and registers, client 2 connects and
+   * sends "NICK foo" but doesn't send anything more.  client 1 now does
+   * /nick foo, they should succeed and client 2 gets disconnected with
+   * the message below.
    */
   if (IsUnknown(acptr) && MyConnect(acptr)) {
-    ++ServerStats->is_ref;
+    ServerStats->is_ref++;
+    IPcheck_connect_fail(acptr, 0);
     exit_client(cptr, acptr, &me, "Overridden by other sign on");
     return set_nick_name(cptr, sptr, nick, parc, parv);
   }
@@ -243,7 +273,6 @@ int m_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
  *   parv[4] = username
  *   parv[5] = hostname
  *   parv[6] = umode (optional)
- *   parv[parc-4] = %<lastmod>:<mask>   <- Only if matching GLINE
  *   parv[parc-3] = IP#                 <- Only Protocol >= 10
  *   parv[parc-2] = YXX, numeric nick   <- Only Protocol >= 10
  *   parv[parc-1] = info
@@ -251,16 +280,18 @@ int m_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
  */
 int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
 {
-  struct Client* acptr;
-  char           nick[NICKLEN + 2];
-  time_t         lastnick = 0;
-  int            differ = 1;
+  struct Client *acptr;
+  char nick[NICKLEN + 2];
+  time_t lastnick = 0;
+  int differ = 1;
+  const char *type;
 
   assert(0 != cptr);
   assert(0 != sptr);
   assert(IsServer(cptr));
 
-  if ((IsServer(sptr) && parc < 8) || parc < 3) {
+  if ((IsServer(sptr) && parc < 8) || parc < 3)
+  {
     sendto_opmask_butone(0, SNO_OLDSNO, "bad NICK param count for %s from %C",
                         parv[1], cptr);
     return need_more_params(sptr, "NICK");
@@ -269,17 +300,17 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   ircd_strncpy(nick, parv[1], NICKLEN);
   nick[NICKLEN] = '\0';
 
-  if (!IsBurstOrBurstAck(sptr)) {
-     if (IsServer(sptr)) {
-       lastnick = atoi(parv[3]);
-       if (lastnick > OLDEST_TS
-         sptr->serv->lag = TStime() - lastnick;
-     }
-     else {
-       lastnick = atoi(parv[2]); 
-       if (lastnick > OLDEST_TS)
-         sptr->user->server->serv->lag = TStime() - lastnick;
-     }
+  if (IsServer(sptr))
+  {
+    lastnick = atoi(parv[3]);
+    if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)
+      cli_serv(sptr)->lag = TStime() - lastnick;
+  }
+  else
+  {
+    lastnick = atoi(parv[2]); 
+    if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr))
+      cli_serv(cli_user(sptr)->server)->lag = TStime() - lastnick;
   }
   /*
    * If do_nick_name() returns a null name OR if the server sent a nick
@@ -287,66 +318,33 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
    * creation) then reject it. If from a server and we reject it,
    * and KILL it. -avalon 4/4/92
    */
-  if (0 == do_nick_name(nick) || 0 != strcmp(nick, parv[1])) {
+  if (!do_nick_name(nick) || strcmp(nick, parv[1]))
+  {
     send_reply(sptr, ERR_ERRONEUSNICKNAME, parv[1]);
-
+    
     ++ServerStats->is_kill;
     sendto_opmask_butone(0, SNO_OLDSNO, "Bad Nick: %s From: %s %C", parv[1],
                         parv[0], cptr);
     sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s <- %s[%s])",
-                 IsServer(sptr) ? parv[parc - 2] : parv[0], me.name, parv[1],
-                 nick, cptr->name);
-    if (!IsServer(sptr)) {
+                 IsServer(sptr) ? parv[parc - 2] : parv[0], cli_name(&me), parv[1],
+                 nick, cli_name(cptr));
+    if (!IsServer(sptr))
+    {
       /*
        * bad nick _change_
        */
       sendcmdto_serv_butone(&me, CMD_KILL, 0, "%s :%s (%s <- %s!%s@%s)",
-                           parv[0], me.name, cptr->name, parv[0],
-                           sptr->user ? sptr->username : "",
-                           sptr->user ? sptr->user->server->name :
-                           cptr->name);
+                           parv[0], cli_name(&me), cli_name(cptr), parv[0],
+                           cli_user(sptr) ? cli_username(sptr) : "",
+                           cli_user(sptr) ? cli_name(cli_user(sptr)->server) :
+                           cli_name(cptr));
     }
     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
-   */
-   
-  acptr = FindClient(nick);
-  if (!acptr) {
-    /*
-     * No collisions, all clear...
-     */
+  /* Check against nick name collisions. */
+  if ((acptr = FindClient(nick)) == NULL)
+    /* No collisions, all clear... */
     return set_nick_name(cptr, sptr, nick, parc, parv);
-  }
-  assert(0 != acptr);
-
-  if (IsServer(acptr)) {
-    /*
-     * We have a nickname trying to use the same name as
-     * a server. Send out a nick collision KILL to remove
-     * the nickname. As long as only a KILL is sent out,
-     * there is no danger of the server being disconnected.
-     * Ultimate way to jupiter a nick ? >;-). -avalon
-     */
-    sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C(%C <- %C)", sptr,
-                        acptr->from, cptr);
-    ++ServerStats->is_kill;
-
-    sendcmdto_one(&me, CMD_KILL, cptr, "%C :%s (%s <- %s)", sptr, me.name,
-                 acptr->from->name, cptr->name);
-
-    sptr->flags |= FLAGS_KILLED;
-    /*
-     * if sptr is a server it is exited here, nothing else to do
-     */
-    return exit_client(cptr, sptr, &me, "Nick/Server collision");
-  }
 
   /*
    * If acptr == sptr, then we have a client doing a nick
@@ -354,23 +352,16 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
    * is concerned (user is changing the case of his/her
    * nickname or somesuch)
    */
-  if (acptr == sptr) {
-    if (strcmp(acptr->name, nick) != 0)
-      /*
-       * Allows change of case in his/her nick
-       */
+  if (acptr == sptr)
+  {
+    if (strcmp(cli_name(acptr), nick) != 0)
+      /* Allows change of case in his/her nick */
       return set_nick_name(cptr, sptr, nick, parc, parv);
     else
-      /*
-       * This is just ':old NICK old' type thing.
-       * Just forget the whole thing here. There is
-       * no point forwarding it to anywhere,
-       * especially since servers prior to this
-       * version would treat it as nick collision.
-       */
-      return 0;                        /* NICK Message ignored */
+      /* Setting their nick to what it already is? Ignore it. */
+      return 0;
   }
-
+  /* now we know we have a real collision. */
   /*
    * Note: From this point forward it can be assumed that
    * acptr != sptr (point to different client structures).
@@ -382,8 +373,10 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
    * and proceed with the nick. This should take care of the
    * "dormant nick" way of generating collisions...
    */
-  if (IsUnknown(acptr) && MyConnect(acptr)) {
-    ++ServerStats->is_ref;
+  if (IsUnknown(acptr) && MyConnect(acptr))
+  {
+    ServerStats->is_ref++;
+    IPcheck_connect_fail(acptr, 0);
     exit_client(cptr, acptr, &me, "Overridden by other sign on");
     return set_nick_name(cptr, sptr, nick, parc, parv);
   }
@@ -405,32 +398,37 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
    * --Run
    *
    */
-  if (IsServer(sptr)) {
+  if (IsServer(sptr))
+  {
+    struct irc_in_addr ip;
     /*
      * A new NICK being introduced by a neighbouring
      * server (e.g. message type ":server NICK new ..." received)
      *
      * compare IP address and username
      */
-    differ =  (acptr->ip.s_addr != htonl(base64toint(parv[parc - 3]))) ||
-              (0 != ircd_strcmp(acptr->user->username, parv[4]));
+    base64toip(parv[parc - 3], &ip);
+    differ =  (0 != memcmp(&cli_ip(acptr), &ip, sizeof(cli_ip(acptr)))) ||
+              (0 != ircd_strcmp(cli_user(acptr)->username, parv[4]));
     sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C (%C %Tu <- "
-                        "%C %Tu (%s user@host))", acptr, acptr->from,
-                        acptr->lastnick, cptr, lastnick,
+                        "%C %Tu (%s user@host))", acptr, cli_from(acptr),
+                        cli_lastnick(acptr), cptr, lastnick,
                         differ ? "Different" : "Same");
   }
-  else {
+  else
+  {
     /*
      * A NICK change has collided (e.g. message type ":old NICK new").
      *
      * compare IP address and username
      */
-    differ =  (acptr->ip.s_addr != sptr->ip.s_addr) ||
-              (0 != ircd_strcmp(acptr->user->username, sptr->user->username));              
+    differ =  (0 != memcmp(&cli_ip(acptr), &cli_ip(sptr), sizeof(cli_ip(acptr)))) ||
+              (0 != ircd_strcmp(cli_user(acptr)->username, cli_user(sptr)->username));
     sendto_opmask_butone(0, SNO_OLDSNO, "Nick change collision from %C to "
-                        "%C (%C %Tu <- %C %Tu)", sptr, acptr, acptr->from,
-                        acptr->lastnick, cptr, lastnick);
+                        "%C (%C %Tu <- %C %Tu)", sptr, acptr, cli_from(acptr),
+                        cli_lastnick(acptr), cptr, lastnick);
   }
+  type = differ ? "overruled by older nick" : "nick collision from same user@host";
   /*
    * Now remove (kill) the nick on our side if it is the youngest.
    * If no timestamp was received, we ignore the incoming nick
@@ -440,357 +438,58 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
    *
    * This exits the client sending the NICK message
    */
-  if (acptr->from != cptr) {
-    if ((differ && lastnick >= acptr->lastnick) || (!differ && lastnick <= acptr->lastnick)) {
-      if (!IsServer(sptr)) {
-        ++ServerStats->is_kill;
-       sendcmdto_serv_butone(&me, CMD_KILL, sptr, "%C :%s (%s <- %s (Nick "
-                             "collision))", sptr, me.name, acptr->from->name,
-                             cptr->name);
-        assert(!MyConnect(sptr));
-#if 0
-        /*
-         * XXX - impossible
-         */
-        if (MyConnect(sptr))
-          sendto_one(cptr, "%s " TOK_KILL " %s%s :%s (Ghost 2)", /* XXX DEAD */
-                     NumServ(&me), NumNick(sptr), me.name);
-#endif
-        sptr->flags |= FLAGS_KILLED;
-        exit_client(cptr, sptr, &me, "Nick collision (you're a ghost)");
-        /*
-         * we have killed sptr off, zero out it's pointer so if it's used
-         * again we'll know about it --Bleep
-         */
-        sptr = 0;
-      }
-      if (lastnick != acptr->lastnick)
-        return 0;                /* Ignore the NICK */
-    }
-    send_reply(acptr, ERR_NICKCOLLISION, nick);
-  }
-
-  ++ServerStats->is_kill;
-  acptr->flags |= FLAGS_KILLED;
-  /*
-   * This exits the client we had before getting the NICK message
-   */
-  if (differ) {
-    sendcmdto_serv_butone(&me, CMD_KILL, acptr, "%C :%s (%s <- %s (older "
-                         "nick overruled))", acptr, me.name,
-                         acptr->from->name, cptr->name);
-    if (MyConnect(acptr))
-      sendcmdto_one(acptr, CMD_QUIT, cptr, ":Local kill by %s (Ghost)",
-                   me.name);
-    exit_client(cptr, acptr, &me, "Nick collision (older nick overruled)");
-  }
-  else {
-    sendcmdto_serv_butone(&me, CMD_KILL, acptr, "%C :%s (%s <- %s (nick "
-                         "collision from same user@host))", acptr, me.name,
-                         acptr->from->name, cptr->name);
-    if (MyConnect(acptr))
-      sendcmdto_one(acptr, CMD_QUIT, cptr, ":Local kill by %s (Ghost: ",
-                   "switched servers too fast)", me.name);
-    exit_client(cptr, acptr, &me, "Nick collision (You collided yourself)");
-  }
-  if (lastnick == acptr->lastnick)
-    return 0;
-
-  assert(0 != sptr);
-  return set_nick_name(cptr, sptr, nick, parc, parv);
-}
-
-#if 0
-/*
- * m_nick
- *
- * parv[0] = sender prefix
- * parv[1] = nickname
- *
- * If from server, source is client:
- *   parv[2] = timestamp
- *
- * Source is server:
- *   parv[2] = hopcount
- *   parv[3] = timestamp
- *   parv[4] = username
- *   parv[5] = hostname
- *   parv[6] = umode (optional)
- *   parv[parc-3] = IP#                 <- Only Protocol >= 10
- *   parv[parc-2] = YXX, numeric nick   <- Only Protocol >= 10
- *   parv[parc-1] = info
- *   parv[0] = server
- */
-int m_nick(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
-{
-  struct Client* acptr;
-  char           nick[NICKLEN + 2];
-  char*          s;
-  time_t         lastnick = 0;
-  int            differ = 1;
-
-  if (parc < 2) {
-    sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]); /* XXX DEAD */
-    return 0;
-  }
-  else if ((IsServer(sptr) && parc < 8) || (IsServer(cptr) && parc < 3))
-  {
-    need_more_params(sptr, "NICK");
-    sendto_ops("bad NICK param count for %s from %s", parv[1], cptr->name); /* XXX DEAD */
-    return 0;
-  }
-  if (MyConnect(sptr) && (s = strchr(parv[1], '~')))
-    *s = '\0';
-  ircd_strncpy(nick, parv[1], NICKLEN);
-  nick[NICKLEN] = '\0';
-  if (IsServer(cptr)) {
-    if (IsServer(sptr)) {
-      lastnick = atoi(parv[3]);
-      if (lastnick > OLDEST_TS) 
-       sptr->serv->lag = TStime() - lastnick;
-    } else {
-      lastnick = atoi(parv[2]); 
-      if (lastnick > OLDEST_TS)
-       sptr->user->server->serv->lag = TStime() - lastnick;
-    }
-  }
-  /*
-   * If do_nick_name() returns a null name OR if the server sent a nick
-   * name and do_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 (do_nick_name(nick) == 0 || (IsServer(cptr) && strcmp(nick, parv[1])))
+  if ((differ && lastnick >= cli_lastnick(acptr)) ||
+      (!differ && lastnick <= cli_lastnick(acptr)))
   {
-    sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, parv[0], parv[1]); /* XXX DEAD */
-
-    if (IsServer(cptr))
+    ServerStats->is_kill++;
+    if (!IsServer(sptr))
     {
-      ServerStats->is_kill++;
-      sendto_ops("Bad Nick: %s From: %s %s", /* XXX DEAD */
-          parv[1], parv[0], cptr->name);
-      sendto_one(cptr, "%s " TOK_KILL " %s :%s (%s <- %s[%s])", /* XXX DEAD */
-            NumServ(&me), IsServer(sptr) ? parv[parc - 2] : parv[0], me.name,
-            parv[1], nick, cptr->name);
-      if (!IsServer(sptr))        /* bad nick _change_ */
-      {
-        sendto_highprot_butone(&me, 10, "%s " TOK_KILL " %s :%s (%s <- %s!%s@%s)", /* XXX DEAD */
-            NumServ(&me), parv[0], me.name, cptr->name,
-            parv[0], sptr->user ? sptr->username : "",
-            sptr->user ? sptr->user->server->name : cptr->name);
-      }
+      /* If this was a nick change and not a nick introduction, we
+       * need to ensure that we remove our record of the client, and
+       * send a KILL to the whole network.
+       */
+      assert(!MyConnect(sptr));
+      /* Inform the rest of the net... */
+      sendcmdto_serv_butone(&me, CMD_KILL, 0, "%C :%s (%s)",
+                            sptr, cli_name(&me), type);
+      /* Don't go sending off a QUIT message... */
+      SetFlag(sptr, FLAG_KILLED);
+      /* Remove them locally. */
+      exit_client_msg(cptr, sptr, &me,
+                      "Killed (%s (%s))",
+                      feature_str(FEAT_HIS_SERVERNAME), type);
     }
-    return 0;
-  }
-
-  /* 
-   * Check if this is a LOCAL user trying to use a reserved (Juped)
-   * nick, if so tell him that it's a nick in use...
-   */
-  if ((!IsServer(cptr)) && isNickJuped(nick))
-  {
-    sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name, /* XXX DEAD */
-        /* parv[0] is empty when connecting */
-        EmptyString(parv[0]) ? "*" : parv[0], nick);
-    return 0;                        /* NICK message ignored */
-  }
-
-  /*
-   * 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
-   */
-   
-  acptr = FindServer(nick);
-  
-  if (acptr) { /* There is a nick collision with a server */
-    if (MyConnect(sptr))
+    else
     {
-      /* Local user trying to use a nick thats a server
-       * Return an error message and ignore the command
+      /* If the origin is a server, this was a new client, so we only
+       * send the KILL in the direction it came from.  We have no
+       * client record that we would have to clean up.
        */
-      sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name, /* XXX DEAD */
-          EmptyString(parv[0]) ? "*" : parv[0], nick);
-      return 0;                        /* NICK message ignored */
+      sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s)",
+                    parv[parc - 2], cli_name(&me), type);
     }
-    
-    /*
-     * We have a nickname trying to use the same name as
-     * a server. Send out a nick collision KILL to remove
-     * the nickname. As long as only a KILL is sent out,
-     * there is no danger of the server being disconnected.
-     * Ultimate way to jupiter a nick ? >;-). -avalon
+    /* If the timestamps differ and we just killed sptr, we don't need to kill
+     * acptr as well.
      */
-    sendto_ops("Nick collision on %s(%s <- %s)", /* XXX DEAD */
-               sptr->name, acptr->from->name, cptr->name);
-    ServerStats->is_kill++;
-    sendto_one(cptr, "%s " TOK_KILL " %s%s :%s (%s <- %s)", /* XXX DEAD */
-               NumServ(&me), NumNick(sptr), me.name, acptr->from->name,
-               cptr->name);
-    sptr->flags |= FLAGS_KILLED;
-    return exit_client(cptr, sptr, &me, "Nick/Server collision");
-  }
-  
-  acptr = FindClient(nick);
-
-  /* No collisions?  Set the nick name and we're done */
-  if (!acptr)
-    return set_nick_name(cptr, sptr, nick, parc, parv);
-  /*
-   * 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)
-      /*
-       * Allows change of case in his/her nick
-       */
-      return set_nick_name(cptr, sptr, nick, parc, parv);
-    else
-      /*
-       * This is just ':old NICK old' type thing.
-       * Just forget the whole thing here. There is
-       * no point forwarding it to anywhere,
-       * especially since servers prior to this
-       * version would treat it as nick collision.
-       */
-      return 0;                        /* NICK Message ignored */
+    if (lastnick != cli_lastnick(acptr))
+      return 0;
   }
+  /* Tell acptr why we are killing it. */
+  send_reply(acptr, ERR_NICKCOLLISION, nick);
 
+  ServerStats->is_kill++;
+  SetFlag(acptr, FLAG_KILLED);
   /*
-   * Note: From this point forward it can be assumed that
-   * acptr != sptr (point to different client structures).
-   */
-  /*
-   * 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) && MyConnect(acptr))
-  {
-    ++ServerStats->is_ref;
-    exit_client(cptr, acptr, &me, "Overridden by other sign on");
-    return set_nick_name(cptr, sptr, nick, parc, parv);
-  }
-  /*
-   * Decide, we really have a nick collision and deal with it
-   */
-  if (!IsServer(cptr))
-  {
-    /*
-     * NICK is coming from local client connection. Just
-     * send error reply and ignore the command.
-     */
-    sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name, /* XXX DEAD */
-        /* parv[0] is empty when connecting */
-        EmptyString(parv[0]) ? "*" : parv[0], nick);
-    return 0;                        /* NICK message ignored */
-  }
-  /*
-   * NICK was coming from a server connection.
-   * This means we have a race condition (two users signing on
-   * at the same time), or two net fragments reconnecting with the same nick.
-   * The latter can happen because two different users connected
-   * or because one and the same user switched server during a net break.
-   * If the TimeStamps are equal, we kill both (or only 'new'
-   * if it was a ":server NICK new ...").
-   * Otherwise we kill the youngest when user@host differ,
-   * or the oldest when they are the same.
-   * We treat user and ~user as different, because if it wasn't
-   * a faked ~user the AUTH wouldn't have added the '~'.
-   * --Run
-   *
-   */
-  if (IsServer(sptr))
-  {
-    /*
-     * A new NICK being introduced by a neighbouring
-     * server (e.g. message type ":server NICK new ..." received)
-     */
-    differ =  (acptr->ip.s_addr != htonl(base64toint(parv[parc - 3]))) ||
-            (0 != ircd_strcmp(acptr->user->username, parv[4]));
-    sendto_ops("Nick collision on %s (%s " TIME_T_FMT " <- %s " TIME_T_FMT /* XXX DEAD */
-               " (%s user@host))", acptr->name, acptr->from->name, acptr->lastnick,
-               cptr->name, lastnick, differ ? "Different" : "Same");
-  }
-  else
-  {
-    /*
-     * A NICK change has collided (e.g. message type ":old NICK new").
-     */
-    lastnick = atoi(parv[2]);
-    differ =  (acptr->ip.s_addr != sptr->ip.s_addr) ||
-            (0 != ircd_strcmp(acptr->user->username, sptr->user->username));              
-    sendto_ops("Nick change collision from %s to %s (%s " TIME_T_FMT " <- %s " /* XXX DEAD */
-               TIME_T_FMT ")", sptr->name, acptr->name, acptr->from->name,
-               acptr->lastnick, cptr->name, lastnick);
-  }
-  /*
-   * Now remove (kill) the nick on our side if it is the youngest.
-   * If no timestamp was received, we ignore the incoming nick
-   * (and expect a KILL for our legit nick soon ):
-   * When the timestamps are equal we kill both nicks. --Run
-   * acptr->from != cptr should *always* be true (?).
+   * This exits the client we had before getting the NICK message
    */
-  if (acptr->from != cptr)
-  {
-    if ((differ && lastnick >= acptr->lastnick) ||
-        (!differ && lastnick <= acptr->lastnick))
-    {
-      if (!IsServer(sptr))
-      {
-        ServerStats->is_kill++;
-        sendto_highprot_butone(cptr, 10,        /* Kill old from outgoing servers */ /* XXX DEAD */
-                               "%s " TOK_KILL " %s%s :%s (%s <- %s (Nick collision))",
-                               NumServ(&me), NumNick(sptr), me.name, acptr->from->name,
-                               cptr->name);
-        if (MyConnect(sptr) && IsServer(cptr) && Protocol(cptr) > 9)
-          sendto_one(cptr, "%s " TOK_KILL " %s%s :%s (Ghost2)", /* XXX DEAD */
-                     NumServ(&me), NumNick(sptr), me.name);
-        sptr->flags |= FLAGS_KILLED;
-        exit_client(cptr, sptr, &me, "Nick collision (you're a ghost)");
-      }
-      if (lastnick != acptr->lastnick)
-        return 0;                /* Ignore the NICK */
-    }
-    sendto_one(acptr, err_str(ERR_NICKCOLLISION), me.name, acptr->name, nick); /* XXX DEAD */
-  }
-  ServerStats->is_kill++;
-  acptr->flags |= FLAGS_KILLED;
-  if (differ)
-  {
-    sendto_highprot_butone(cptr, 10,        /* Kill our old from outgoing servers */ /* XXX DEAD */
-                           "%s " TOK_KILL " %s%s :%s (%s <- %s (older nick overruled))",
-                           NumServ(&me), NumNick(acptr), me.name, acptr->from->name,
-                           cptr->name);
-    if (MyConnect(acptr) && IsServer(cptr) && Protocol(cptr) > 9)
-      sendto_one(cptr, "%s%s " TOK_QUIT " :Local kill by %s (Ghost)", /* XXX DEAD */
-          NumNick(acptr), me.name);
-    exit_client(cptr, acptr, &me, "Nick collision (older nick overruled)");
-  }
-  else
-  {
-    sendto_highprot_butone(cptr, 10,        /* Kill our old from outgoing servers */ /* XXX DEAD */
-                           "%s " TOK_KILL " %s%s :%s (%s <- %s (nick collision from same user@host))",
-                           NumServ(&me), NumNick(acptr), me.name, acptr->from->name,
-                           cptr->name);
-    if (MyConnect(acptr) && IsServer(cptr) && Protocol(cptr) > 9)
-      sendto_one(cptr, /* XXX DEAD */
-          "%s%s " TOK_QUIT " :Local kill by %s (Ghost: switched servers too fast)",
-          NumNick(acptr), me.name);
-    exit_client(cptr, acptr, &me, "Nick collision (You collided yourself)");
-  }
-  if (lastnick == acptr->lastnick)
+  sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (%s)",
+                        acptr, feature_str(FEAT_HIS_SERVERNAME),
+                        type);
+  exit_client_msg(cptr, acptr, &me, "Killed (%s (%s))",
+                  feature_str(FEAT_HIS_SERVERNAME), type);
+  if (lastnick == cli_lastnick(acptr))
+    return 0;
+  if (sptr == NULL)
     return 0;
-
   return set_nick_name(cptr, sptr, nick, parc, parv);
 }
-
-#endif /* 0 */