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