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