]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc-login.c
c3f32b7e8b2516fe383b6482341817ce668269a6
[ngircd-alex.git] / src / ngircd / irc-login.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11
12 #include "portab.h"
13
14 /**
15  * @file
16  * Login and logout
17  */
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <strings.h>
25
26 #include "conn-func.h"
27 #include "class.h"
28 #include "conf.h"
29 #include "channel.h"
30 #include "log.h"
31 #include "login.h"
32 #include "messages.h"
33 #include "parse.h"
34 #include "irc.h"
35 #include "irc-info.h"
36 #include "irc-write.h"
37
38 #include "exp.h"
39 #include "irc-login.h"
40
41 static void Kill_Nick PARAMS((char *Nick, char *Reason));
42 static void Change_Nick PARAMS((CLIENT * Origin, CLIENT * Target,
43                                 char *NewNick, bool RegisterWhowas));
44
45
46 /**
47  * Handler for the IRC "PASS" command.
48  *
49  * See RFC 2813 section 4.1.1, and RFC 2812 section 3.1.1.
50  *
51  * @param Client        The client from which this command has been received.
52  * @param Req           Request structure with prefix and all parameters.
53  * @returns             CONNECTED or DISCONNECTED.
54  */
55 GLOBAL bool
56 IRC_PASS( CLIENT *Client, REQUEST *Req )
57 {
58         char *type, *orig_flags;
59         int protohigh, protolow;
60
61         assert( Client != NULL );
62         assert( Req != NULL );
63
64         /* Return an error if this is not a local client */
65         if (Client_Conn(Client) <= NONE)
66                 return IRC_WriteStrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
67                                           Client_ID(Client), Req->command);
68
69         if (Client_Type(Client) == CLIENT_UNKNOWN && Req->argc == 1) {
70                 /* Not yet registered "unknown" connection, PASS with one
71                  * argument: either a regular client, service, or server
72                  * using the old RFC 1459 section 4.1.1 syntax. */
73                 LogDebug("Connection %d: got PASS command (RFC 1459) ...",
74                          Client_Conn(Client));
75         } else if ((Client_Type(Client) == CLIENT_UNKNOWN ||
76                     Client_Type(Client) == CLIENT_UNKNOWNSERVER) &&
77                    (Req->argc == 3 || Req->argc == 4)) {
78                 /* Not yet registered "unknown" connection or outgoing server
79                  * link, PASS with three or four argument: server using the
80                  * RFC 2813 section 4.1.1 syntax. */
81                 LogDebug("Connection %d: got PASS command (RFC 2813, new server link) ...",
82                          Client_Conn(Client));
83         } else if (Client_Type(Client) == CLIENT_UNKNOWN ||
84                    Client_Type(Client) == CLIENT_UNKNOWNSERVER) {
85                 /* Unregistered connection, but wrong number of arguments: */
86                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
87                                           Client_ID(Client), Req->command);
88         } else {
89                 /* Registered connection, PASS command is not allowed! */
90                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
91                                           Client_ID(Client));
92         }
93
94         Conn_SetPassword(Client_Conn(Client), Req->argv[0]);
95
96         /* Protocol version */
97         if (Req->argc >= 2 && strlen(Req->argv[1]) >= 4) {
98                 int c2, c4;
99
100                 c2 = Req->argv[1][2];
101                 c4 = Req->argv[1][4];
102
103                 Req->argv[1][4] = '\0';
104                 protolow = atoi(&Req->argv[1][2]);
105                 Req->argv[1][2] = '\0';
106                 protohigh = atoi(Req->argv[1]);
107
108                 Req->argv[1][2] = c2;
109                 Req->argv[1][4] = c4;
110
111                 Client_SetType(Client, CLIENT_GOTPASS_2813);
112         } else {
113                 protohigh = protolow = 0;
114                 Client_SetType(Client, CLIENT_GOTPASS);
115         }
116
117         /* Protocol type, see doc/Protocol.txt */
118         if (Req->argc >= 2 && strlen(Req->argv[1]) > 4)
119                 type = &Req->argv[1][4];
120         else
121                 type = NULL;
122
123         /* Protocol flags/options */
124         if (Req->argc >= 4)
125                 orig_flags = Req->argv[3];
126         else
127                 orig_flags = "";
128
129         /* Implementation, version and IRC+ flags */
130         if (Req->argc >= 3) {
131                 char *impl, *ptr, *serverver, *flags;
132
133                 impl = Req->argv[2];
134                 ptr = strchr(impl, '|');
135                 if (ptr)
136                         *ptr = '\0';
137
138                 if (type && strcmp(type, PROTOIRCPLUS) == 0) {
139                         /* The peer seems to be a server which supports the
140                          * IRC+ protocol (see doc/Protocol.txt). */
141                         serverver = ptr ? ptr + 1 : "?";
142                         flags = strchr(ptr ? serverver : impl, ':');
143                         if (flags) {
144                                 *flags = '\0';
145                                 flags++;
146                         } else
147                                 flags = "";
148                         Log(LOG_INFO,
149                             "Peer on conenction %d announces itself as %s-%s using protocol %d.%d/IRC+ (flags: \"%s\").",
150                             Client_Conn(Client), impl, serverver,
151                             protohigh, protolow, flags);
152                 } else {
153                         /* The peer seems to be a server supporting the
154                          * "original" IRC protocol (RFC 2813). */
155                         if (strchr(orig_flags, 'Z'))
156                                 flags = "Z";
157                         else
158                                 flags = "";
159                         Log(LOG_INFO,
160                             "Peer on connection %d announces itself as \"%s\" using protocol %d.%d (flags: \"%s\").",
161                             Client_Conn(Client), impl,
162                             protohigh, protolow, flags);
163                 }
164                 Client_SetFlags(Client, flags);
165         }
166
167         return CONNECTED;
168 } /* IRC_PASS */
169
170
171 /**
172  * Handler for the IRC "NICK" command.
173  *
174  * See RFC 2812, 3.1.2 "Nick message", and RFC 2813, 4.1.3 "Nick".
175  *
176  * This function implements the IRC command "NICK" which is used to register
177  * with the server, to change already registered nicknames and to introduce
178  * new users which are connected to other servers.
179  *
180  * @param Client        The client from which this command has been received.
181  * @param Req           Request structure with prefix and all parameters.
182  * @returns             CONNECTED or DISCONNECTED.
183  */
184 GLOBAL bool
185 IRC_NICK( CLIENT *Client, REQUEST *Req )
186 {
187         CLIENT *intr_c, *target, *c;
188         char *nick, *user, *hostname, *modes, *info;
189         int token, hops;
190
191         assert( Client != NULL );
192         assert( Req != NULL );
193
194         /* Some IRC clients, for example BitchX, send the NICK and USER
195          * commands in the wrong order ... */
196         if(Client_Type(Client) == CLIENT_UNKNOWN
197             || Client_Type(Client) == CLIENT_GOTPASS
198             || Client_Type(Client) == CLIENT_GOTNICK
199 #ifndef STRICT_RFC
200             || Client_Type(Client) == CLIENT_GOTUSER
201 #endif
202             || Client_Type(Client) == CLIENT_USER
203             || Client_Type(Client) == CLIENT_SERVICE
204             || (Client_Type(Client) == CLIENT_SERVER && Req->argc == 1))
205         {
206                 /* User registration or change of nickname */
207
208                 /* Wrong number of arguments? */
209                 if( Req->argc != 1 )
210                         return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG,
211                                                    Client_ID( Client ),
212                                                    Req->command );
213
214                 /* Search "target" client */
215                 if( Client_Type( Client ) == CLIENT_SERVER )
216                 {
217                         target = Client_Search( Req->prefix );
218                         if( ! target )
219                                 return IRC_WriteStrClient( Client,
220                                                            ERR_NOSUCHNICK_MSG,
221                                                            Client_ID( Client ),
222                                                            Req->argv[0] );
223                 }
224                 else
225                 {
226                         /* Is this a restricted client? */
227                         if( Client_HasMode( Client, 'r' ))
228                                 return IRC_WriteStrClient( Client,
229                                                            ERR_RESTRICTED_MSG,
230                                                            Client_ID( Client ));
231
232                         target = Client;
233                 }
234
235 #ifndef STRICT_RFC
236                 /* If the clients tries to change to its own nickname we won't
237                  * do anything. This is how the original ircd behaves and some
238                  * clients (for example Snak) expect it to be like this.
239                  * But I doubt that this is "really the right thing" ... */
240                 if( strcmp( Client_ID( target ), Req->argv[0] ) == 0 )
241                         return CONNECTED;
242 #endif
243
244                 /* Check that the new nickname is available. Special case:
245                  * the client only changes from/to upper to lower case. */
246                 if( strcasecmp( Client_ID( target ), Req->argv[0] ) != 0 )
247                 {
248                         if( ! Client_CheckNick( target, Req->argv[0] ))
249                                 return CONNECTED;
250                 }
251
252                 if (Client_Type(target) != CLIENT_USER &&
253                     Client_Type(target) != CLIENT_SERVICE &&
254                     Client_Type(target) != CLIENT_SERVER) {
255                         /* New client */
256                         LogDebug("Connection %d: got valid NICK command ...",
257                              Client_Conn( Client ));
258
259                         /* Register new nickname of this client */
260                         Client_SetID( target, Req->argv[0] );
261
262 #ifndef STRICT_RFC
263                         if (Conf_AuthPing) {
264                                 Conn_SetAuthPing(Client_Conn(Client), rand());
265                                 IRC_WriteStrClient(Client, "PING :%ld",
266                                         Conn_GetAuthPing(Client_Conn(Client)));
267                                 LogDebug("Connection %d: sent AUTH PING %ld ...",
268                                         Client_Conn(Client),
269                                         Conn_GetAuthPing(Client_Conn(Client)));
270                         }
271 #endif
272
273                         /* If we received a valid USER command already then
274                          * register the new client! */
275                         if( Client_Type( Client ) == CLIENT_GOTUSER )
276                                 return Login_User( Client );
277                         else
278                                 Client_SetType( Client, CLIENT_GOTNICK );
279                 } else {
280                         /* Nickname change */
281                         Change_Nick(Client, target, Req->argv[0], true);
282                         IRC_SetPenalty(target, 2);
283                 }
284
285                 return CONNECTED;
286         } else if(Client_Type(Client) == CLIENT_SERVER ||
287                   Client_Type(Client) == CLIENT_SERVICE) {
288                 /* Server or service introduces new client */
289
290                 /* Bad number of parameters? */
291                 if (Req->argc != 2 && Req->argc != 7)
292                         return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
293                                                   Client_ID(Client), Req->command);
294
295                 if (Req->argc >= 7) {
296                         /* RFC 2813 compatible syntax */
297                         nick = Req->argv[0];
298                         hops = atoi(Req->argv[1]);
299                         user = Req->argv[2];
300                         hostname = Req->argv[3];
301                         token = atoi(Req->argv[4]);
302                         modes = Req->argv[5] + 1;
303                         info = Req->argv[6];
304                 } else {
305                         /* RFC 1459 compatible syntax */
306                         nick = Req->argv[0];
307                         hops = 1;
308                         user = Req->argv[0];
309                         hostname = Client_ID(Client);
310                         token = atoi(Req->argv[1]);
311                         modes = "";
312                         info = Req->argv[0];
313                 }
314
315                 c = Client_Search(nick);
316                 if(c) {
317                         /*
318                          * the new nick is already present on this server:
319                          * the new and the old one have to be disconnected now.
320                          */
321                         Log( LOG_ERR, "Server %s introduces already registered nick \"%s\"!", Client_ID( Client ), Req->argv[0] );
322                         Kill_Nick( Req->argv[0], "Nick collision" );
323                         return CONNECTED;
324                 }
325
326                 /* Find the Server this client is connected to */
327                 intr_c = Client_GetFromToken(Client, token);
328                 if( ! intr_c )
329                 {
330                         Log( LOG_ERR, "Server %s introduces nick \"%s\" on unknown server!?", Client_ID( Client ), Req->argv[0] );
331                         Kill_Nick( Req->argv[0], "Unknown server" );
332                         return CONNECTED;
333                 }
334
335                 c = Client_NewRemoteUser(intr_c, nick, hops, user, hostname,
336                                          token, modes, info, true);
337                 if( ! c )
338                 {
339                         /* out of memory, need to disconnect client to keep network state consistent */
340                         Log( LOG_ALERT, "Can't create client structure! (on connection %d)", Client_Conn( Client ));
341                         Kill_Nick( Req->argv[0], "Server error" );
342                         return CONNECTED;
343                 }
344
345                 /* RFC 2813: client is now fully registered, inform all the
346                  * other servers about the new user.
347                  * RFC 1459: announce the new client only after receiving the
348                  * USER command, first we need more information! */
349                 if (Req->argc < 7) {
350                         LogDebug("Client \"%s\" is being registered (RFC 1459) ...",
351                                  Client_Mask(c));
352                         Client_SetType(c, CLIENT_GOTNICK);
353                 } else
354                         Client_Introduce(Client, c, CLIENT_USER);
355
356                 return CONNECTED;
357         }
358         else return IRC_WriteStrClient( Client, ERR_ALREADYREGISTRED_MSG, Client_ID( Client ));
359 } /* IRC_NICK */
360
361
362 /**
363  * Handler for the IRC "USER" command.
364  *
365  * See RFC 2812, 3.1.3 "User message".
366  *
367  * @param Client        The client from which this command has been received.
368  * @param Req           Request structure with prefix and all parameters.
369  * @returns             CONNECTED or DISCONNECTED.
370  */
371 GLOBAL bool
372 IRC_USER(CLIENT * Client, REQUEST * Req)
373 {
374         CLIENT *c;
375         char *ptr;
376
377         assert(Client != NULL);
378         assert(Req != NULL);
379
380         if (Client_Type(Client) == CLIENT_GOTNICK ||
381 #ifndef STRICT_RFC
382             Client_Type(Client) == CLIENT_UNKNOWN ||
383 #endif
384             Client_Type(Client) == CLIENT_GOTPASS)
385         {
386                 /* New connection */
387                 if (Req->argc != 4)
388                         return IRC_WriteStrClient(Client,
389                                                   ERR_NEEDMOREPARAMS_MSG,
390                                                   Client_ID(Client),
391                                                   Req->command);
392
393                 /* User name: only alphanumeric characters and limited
394                    punctuation is allowed.*/
395                 ptr = Req->argv[0];
396                 while (*ptr) {
397                         if (!isalnum(*ptr) &&
398                             *ptr != '+' && *ptr != '-' &&
399                             *ptr != '.' && *ptr != '_') {
400                                 Conn_Close(Client_Conn(Client), NULL,
401                                            "Invalid user name", true);
402                                 return DISCONNECTED;
403                         }
404                         ptr++;
405                 }
406
407 #ifdef IDENTAUTH
408                 ptr = Client_User(Client);
409                 if (!ptr || !*ptr || *ptr == '~')
410                         Client_SetUser(Client, Req->argv[0], false);
411 #else
412                 Client_SetUser(Client, Req->argv[0], false);
413 #endif
414                 Client_SetOrigUser(Client, Req->argv[0]);
415
416                 /* "Real name" or user info text: Don't set it to the empty
417                  * string, the original ircd can't deal with such "real names"
418                  * (e. g. "USER user * * :") ... */
419                 if (*Req->argv[3])
420                         Client_SetInfo(Client, Req->argv[3]);
421                 else
422                         Client_SetInfo(Client, "-");
423
424                 LogDebug("Connection %d: got valid USER command ...",
425                     Client_Conn(Client));
426                 if (Client_Type(Client) == CLIENT_GOTNICK)
427                         return Login_User(Client);
428                 else
429                         Client_SetType(Client, CLIENT_GOTUSER);
430                 return CONNECTED;
431
432         } else if (Client_Type(Client) == CLIENT_SERVER ||
433                    Client_Type(Client) == CLIENT_SERVICE) {
434                 /* Server/service updating an user */
435                 if (Req->argc != 4)
436                         return IRC_WriteStrClient(Client,
437                                                   ERR_NEEDMOREPARAMS_MSG,
438                                                   Client_ID(Client),
439                                                   Req->command);
440                 c = Client_Search(Req->prefix);
441                 if (!c)
442                         return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
443                                                   Client_ID(Client),
444                                                   Req->prefix);
445
446                 Client_SetUser(c, Req->argv[0], true);
447                 Client_SetOrigUser(c, Req->argv[0]);
448                 Client_SetHostname(c, Req->argv[1]);
449                 Client_SetInfo(c, Req->argv[3]);
450
451                 LogDebug("Connection %d: got valid USER command for \"%s\".",
452                          Client_Conn(Client), Client_Mask(c));
453
454                 /* RFC 1459 style user registration?
455                  * Introduce client to network: */
456                 if (Client_Type(c) == CLIENT_GOTNICK)
457                         Client_Introduce(Client, c, CLIENT_USER);
458
459                 return CONNECTED;
460         } else if (Client_Type(Client) == CLIENT_USER) {
461                 /* Already registered connection */
462                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
463                                           Client_ID(Client));
464         } else {
465                 /* Unexpected/invalid connection state? */
466                 return IRC_WriteStrClient(Client, ERR_NOTREGISTERED_MSG,
467                                           Client_ID(Client));
468         }
469 } /* IRC_USER */
470
471
472 /**
473  * Handler for the IRC "SERVICE" command.
474  *
475  * This function implements IRC Services registration using the SERVICE command
476  * defined in RFC 2812 3.1.6 and RFC 2813 4.1.4.
477  *
478  * At the moment ngIRCd doesn't support directly linked services, so this
479  * function returns ERR_ERRONEUSNICKNAME when the SERVICE command has not been
480  * received from a peer server.
481  *
482  * @param Client        The client from which this command has been received.
483  * @param Req           Request structure with prefix and all parameters.
484  * @returns             CONNECTED or DISCONNECTED..
485  */
486 GLOBAL bool
487 IRC_SERVICE(CLIENT *Client, REQUEST *Req)
488 {
489         CLIENT *c, *intr_c;
490         char *nick, *user, *host, *info, *modes, *ptr;
491         int token, hops;
492
493         assert(Client != NULL);
494         assert(Req != NULL);
495
496         if (Client_Type(Client) != CLIENT_GOTPASS &&
497             Client_Type(Client) != CLIENT_SERVER)
498                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
499                                           Client_ID(Client));
500
501         if (Req->argc != 6)
502                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
503                                           Client_ID(Client), Req->command);
504
505         if (Client_Type(Client) != CLIENT_SERVER)
506                 return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
507                                   Client_ID(Client), Req->argv[0]);
508
509         /* Bad number of parameters? */
510         if (Req->argc != 6)
511                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
512                                           Client_ID(Client), Req->command);
513
514         nick = Req->argv[0];
515         user = NULL; host = NULL;
516         token = atoi(Req->argv[1]);
517         hops = atoi(Req->argv[4]);
518         info = Req->argv[5];
519
520         /* Validate service name ("nickname") */
521         c = Client_Search(nick);
522         if(c) {
523                 /* Nickname collission: disconnect (KILL) both clients! */
524                 Log(LOG_ERR, "Server %s introduces already registered service \"%s\"!",
525                     Client_ID(Client), nick);
526                 Kill_Nick(nick, "Nick collision");
527                 return CONNECTED;
528         }
529
530         /* Get the server to which the service is connected */
531         intr_c = Client_GetFromToken(Client, token);
532         if (! intr_c) {
533                 Log(LOG_ERR, "Server %s introduces service \"%s\" on unknown server!?",
534                     Client_ID(Client), nick);
535                 Kill_Nick(nick, "Unknown server");
536                 return CONNECTED;
537         }
538
539         /* Get user and host name */
540         ptr = strchr(nick, '@');
541         if (ptr) {
542                 *ptr = '\0';
543                 host = ++ptr;
544         }
545         if (!host)
546                 host = Client_Hostname(intr_c);
547         ptr = strchr(nick, '!');
548         if (ptr) {
549                 *ptr = '\0';
550                 user = ++ptr;
551         }
552         if (!user)
553                 user = nick;
554
555         /* According to RFC 2812/2813 parameter 4 <type> "is currently reserved
556          * for future usage"; but we use it to transfer the modes and check
557          * that the first character is a '+' sign and ignore it otherwise. */
558         modes = (Req->argv[3][0] == '+') ? ++Req->argv[3] : "";
559
560         c = Client_NewRemoteUser(intr_c, nick, hops, user, host,
561                                  token, modes, info, true);
562         if (! c) {
563                 /* Couldn't create client structure, so KILL the service to
564                  * keep network status consistent ... */
565                 Log(LOG_ALERT, "Can't create client structure! (on connection %d)",
566                     Client_Conn(Client));
567                 Kill_Nick(nick, "Server error");
568                 return CONNECTED;
569         }
570
571         Client_Introduce(Client, c, CLIENT_SERVICE);
572         return CONNECTED;
573 } /* IRC_SERVICE */
574
575
576 /**
577  * Handler for the IRC "WEBIRC" command.
578  *
579  * See doc/Protocol.txt, section II.4:
580  * "Update webchat/proxy client information".
581  *
582  * @param Client        The client from which this command has been received.
583  * @param Req           Request structure with prefix and all parameters.
584  * @returns             CONNECTED or DISCONNECTED.
585  */
586 GLOBAL bool
587 IRC_WEBIRC(CLIENT *Client, REQUEST *Req)
588 {
589         /* Exactly 4 parameters are requited */
590         if (Req->argc != 4)
591                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
592                                           Client_ID(Client), Req->command);
593
594         if (!Conf_WebircPwd[0] || strcmp(Req->argv[0], Conf_WebircPwd) != 0)
595                 return IRC_WriteStrClient(Client, ERR_PASSWDMISMATCH_MSG,
596                                           Client_ID(Client));
597
598         LogDebug("Connection %d: got valid WEBIRC command: user=%s, host=%s, ip=%s",
599                  Client_Conn(Client), Req->argv[1], Req->argv[2], Req->argv[3]);
600
601         Client_SetUser(Client, Req->argv[1], true);
602         Client_SetOrigUser(Client, Req->argv[1]);
603         Client_SetHostname(Client, Req->argv[2]);
604         return CONNECTED;
605 } /* IRC_WEBIRC */
606
607
608 /**
609  * Handler for the IRC "QUIT" command.
610  *
611  * See RFC 2812, 3.1.7 "Quit", and RFC 2813, 4.1.5 "Quit".
612  *
613  * @param Client        The client from which this command has been received.
614  * @param Req           Request structure with prefix and all parameters.
615  * @returns             CONNECTED or DISCONNECTED.
616  */
617 GLOBAL bool
618 IRC_QUIT( CLIENT *Client, REQUEST *Req )
619 {
620         CLIENT *target;
621         char quitmsg[LINE_LEN];
622
623         assert(Client != NULL);
624         assert(Req != NULL);
625
626         /* Wrong number of arguments? */
627         if (Req->argc > 1)
628                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
629                                           Client_ID(Client), Req->command);
630
631         if (Req->argc == 1)
632                 strlcpy(quitmsg, Req->argv[0], sizeof quitmsg);
633
634         if (Client_Type(Client) == CLIENT_SERVER) {
635                 /* Server */
636                 target = Client_Search(Req->prefix);
637                 if (!target) {
638                         Log(LOG_WARNING,
639                             "Got QUIT from %s for unknown client!?",
640                             Client_ID(Client));
641                         return CONNECTED;
642                 }
643
644                 if (target != Client) {
645                         Client_Destroy(target, "Got QUIT command.",
646                                        Req->argc == 1 ? quitmsg : NULL, true);
647                         return CONNECTED;
648                 } else {
649                         Conn_Close(Client_Conn(Client), "Got QUIT command.",
650                                    Req->argc == 1 ? quitmsg : NULL, true);
651                         return DISCONNECTED;
652                 }
653         } else {
654                 if (Req->argc == 1 && quitmsg[0] != '\"') {
655                         /* " " to avoid confusion */
656                         strlcpy(quitmsg, "\"", sizeof quitmsg);
657                         strlcat(quitmsg, Req->argv[0], sizeof quitmsg-1);
658                         strlcat(quitmsg, "\"", sizeof quitmsg );
659                 }
660
661                 /* User, Service, or not yet registered */
662                 Conn_Close(Client_Conn(Client), "Got QUIT command.",
663                            Req->argc == 1 ? quitmsg : NULL, true);
664
665                 return DISCONNECTED;
666         }
667 } /* IRC_QUIT */
668
669
670 #ifndef STRICT_RFC
671
672 /**
673  * Handler for HTTP command, e.g. GET and POST
674  *
675  * We handle these commands here to avoid the quite long timeout when
676  * some user tries to access this IRC daemon using an web browser ...
677  *
678  * @param Client        The client from which this command has been received.
679  * @param Req           Request structure with prefix and all parameters.
680  * @returns             CONNECTED or DISCONNECTED.
681  */
682 GLOBAL bool
683 IRC_QUIT_HTTP( CLIENT *Client, REQUEST *Req )
684 {
685         Req->argc = 1;
686         Req->argv[0] = "Oops, HTTP request received? This is IRC!";
687         return IRC_QUIT(Client, Req);
688 } /* IRC_QUIT_HTTP */
689
690 #endif
691
692
693 /**
694  * Handler for the IRC "PING" command.
695  *
696  * See RFC 2812, 3.7.2 "Ping message".
697  *
698  * @param Client        The client from which this command has been received.
699  * @param Req           Request structure with prefix and all parameters.
700  * @returns             CONNECTED or DISCONNECTED.
701  */
702 GLOBAL bool
703 IRC_PING(CLIENT *Client, REQUEST *Req)
704 {
705         CLIENT *target, *from;
706
707         assert(Client != NULL);
708         assert(Req != NULL);
709
710         if (Req->argc < 1)
711                 return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
712                                           Client_ID(Client));
713 #ifdef STRICT_RFC
714         /* Don't ignore additional arguments when in "strict" mode */
715         if (Req->argc > 2)
716                  return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
717                                            Client_ID(Client), Req->command);
718 #endif
719
720         if (Req->argc > 1) {
721                 /* A target has been specified ... */
722                 target = Client_Search(Req->argv[1]);
723
724                 if (!target || Client_Type(target) != CLIENT_SERVER)
725                         return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
726                                         Client_ID(Client), Req->argv[1]);
727
728                 if (target != Client_ThisServer()) {
729                         /* Ok, we have to forward the PING */
730                         if (Client_Type(Client) == CLIENT_SERVER)
731                                 from = Client_Search(Req->prefix);
732                         else
733                                 from = Client;
734                         if (!from)
735                                 return IRC_WriteStrClient(Client,
736                                                 ERR_NOSUCHSERVER_MSG,
737                                                 Client_ID(Client), Req->prefix);
738
739                         return IRC_WriteStrClientPrefix(target, from,
740                                         "PING %s :%s", Req->argv[0],
741                                         Req->argv[1] );
742                 }
743         }
744
745         if (Client_Type(Client) == CLIENT_SERVER) {
746                 if (Req->prefix)
747                         from = Client_Search(Req->prefix);
748                 else
749                         from = Client;
750         } else
751                 from = Client_ThisServer();
752         if (!from)
753                 return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
754                                         Client_ID(Client), Req->prefix);
755
756         Log(LOG_DEBUG, "Connection %d: got PING, sending PONG ...",
757             Client_Conn(Client));
758
759 #ifdef STRICT_RFC
760         return IRC_WriteStrClient(Client, "PONG %s :%s",
761                 Client_ID(from), Client_ID(Client));
762 #else
763         /* Some clients depend on the argument being returned in the PONG
764          * reply (not mentioned in any RFC, though) */
765         return IRC_WriteStrClient(Client, "PONG %s :%s",
766                 Client_ID(from), Req->argv[0]);
767 #endif
768 } /* IRC_PING */
769
770
771 /**
772  * Handler for the IRC "PONG" command.
773  *
774  * See RFC 2812, 3.7.3 "Pong message".
775  *
776  * @param Client        The client from which this command has been received.
777  * @param Req           Request structure with prefix and all parameters.
778  * @returns             CONNECTED or DISCONNECTED.
779  */
780 GLOBAL bool
781 IRC_PONG(CLIENT *Client, REQUEST *Req)
782 {
783         CLIENT *target, *from;
784         CONN_ID conn;
785 #ifndef STRICT_RFC
786         long auth_ping;
787 #endif
788         char *s;
789
790         assert(Client != NULL);
791         assert(Req != NULL);
792
793         /* Wrong number of arguments? */
794         if (Req->argc < 1) {
795                 if (Client_Type(Client) == CLIENT_USER)
796                         return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
797                                                   Client_ID(Client));
798                 else
799                         return CONNECTED;
800         }
801         if (Req->argc > 2) {
802                 if (Client_Type(Client) == CLIENT_USER)
803                         return IRC_WriteStrClient(Client,
804                                                   ERR_NEEDMOREPARAMS_MSG,
805                                                   Client_ID(Client),
806                                                   Req->command);
807                 else
808                         return CONNECTED;
809         }
810
811         /* Forward? */
812         if (Req->argc == 2 && Client_Type(Client) == CLIENT_SERVER) {
813                 target = Client_Search(Req->argv[0]);
814                 if (!target)
815                         return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
816                                         Client_ID(Client), Req->argv[0]);
817
818                 from = Client_Search(Req->prefix);
819
820                 if (target != Client_ThisServer() && target != from) {
821                         /* Ok, we have to forward the message. */
822                         if (!from)
823                                 return IRC_WriteStrClient(Client,
824                                                 ERR_NOSUCHSERVER_MSG,
825                                                 Client_ID(Client), Req->prefix);
826
827                         if (Client_Type(Client_NextHop(target)) != CLIENT_SERVER)
828                                 s = Client_ID(from);
829                         else
830                                 s = Req->argv[0];
831                         return IRC_WriteStrClientPrefix(target, from,
832                                  "PONG %s :%s", s, Req->argv[1]);
833                 }
834         }
835
836         /* The connection timestamp has already been updated when the data has
837          * been read from so socket, so we don't need to update it here. */
838
839         conn = Client_Conn(Client);
840
841 #ifndef STRICT_RFC
842         /* Check authentication PING-PONG ... */
843         auth_ping = Conn_GetAuthPing(conn);
844         if (auth_ping) {
845                 LogDebug("AUTH PONG: waiting for token \"%ld\", got \"%s\" ...",
846                          auth_ping, Req->argv[0]);
847                 if (auth_ping == atoi(Req->argv[0])) {
848                         Conn_SetAuthPing(conn, 0);
849                         if (Client_Type(Client) == CLIENT_WAITAUTHPING)
850                                 Login_User(Client);
851                 } else
852                         if (!IRC_WriteStrClient(Client,
853                                         "To connect, type /QUOTE PONG %ld",
854                                         auth_ping))
855                                 return DISCONNECTED;
856         }
857 #endif
858
859         if (Client_Type(Client) == CLIENT_SERVER && Conn_LastPing(conn) == 0) {
860                 Log(LOG_INFO,
861                     "Synchronization with \"%s\" done (connection %d): %ld seconds [%ld users, %ld channels]",
862                     Client_ID(Client), conn, time(NULL) - Conn_GetSignon(conn),
863                     Client_UserCount(), Channel_CountVisible(NULL));
864                 Conn_UpdatePing(conn);
865         } else
866                 LogDebug("Connection %d: received PONG. Lag: %ld seconds.",
867                          conn, time(NULL) - Conn_LastPing(conn));
868
869         return CONNECTED;
870 } /* IRC_PONG */
871
872
873 /**
874  * Kill all users with a specific nickname in the network.
875  *
876  * @param Nick          Nickname.
877  * @param Reason        Reason for the KILL.
878  */
879 static void
880 Kill_Nick(char *Nick, char *Reason)
881 {
882         REQUEST r;
883
884         assert (Nick != NULL);
885         assert (Reason != NULL);
886
887         r.prefix = NULL;
888         r.argv[0] = Nick;
889         r.argv[1] = Reason;
890         r.argc = 2;
891
892         Log(LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s",
893             Nick, Reason);
894
895         IRC_KILL(Client_ThisServer(), &r);
896 } /* Kill_Nick */
897
898
899 /**
900  * Change the nickname of a client.
901  *
902  * @param Origin The client which caused the nickname change.
903  * @param Target The client of which the nickname should be changed.
904  * @param NewNick The new nickname.
905  */
906 static void
907 Change_Nick(CLIENT *Origin, CLIENT *Target, char *NewNick, bool RegisterWhowas)
908 {
909         if (Client_Conn(Target) > NONE) {
910                 /* Local client */
911                 Log(LOG_INFO,
912                     "%s \"%s\" changed nick (connection %d): \"%s\" -> \"%s\".",
913                     Client_TypeText(Target), Client_Mask(Target),
914                     Client_Conn(Target), Client_ID(Target), NewNick);
915                 Conn_UpdateIdle(Client_Conn(Target));
916         } else {
917                 /* Remote client */
918                 LogDebug("%s \"%s\" changed nick: \"%s\" -> \"%s\".",
919                          Client_TypeText(Target),
920                          Client_Mask(Target), Client_ID(Target), NewNick);
921         }
922
923         /* Inform all servers and users (which have to know) of the new name */
924         if (Client_Type(Origin) == CLIENT_USER)
925                 IRC_WriteStrClientPrefix(Origin, Origin, "NICK :%s", NewNick);
926         IRC_WriteStrServersPrefix(Origin, Target, "NICK :%s", NewNick);
927         IRC_WriteStrRelatedPrefix(Target, Target, false, "NICK :%s", NewNick);
928
929         /* Register old nickname for WHOWAS queries, if required */
930         if (RegisterWhowas)
931                 Client_RegisterWhowas(Target);
932
933         /* Save new nickname */
934         Client_SetID(Target, NewNick);
935 }
936
937
938 /* -eof- */