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