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