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