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