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