Author: Kev <klmitch@mit.edu>
[ircu2.10.12-pk.git] / ircd / m_nick.c
1 /*
2  * IRC - Internet Relay Chat, ircd/m_nick.c
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * See file AUTHORS in IRC package for additional names of
7  * the programmers.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 1, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * $Id$
24  */
25
26 /*
27  * m_functions execute protocol messages on this server:
28  *
29  *    cptr    is always NON-NULL, pointing to a *LOCAL* client
30  *            structure (with an open socket connected!). This
31  *            identifies the physical socket where the message
32  *            originated (or which caused the m_function to be
33  *            executed--some m_functions may call others...).
34  *
35  *    sptr    is the source of the message, defined by the
36  *            prefix part of the message if present. If not
37  *            or prefix not found, then sptr==cptr.
38  *
39  *            (!IsServer(cptr)) => (cptr == sptr), because
40  *            prefixes are taken *only* from servers...
41  *
42  *            (IsServer(cptr))
43  *                    (sptr == cptr) => the message didn't
44  *                    have the prefix.
45  *
46  *                    (sptr != cptr && IsServer(sptr) means
47  *                    the prefix specified servername. (?)
48  *
49  *                    (sptr != cptr && !IsServer(sptr) means
50  *                    that message originated from a remote
51  *                    user (not local).
52  *
53  *            combining
54  *
55  *            (!IsServer(sptr)) means that, sptr can safely
56  *            taken as defining the target structure of the
57  *            message in this server.
58  *
59  *    *Always* true (if 'parse' and others are working correct):
60  *
61  *    1)      sptr->from == cptr  (note: cptr->from == cptr)
62  *
63  *    2)      MyConnect(sptr) <=> sptr == cptr (e.g. sptr
64  *            *cannot* be a local connection, unless it's
65  *            actually cptr!). [MyConnect(x) should probably
66  *            be defined as (x == x->from) --msa ]
67  *
68  *    parc    number of variable parameter strings (if zero,
69  *            parv is allowed to be NULL)
70  *
71  *    parv    a NULL terminated list of parameter pointers,
72  *
73  *                    parv[0], sender (prefix string), if not present
74  *                            this points to an empty string.
75  *                    parv[1]...parv[parc-1]
76  *                            pointers to additional parameters
77  *                    parv[parc] == NULL, *always*
78  *
79  *            note:   it is guaranteed that parv[0]..parv[parc-1] are all
80  *                    non-NULL pointers.
81  */
82 #include "config.h"
83
84 #if 0
85 /*
86  * No need to include handlers.h here the signatures must match
87  * and we don't need to force a rebuild of all the handlers everytime
88  * we add a new one to the list. --Bleep
89  */
90 #include "handlers.h"
91 #endif /* 0 */
92 #include "IPcheck.h"
93 #include "client.h"
94 #include "hash.h"
95 #include "ircd.h"
96 #include "ircd_chattr.h"
97 #include "ircd_reply.h"
98 #include "ircd_string.h"
99 #include "msg.h"
100 #include "numeric.h"
101 #include "numnicks.h"
102 #include "s_debug.h"
103 #include "s_misc.h"
104 #include "s_user.h"
105 #include "send.h"
106
107 #include <assert.h>
108 #include <stdlib.h>
109 #include <string.h>
110
111 /*
112  * m_nick - message handler for local clients
113  * parv[0] = sender prefix
114  * parv[1] = nickname
115  */
116 int m_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
117 {
118   struct Client* acptr;
119   char           nick[NICKLEN + 2];
120   char*          arg;
121   char*          s;
122   const char*    client_name;
123
124   assert(0 != cptr);
125   assert(cptr == sptr);
126
127   /*
128    * parv[0] will be empty for clients connecting for the first time
129    */
130   client_name = (*(cli_name(sptr))) ? cli_name(sptr) : "*";
131
132   if (parc < 2) {
133     send_reply(sptr, ERR_NONICKNAMEGIVEN);
134     return 0;
135   }
136   /*
137    * Don't let them send make us send back a really long string of
138    * garbage
139    */
140   arg = parv[1];
141   if (strlen(arg) > NICKLEN)
142     arg[NICKLEN] = '\0';
143
144   if ((s = strchr(arg, '~')))
145     *s = '\0';
146
147   strcpy(nick, arg);
148
149   /*
150    * If do_nick_name() returns a null name OR if the server sent a nick
151    * name and do_nick_name() changed it in some way (due to rules of nick
152    * creation) then reject it. If from a server and we reject it,
153    * and KILL it. -avalon 4/4/92
154    */
155   if (0 == do_nick_name(nick)) {
156     send_reply(sptr, ERR_ERRONEUSNICKNAME, arg);
157     return 0;
158   }
159
160   /* 
161    * Check if this is a LOCAL user trying to use a reserved (Juped)
162    * nick, if so tell him that it's a nick in use...
163    */
164   if (isNickJuped(nick)) {
165     send_reply(sptr, ERR_NICKNAMEINUSE, nick);
166     return 0;                        /* NICK message ignored */
167   }
168
169   if (!(acptr = FindClient(nick))) {
170     /*
171      * No collisions, all clear...
172      */
173     return set_nick_name(cptr, sptr, nick, parc, parv);
174   }
175   if (IsServer(acptr)) {
176     send_reply(sptr, ERR_NICKNAMEINUSE, nick);
177     return 0;                        /* NICK message ignored */
178   }
179   /*
180    * If acptr == sptr, then we have a client doing a nick
181    * change between *equivalent* nicknames as far as server
182    * is concerned (user is changing the case of his/her
183    * nickname or somesuch)
184    */
185   if (acptr == sptr) {
186     /*
187      * If acptr == sptr, then we have a client doing a nick
188      * change between *equivalent* nicknames as far as server
189      * is concerned (user is changing the case of his/her
190      * nickname or somesuch)
191      */
192     if (0 != strcmp(cli_name(acptr), nick)) {
193       /*
194        * Allows change of case in his/her nick
195        */
196       return set_nick_name(cptr, sptr, nick, parc, parv);
197     }
198     /*
199      * This is just ':old NICK old' type thing.
200      * Just forget the whole thing here. There is
201      * no point forwarding it to anywhere,
202      * especially since servers prior to this
203      * version would treat it as nick collision.
204      */
205     return 0;
206   }
207   /*
208    * Note: From this point forward it can be assumed that
209    * acptr != sptr (point to different client structures).
210    */
211   assert(acptr != sptr);
212   /*
213    * If the older one is "non-person", the new entry is just
214    * allowed to overwrite it. Just silently drop non-person,
215    * and proceed with the nick. This should take care of the
216    * "dormant nick" way of generating collisions...
217    *
218    * XXX - hmmm can this happen after one is registered?
219    */
220   if (IsUnknown(acptr) && MyConnect(acptr)) {
221     ++ServerStats->is_ref;
222     IPcheck_connect_fail(cli_ip(acptr));
223     exit_client(cptr, acptr, &me, "Overridden by other sign on");
224     return set_nick_name(cptr, sptr, nick, parc, parv);
225   }
226   /*
227    * NICK is coming from local client connection. Just
228    * send error reply and ignore the command.
229    */
230   send_reply(sptr, ERR_NICKNAMEINUSE, nick);
231   return 0;                        /* NICK message ignored */
232 }
233
234
235 /*
236  * ms_nick - server message handler for nicks
237  * parv[0] = sender prefix
238  * parv[1] = nickname
239  *
240  * If from server, source is client:
241  *   parv[2] = timestamp
242  *
243  * Source is server:
244  *   parv[2] = hopcount
245  *   parv[3] = timestamp
246  *   parv[4] = username
247  *   parv[5] = hostname
248  *   parv[6] = umode (optional)
249  *   parv[parc-4] = %<lastmod>:<mask>   <- Only if matching GLINE
250  *   parv[parc-3] = IP#                 <- Only Protocol >= 10
251  *   parv[parc-2] = YXX, numeric nick   <- Only Protocol >= 10
252  *   parv[parc-1] = info
253  *   parv[0] = server
254  */
255 int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
256 {
257   struct Client* acptr;
258   char           nick[NICKLEN + 2];
259   time_t         lastnick = 0;
260   int            differ = 1;
261
262   assert(0 != cptr);
263   assert(0 != sptr);
264   assert(IsServer(cptr));
265
266   if ((IsServer(sptr) && parc < 8) || parc < 3) {
267     sendto_opmask_butone(0, SNO_OLDSNO, "bad NICK param count for %s from %C",
268                          parv[1], cptr);
269     return need_more_params(sptr, "NICK");
270   }
271
272   ircd_strncpy(nick, parv[1], NICKLEN);
273   nick[NICKLEN] = '\0';
274
275   if (IsServer(sptr)) {
276     lastnick = atoi(parv[3]);
277     if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) 
278       cli_serv(sptr)->lag = TStime() - lastnick;
279   }
280   else {
281     lastnick = atoi(parv[2]); 
282     if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr))
283       cli_serv(cli_user(sptr)->server)->lag = TStime() - lastnick;
284   }
285   /*
286    * If do_nick_name() returns a null name OR if the server sent a nick
287    * name and do_nick_name() changed it in some way (due to rules of nick
288    * creation) then reject it. If from a server and we reject it,
289    * and KILL it. -avalon 4/4/92
290    */
291   if (0 == do_nick_name(nick) || 0 != strcmp(nick, parv[1])) {
292     send_reply(sptr, ERR_ERRONEUSNICKNAME, parv[1]);
293
294     ++ServerStats->is_kill;
295     sendto_opmask_butone(0, SNO_OLDSNO, "Bad Nick: %s From: %s %C", parv[1],
296                          parv[0], cptr);
297     sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s <- %s[%s])",
298                   IsServer(sptr) ? parv[parc - 2] : parv[0], cli_name(&me), parv[1],
299                   nick, cli_name(cptr));
300     if (!IsServer(sptr)) {
301       /*
302        * bad nick _change_
303        */
304       sendcmdto_serv_butone(&me, CMD_KILL, 0, "%s :%s (%s <- %s!%s@%s)",
305                             parv[0], cli_name(&me), cli_name(cptr), parv[0],
306                             cli_user(sptr) ? cli_username(sptr) : "",
307                             cli_user(sptr) ? cli_name(cli_user(sptr)->server) :
308                             cli_name(cptr));
309     }
310     return 0;
311   }
312   /*
313    * Check against nick name collisions.
314    *
315    * Put this 'if' here so that the nesting goes nicely on the screen :)
316    * We check against server name list before determining if the nickname
317    * is present in the nicklist (due to the way the below for loop is
318    * constructed). -avalon
319    */
320    
321   acptr = FindClient(nick);
322   if (!acptr) {
323     /*
324      * No collisions, all clear...
325      */
326     return set_nick_name(cptr, sptr, nick, parc, parv);
327   }
328   assert(0 != acptr);
329
330   if (IsServer(acptr)) {
331     /*
332      * We have a nickname trying to use the same name as
333      * a server. Send out a nick collision KILL to remove
334      * the nickname. As long as only a KILL is sent out,
335      * there is no danger of the server being disconnected.
336      * Ultimate way to jupiter a nick ? >;-). -avalon
337      */
338     sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C(%C <- %C)", sptr,
339                          cli_from(acptr), cptr);
340     ++ServerStats->is_kill;
341
342     sendcmdto_one(&me, CMD_KILL, cptr, "%C :%s (%s <- %s)", sptr, cli_name(&me),
343                   cli_name(cli_from(acptr)), cli_name(cptr));
344
345     cli_flags(sptr) |= FLAGS_KILLED;
346     /*
347      * if sptr is a server it is exited here, nothing else to do
348      */
349     return exit_client(cptr, sptr, &me, "Nick/Server collision");
350   }
351
352   /*
353    * If acptr == sptr, then we have a client doing a nick
354    * change between *equivalent* nicknames as far as server
355    * is concerned (user is changing the case of his/her
356    * nickname or somesuch)
357    */
358   if (acptr == sptr) {
359     if (strcmp(cli_name(acptr), nick) != 0)
360       /*
361        * Allows change of case in his/her nick
362        */
363       return set_nick_name(cptr, sptr, nick, parc, parv);
364     else
365       /*
366        * This is just ':old NICK old' type thing.
367        * Just forget the whole thing here. There is
368        * no point forwarding it to anywhere,
369        * especially since servers prior to this
370        * version would treat it as nick collision.
371        */
372       return 0;                        /* NICK Message ignored */
373   }
374
375   /*
376    * Note: From this point forward it can be assumed that
377    * acptr != sptr (point to different client structures).
378    */
379   assert(acptr != sptr);
380   /*
381    * If the older one is "non-person", the new entry is just
382    * allowed to overwrite it. Just silently drop non-person,
383    * and proceed with the nick. This should take care of the
384    * "dormant nick" way of generating collisions...
385    */
386   if (IsUnknown(acptr) && MyConnect(acptr)) {
387     ++ServerStats->is_ref;
388     IPcheck_connect_fail(cli_ip(acptr));
389     exit_client(cptr, acptr, &me, "Overridden by other sign on");
390     return set_nick_name(cptr, sptr, nick, parc, parv);
391   }
392   /*
393    * Decide, we really have a nick collision and deal with it
394    */
395   /*
396    * NICK was coming from a server connection.
397    * This means we have a race condition (two users signing on
398    * at the same time), or two net fragments reconnecting with the same nick.
399    * The latter can happen because two different users connected
400    * or because one and the same user switched server during a net break.
401    * If the TimeStamps are equal, we kill both (or only 'new'
402    * if it was a ":server NICK new ...").
403    * Otherwise we kill the youngest when user@host differ,
404    * or the oldest when they are the same.
405    * We treat user and ~user as different, because if it wasn't
406    * a faked ~user the AUTH wouldn't have added the '~'.
407    * --Run
408    *
409    */
410   if (IsServer(sptr)) {
411     /*
412      * A new NICK being introduced by a neighbouring
413      * server (e.g. message type ":server NICK new ..." received)
414      *
415      * compare IP address and username
416      */
417     differ =  (cli_ip(acptr).s_addr != htonl(base64toint(parv[parc - 3]))) ||
418               (0 != ircd_strcmp(cli_user(acptr)->username, parv[4]));
419     sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C (%C %Tu <- "
420                          "%C %Tu (%s user@host))", acptr, cli_from(acptr),
421                          cli_lastnick(acptr), cptr, lastnick,
422                          differ ? "Different" : "Same");
423   }
424   else {
425     /*
426      * A NICK change has collided (e.g. message type ":old NICK new").
427      *
428      * compare IP address and username
429      */
430     differ =  (cli_ip(acptr).s_addr != cli_ip(sptr).s_addr) ||
431               (0 != ircd_strcmp(cli_user(acptr)->username, cli_user(sptr)->username));              
432     sendto_opmask_butone(0, SNO_OLDSNO, "Nick change collision from %C to "
433                          "%C (%C %Tu <- %C %Tu)", sptr, acptr, cli_from(acptr),
434                          cli_lastnick(acptr), cptr, lastnick);
435   }
436   /*
437    * Now remove (kill) the nick on our side if it is the youngest.
438    * If no timestamp was received, we ignore the incoming nick
439    * (and expect a KILL for our legit nick soon ):
440    * When the timestamps are equal we kill both nicks. --Run
441    * acptr->from != cptr should *always* be true (?).
442    *
443    * This exits the client sending the NICK message
444    */
445   if (cli_from(acptr) != cptr) {
446     if ((differ && lastnick >= cli_lastnick(acptr)) ||
447         (!differ && lastnick <= cli_lastnick(acptr))) {
448       if (!IsServer(sptr)) {
449         ++ServerStats->is_kill;
450         sendcmdto_serv_butone(&me, CMD_KILL, sptr, "%C :%s (%s <- %s (Nick "
451                               "collision))", sptr, cli_name(&me), cli_name(cli_from(acptr)),
452                               cli_name(cptr));
453         assert(!MyConnect(sptr));
454
455         cli_flags(sptr) |= FLAGS_KILLED;
456         exit_client(cptr, sptr, &me, "Nick collision (you're a ghost)");
457         /*
458          * we have killed sptr off, zero out it's pointer so if it's used
459          * again we'll know about it --Bleep
460          */
461         sptr = 0;
462       }
463       if (lastnick != cli_lastnick(acptr))
464         return 0;                /* Ignore the NICK */
465     }
466     send_reply(acptr, ERR_NICKCOLLISION, nick);
467   }
468
469   ++ServerStats->is_kill;
470   cli_flags(acptr) |= FLAGS_KILLED;
471   /*
472    * This exits the client we had before getting the NICK message
473    */
474   if (differ) {
475     sendcmdto_serv_butone(&me, CMD_KILL, acptr, "%C :%s (%s <- %s (older "
476                           "nick overruled))", acptr, cli_name(&me),
477                           cli_name(cli_from(acptr)), cli_name(cptr));
478     if (MyConnect(acptr))
479       sendcmdto_one(acptr, CMD_QUIT, cptr, ":Local kill by %s (Ghost)",
480                     cli_name(&me));
481     exit_client(cptr, acptr, &me, "Nick collision (older nick overruled)");
482   }
483   else {
484     sendcmdto_serv_butone(&me, CMD_KILL, acptr, "%C :%s (%s <- %s (nick "
485                           "collision from same user@host))", acptr, cli_name(&me),
486                           cli_name(cli_from(acptr)), cli_name(cptr));
487     if (MyConnect(acptr))
488       sendcmdto_one(acptr, CMD_QUIT, cptr, ":Local kill by %s (Ghost: ",
489                     "switched servers too fast)", cli_name(&me));
490     exit_client(cptr, acptr, &me, "Nick collision (You collided yourself)");
491   }
492   if (lastnick == cli_lastnick(acptr))
493     return 0;
494
495   assert(0 != sptr);
496   return set_nick_name(cptr, sptr, nick, parc, parv);
497 }
498
499 #if 0
500 /*
501  * m_nick
502  *
503  * parv[0] = sender prefix
504  * parv[1] = nickname
505  *
506  * If from server, source is client:
507  *   parv[2] = timestamp
508  *
509  * Source is server:
510  *   parv[2] = hopcount
511  *   parv[3] = timestamp
512  *   parv[4] = username
513  *   parv[5] = hostname
514  *   parv[6] = umode (optional)
515  *   parv[parc-3] = IP#                 <- Only Protocol >= 10
516  *   parv[parc-2] = YXX, numeric nick   <- Only Protocol >= 10
517  *   parv[parc-1] = info
518  *   parv[0] = server
519  */
520 int m_nick(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
521 {
522   struct Client* acptr;
523   char           nick[NICKLEN + 2];
524   char*          s;
525   time_t         lastnick = 0;
526   int            differ = 1;
527
528   if (parc < 2) {
529     sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]); /* XXX DEAD */
530     return 0;
531   }
532   else if ((IsServer(sptr) && parc < 8) || (IsServer(cptr) && parc < 3))
533   {
534     need_more_params(sptr, "NICK");
535     sendto_ops("bad NICK param count for %s from %s", parv[1], cptr->name); /* XXX DEAD */
536     return 0;
537   }
538   if (MyConnect(sptr) && (s = strchr(parv[1], '~')))
539     *s = '\0';
540   ircd_strncpy(nick, parv[1], NICKLEN);
541   nick[NICKLEN] = '\0';
542   if (IsServer(cptr)) {
543     if (IsServer(sptr)) {
544       lastnick = atoi(parv[3]);
545       if (lastnick > OLDEST_TS) 
546         sptr->serv->lag = TStime() - lastnick;
547     } else {
548       lastnick = atoi(parv[2]); 
549       if (lastnick > OLDEST_TS)
550        sptr->user->server->serv->lag = TStime() - lastnick;
551     }
552   }
553   /*
554    * If do_nick_name() returns a null name OR if the server sent a nick
555    * name and do_nick_name() changed it in some way (due to rules of nick
556    * creation) then reject it. If from a server and we reject it,
557    * and KILL it. -avalon 4/4/92
558    */
559   if (do_nick_name(nick) == 0 || (IsServer(cptr) && strcmp(nick, parv[1])))
560   {
561     sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, parv[0], parv[1]); /* XXX DEAD */
562
563     if (IsServer(cptr))
564     {
565       ServerStats->is_kill++;
566       sendto_ops("Bad Nick: %s From: %s %s", /* XXX DEAD */
567           parv[1], parv[0], cptr->name);
568       sendto_one(cptr, "%s " TOK_KILL " %s :%s (%s <- %s[%s])", /* XXX DEAD */
569             NumServ(&me), IsServer(sptr) ? parv[parc - 2] : parv[0], me.name,
570             parv[1], nick, cptr->name);
571       if (!IsServer(sptr))        /* bad nick _change_ */
572       {
573         sendto_highprot_butone(&me, 10, "%s " TOK_KILL " %s :%s (%s <- %s!%s@%s)", /* XXX DEAD */
574             NumServ(&me), parv[0], me.name, cptr->name,
575             parv[0], sptr->user ? sptr->username : "",
576             sptr->user ? sptr->user->server->name : cptr->name);
577       }
578     }
579     return 0;
580   }
581
582   /* 
583    * Check if this is a LOCAL user trying to use a reserved (Juped)
584    * nick, if so tell him that it's a nick in use...
585    */
586   if ((!IsServer(cptr)) && isNickJuped(nick))
587   {
588     sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name, /* XXX DEAD */
589         /* parv[0] is empty when connecting */
590         EmptyString(parv[0]) ? "*" : parv[0], nick);
591     return 0;                        /* NICK message ignored */
592   }
593
594   /*
595    * Check against nick name collisions.
596    *
597    * Put this 'if' here so that the nesting goes nicely on the screen :)
598    * We check against server name list before determining if the nickname
599    * is present in the nicklist (due to the way the below for loop is
600    * constructed). -avalon
601    */
602    
603   acptr = FindServer(nick);
604   
605   if (acptr) { /* There is a nick collision with a server */
606     if (MyConnect(sptr))
607     {
608       /* Local user trying to use a nick thats a server
609        * Return an error message and ignore the command
610        */
611       sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name, /* XXX DEAD */
612           EmptyString(parv[0]) ? "*" : parv[0], nick);
613       return 0;                        /* NICK message ignored */
614     }
615     
616     /*
617      * We have a nickname trying to use the same name as
618      * a server. Send out a nick collision KILL to remove
619      * the nickname. As long as only a KILL is sent out,
620      * there is no danger of the server being disconnected.
621      * Ultimate way to jupiter a nick ? >;-). -avalon
622      */
623     sendto_ops("Nick collision on %s(%s <- %s)", /* XXX DEAD */
624                sptr->name, acptr->from->name, cptr->name);
625     ServerStats->is_kill++;
626     sendto_one(cptr, "%s " TOK_KILL " %s%s :%s (%s <- %s)", /* XXX DEAD */
627                NumServ(&me), NumNick(sptr), me.name, acptr->from->name,
628                cptr->name);
629     sptr->flags |= FLAGS_KILLED;
630     return exit_client(cptr, sptr, &me, "Nick/Server collision");
631   }
632   
633   acptr = FindClient(nick);
634
635   /* No collisions?  Set the nick name and we're done */
636   if (!acptr)
637     return set_nick_name(cptr, sptr, nick, parc, parv);
638   /*
639    * If acptr == sptr, then we have a client doing a nick
640    * change between *equivalent* nicknames as far as server
641    * is concerned (user is changing the case of his/her
642    * nickname or somesuch)
643    */
644   if (acptr == sptr)
645   {
646     if (strcmp(acptr->name, nick) != 0)
647       /*
648        * Allows change of case in his/her nick
649        */
650       return set_nick_name(cptr, sptr, nick, parc, parv);
651     else
652       /*
653        * This is just ':old NICK old' type thing.
654        * Just forget the whole thing here. There is
655        * no point forwarding it to anywhere,
656        * especially since servers prior to this
657        * version would treat it as nick collision.
658        */
659       return 0;                        /* NICK Message ignored */
660   }
661
662   /*
663    * Note: From this point forward it can be assumed that
664    * acptr != sptr (point to different client structures).
665    */
666   /*
667    * If the older one is "non-person", the new entry is just
668    * allowed to overwrite it. Just silently drop non-person,
669    * and proceed with the nick. This should take care of the
670    * "dormant nick" way of generating collisions...
671    */
672   if (IsUnknown(acptr) && MyConnect(acptr))
673   {
674     ++ServerStats->is_ref;
675     IPcheck_connect_fail(acptr->ip);
676     exit_client(cptr, acptr, &me, "Overridden by other sign on");
677     return set_nick_name(cptr, sptr, nick, parc, parv);
678   }
679   /*
680    * Decide, we really have a nick collision and deal with it
681    */
682   if (!IsServer(cptr))
683   {
684     /*
685      * NICK is coming from local client connection. Just
686      * send error reply and ignore the command.
687      */
688     sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name, /* XXX DEAD */
689         /* parv[0] is empty when connecting */
690         EmptyString(parv[0]) ? "*" : parv[0], nick);
691     return 0;                        /* NICK message ignored */
692   }
693   /*
694    * NICK was coming from a server connection.
695    * This means we have a race condition (two users signing on
696    * at the same time), or two net fragments reconnecting with the same nick.
697    * The latter can happen because two different users connected
698    * or because one and the same user switched server during a net break.
699    * If the TimeStamps are equal, we kill both (or only 'new'
700    * if it was a ":server NICK new ...").
701    * Otherwise we kill the youngest when user@host differ,
702    * or the oldest when they are the same.
703    * We treat user and ~user as different, because if it wasn't
704    * a faked ~user the AUTH wouldn't have added the '~'.
705    * --Run
706    *
707    */
708   if (IsServer(sptr))
709   {
710     /*
711      * A new NICK being introduced by a neighbouring
712      * server (e.g. message type ":server NICK new ..." received)
713      */
714     differ =  (acptr->ip.s_addr != htonl(base64toint(parv[parc - 3]))) ||
715             (0 != ircd_strcmp(acptr->user->username, parv[4]));
716     sendto_ops("Nick collision on %s (%s " TIME_T_FMT " <- %s " TIME_T_FMT /* XXX DEAD */
717                " (%s user@host))", acptr->name, acptr->from->name, acptr->lastnick,
718                cptr->name, lastnick, differ ? "Different" : "Same");
719   }
720   else
721   {
722     /*
723      * A NICK change has collided (e.g. message type ":old NICK new").
724      */
725     lastnick = atoi(parv[2]);
726     differ =  (acptr->ip.s_addr != sptr->ip.s_addr) ||
727             (0 != ircd_strcmp(acptr->user->username, sptr->user->username));              
728     sendto_ops("Nick change collision from %s to %s (%s " TIME_T_FMT " <- %s " /* XXX DEAD */
729                TIME_T_FMT ")", sptr->name, acptr->name, acptr->from->name,
730                acptr->lastnick, cptr->name, lastnick);
731   }
732   /*
733    * Now remove (kill) the nick on our side if it is the youngest.
734    * If no timestamp was received, we ignore the incoming nick
735    * (and expect a KILL for our legit nick soon ):
736    * When the timestamps are equal we kill both nicks. --Run
737    * acptr->from != cptr should *always* be true (?).
738    */
739   if (acptr->from != cptr)
740   {
741     if ((differ && lastnick >= acptr->lastnick) ||
742         (!differ && lastnick <= acptr->lastnick))
743     {
744       if (!IsServer(sptr))
745       {
746         ServerStats->is_kill++;
747         sendto_highprot_butone(cptr, 10,        /* Kill old from outgoing servers */ /* XXX DEAD */
748                                "%s " TOK_KILL " %s%s :%s (%s <- %s (Nick collision))",
749                                NumServ(&me), NumNick(sptr), me.name, acptr->from->name,
750                                cptr->name);
751         if (MyConnect(sptr) && IsServer(cptr) && Protocol(cptr) > 9)
752           sendto_one(cptr, "%s " TOK_KILL " %s%s :%s (Ghost2)", /* XXX DEAD */
753                      NumServ(&me), NumNick(sptr), me.name);
754         sptr->flags |= FLAGS_KILLED;
755         exit_client(cptr, sptr, &me, "Nick collision (you're a ghost)");
756       }
757       if (lastnick != acptr->lastnick)
758         return 0;                /* Ignore the NICK */
759     }
760     sendto_one(acptr, err_str(ERR_NICKCOLLISION), me.name, acptr->name, nick); /* XXX DEAD */
761   }
762   ServerStats->is_kill++;
763   acptr->flags |= FLAGS_KILLED;
764   if (differ)
765   {
766     sendto_highprot_butone(cptr, 10,        /* Kill our old from outgoing servers */ /* XXX DEAD */
767                            "%s " TOK_KILL " %s%s :%s (%s <- %s (older nick overruled))",
768                            NumServ(&me), NumNick(acptr), me.name, acptr->from->name,
769                            cptr->name);
770     if (MyConnect(acptr) && IsServer(cptr) && Protocol(cptr) > 9)
771       sendto_one(cptr, "%s%s " TOK_QUIT " :Local kill by %s (Ghost)", /* XXX DEAD */
772           NumNick(acptr), me.name);
773     exit_client(cptr, acptr, &me, "Nick collision (older nick overruled)");
774   }
775   else
776   {
777     sendto_highprot_butone(cptr, 10,        /* Kill our old from outgoing servers */ /* XXX DEAD */
778                            "%s " TOK_KILL " %s%s :%s (%s <- %s (nick collision from same user@host))",
779                            NumServ(&me), NumNick(acptr), me.name, acptr->from->name,
780                            cptr->name);
781     if (MyConnect(acptr) && IsServer(cptr) && Protocol(cptr) > 9)
782       sendto_one(cptr, /* XXX DEAD */
783           "%s%s " TOK_QUIT " :Local kill by %s (Ghost: switched servers too fast)",
784           NumNick(acptr), me.name);
785     exit_client(cptr, acptr, &me, "Nick collision (You collided yourself)");
786   }
787   if (lastnick == acptr->lastnick)
788     return 0;
789
790   return set_nick_name(cptr, sptr, nick, parc, parv);
791 }
792
793 #endif /* 0 */