]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc-login.c
irc-login.c, login.c: add missing include of "string.h"
[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         Client_SetPassword(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 #ifdef IDENTAUTH
404         char *ptr;
405 #endif
406
407         assert(Client != NULL);
408         assert(Req != NULL);
409
410         if (Client_Type(Client) == CLIENT_GOTNICK ||
411 #ifndef STRICT_RFC
412             Client_Type(Client) == CLIENT_UNKNOWN ||
413 #endif
414             Client_Type(Client) == CLIENT_GOTPASS)
415         {
416                 /* New connection */
417                 if (Req->argc != 4)
418                         return IRC_WriteStrClient(Client,
419                                                   ERR_NEEDMOREPARAMS_MSG,
420                                                   Client_ID(Client),
421                                                   Req->command);
422
423                 /* User name */
424 #ifdef IDENTAUTH
425                 ptr = Client_User(Client);
426                 if (!ptr || !*ptr || *ptr == '~')
427                         Client_SetUser(Client, Req->argv[0], false);
428 #else
429                 Client_SetUser(Client, Req->argv[0], false);
430 #endif
431                 Client_SetOrigUser(Client, Req->argv[0]);
432
433                 /* "Real name" or user info text: Don't set it to the empty
434                  * string, the original ircd can't deal with such "real names"
435                  * (e. g. "USER user * * :") ... */
436                 if (*Req->argv[3])
437                         Client_SetInfo(Client, Req->argv[3]);
438                 else
439                         Client_SetInfo(Client, "-");
440
441                 LogDebug("Connection %d: got valid USER command ...",
442                     Client_Conn(Client));
443                 if (Client_Type(Client) == CLIENT_GOTNICK)
444                         return Login_User(Client);
445                 else
446                         Client_SetType(Client, CLIENT_GOTUSER);
447                 return CONNECTED;
448
449         } else if (Client_Type(Client) == CLIENT_SERVER ||
450                    Client_Type(Client) == CLIENT_SERVICE) {
451                 /* Server/service updating an user */
452                 if (Req->argc != 4)
453                         return IRC_WriteStrClient(Client,
454                                                   ERR_NEEDMOREPARAMS_MSG,
455                                                   Client_ID(Client),
456                                                   Req->command);
457                 c = Client_Search(Req->prefix);
458                 if (!c)
459                         return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
460                                                   Client_ID(Client),
461                                                   Req->prefix);
462
463                 Client_SetUser(c, Req->argv[0], true);
464                 Client_SetOrigUser(c, Req->argv[0]);
465                 Client_SetHostname(c, Req->argv[1]);
466                 Client_SetInfo(c, Req->argv[3]);
467
468                 LogDebug("Connection %d: got valid USER command for \"%s\".",
469                          Client_Conn(Client), Client_Mask(c));
470
471                 /* RFC 1459 style user registration?
472                  * Introduce client to network: */
473                 if (Client_Type(c) == CLIENT_GOTNICK)
474                         Client_Introduce(Client, c, CLIENT_USER);
475
476                 return CONNECTED;
477         } else if (Client_Type(Client) == CLIENT_USER) {
478                 /* Already registered connection */
479                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
480                                           Client_ID(Client));
481         } else {
482                 /* Unexpected/invalid connection state? */
483                 return IRC_WriteStrClient(Client, ERR_NOTREGISTERED_MSG,
484                                           Client_ID(Client));
485         }
486 } /* IRC_USER */
487
488
489 /**
490  * Handler for the IRC "SERVICE" command.
491  *
492  * This function implements IRC Services registration using the SERVICE command
493  * defined in RFC 2812 3.1.6 and RFC 2813 4.1.4.
494  *
495  * At the moment ngIRCd doesn't support directly linked services, so this
496  * function returns ERR_ERRONEUSNICKNAME when the SERVICE command has not been
497  * received from a peer server.
498  *
499  * @param Client        The client from which this command has been received.
500  * @param Req           Request structure with prefix and all parameters.
501  * @returns             CONNECTED or DISCONNECTED..
502  */
503 GLOBAL bool
504 IRC_SERVICE(CLIENT *Client, REQUEST *Req)
505 {
506         CLIENT *c, *intr_c;
507         char *nick, *user, *host, *info, *modes, *ptr;
508         int token, hops;
509
510         assert(Client != NULL);
511         assert(Req != NULL);
512
513         if (Client_Type(Client) != CLIENT_GOTPASS &&
514             Client_Type(Client) != CLIENT_SERVER)
515                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
516                                           Client_ID(Client));
517
518         if (Req->argc != 6)
519                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
520                                           Client_ID(Client), Req->command);
521
522         if (Client_Type(Client) != CLIENT_SERVER)
523                 return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
524                                   Client_ID(Client), Req->argv[0]);
525
526         /* Bad number of parameters? */
527         if (Req->argc != 6)
528                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
529                                           Client_ID(Client), Req->command);
530
531         nick = Req->argv[0];
532         user = NULL; host = NULL;
533         token = atoi(Req->argv[1]);
534         hops = atoi(Req->argv[4]);
535         info = Req->argv[5];
536
537         /* Validate service name ("nick name") */
538         c = Client_Search(nick);
539         if(c) {
540                 /* Nick name collission: disconnect (KILL) both clients! */
541                 Log(LOG_ERR, "Server %s introduces already registered service \"%s\"!",
542                     Client_ID(Client), nick);
543                 Kill_Nick(nick, "Nick collision");
544                 return CONNECTED;
545         }
546
547         /* Get the server to which the service is connected */
548         intr_c = Client_GetFromToken(Client, token);
549         if (! intr_c) {
550                 Log(LOG_ERR, "Server %s introduces service \"%s\" on unknown server!?",
551                     Client_ID(Client), nick);
552                 Kill_Nick(nick, "Unknown server");
553                 return CONNECTED;
554         }
555
556         /* Get user and host name */
557         ptr = strchr(nick, '@');
558         if (ptr) {
559                 *ptr = '\0';
560                 host = ++ptr;
561         }
562         if (!host)
563                 host = Client_Hostname(intr_c);
564         ptr = strchr(nick, '!');
565         if (ptr) {
566                 *ptr = '\0';
567                 user = ++ptr;
568         }
569         if (!user)
570                 user = nick;
571
572         /* According to RFC 2812/2813 parameter 4 <type> "is currently reserved
573          * for future usage"; but we use it to transfer the modes and check
574          * that the first character is a '+' sign and ignore it otherwise. */
575         modes = (Req->argv[3][0] == '+') ? ++Req->argv[3] : "";
576
577         c = Client_NewRemoteUser(intr_c, nick, hops, user, host,
578                                  token, modes, info, true);
579         if (! c) {
580                 /* Couldn't create client structure, so KILL the service to
581                  * keep network status consistent ... */
582                 Log(LOG_ALERT, "Can't create client structure! (on connection %d)",
583                     Client_Conn(Client));
584                 Kill_Nick(nick, "Server error");
585                 return CONNECTED;
586         }
587
588         Client_Introduce(Client, c, CLIENT_SERVICE);
589         return CONNECTED;
590 } /* IRC_SERVICE */
591
592
593 /**
594  * Handler for the IRC "WEBIRC" command.
595  *
596  * See doc/Protocol.txt, section II.4:
597  * "Update webchat/proxy client information".
598  *
599  * @param Client        The client from which this command has been received.
600  * @param Req           Request structure with prefix and all parameters.
601  * @returns             CONNECTED or DISCONNECTED.
602  */
603 GLOBAL bool
604 IRC_WEBIRC(CLIENT *Client, REQUEST *Req)
605 {
606         /* Exactly 4 parameters are requited */
607         if (Req->argc != 4)
608                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
609                                           Client_ID(Client), Req->command);
610
611         if (!Conf_WebircPwd[0] || strcmp(Req->argv[0], Conf_WebircPwd) != 0)
612                 return IRC_WriteStrClient(Client, ERR_PASSWDMISMATCH_MSG,
613                                           Client_ID(Client));
614
615         LogDebug("Connection %d: got valid WEBIRC command: user=%s, host=%s, ip=%s",
616                  Client_Conn(Client), Req->argv[1], Req->argv[2], Req->argv[3]);
617
618         Client_SetUser(Client, Req->argv[1], true);
619         Client_SetOrigUser(Client, Req->argv[1]);
620         Client_SetHostname(Client, Req->argv[2]);
621         return CONNECTED;
622 } /* IRC_WEBIRC */
623
624
625 /**
626  * Handler for the IRC "QUIT" command.
627  *
628  * See RFC 2812, 3.1.7 "Quit", and RFC 2813, 4.1.5 "Quit".
629  *
630  * @param Client        The client from which this command has been received.
631  * @param Req           Request structure with prefix and all parameters.
632  * @returns             CONNECTED or DISCONNECTED.
633  */
634 GLOBAL bool
635 IRC_QUIT( CLIENT *Client, REQUEST *Req )
636 {
637         CLIENT *target;
638         char quitmsg[LINE_LEN];
639
640         assert(Client != NULL);
641         assert(Req != NULL);
642
643         /* Wrong number of arguments? */
644         if (Req->argc > 1)
645                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
646                                           Client_ID(Client), Req->command);
647
648         if (Req->argc == 1)
649                 strlcpy(quitmsg, Req->argv[0], sizeof quitmsg);
650
651         if (Client_Type(Client) == CLIENT_SERVER) {
652                 /* Server */
653                 target = Client_Search(Req->prefix);
654                 if (!target) {
655                         Log(LOG_WARNING,
656                             "Got QUIT from %s for unknown client!?",
657                             Client_ID(Client));
658                         return CONNECTED;
659                 }
660
661                 if (target != Client) {
662                         Client_Destroy(target, "Got QUIT command.",
663                                        Req->argc == 1 ? quitmsg : NULL, true);
664                         return CONNECTED;
665                 } else {
666                         Conn_Close(Client_Conn(Client), "Got QUIT command.",
667                                    Req->argc == 1 ? quitmsg : NULL, true);
668                         return DISCONNECTED;
669                 }
670         } else {
671                 if (Req->argc == 1 && quitmsg[0] != '\"') {
672                         /* " " to avoid confusion */
673                         strlcpy(quitmsg, "\"", sizeof quitmsg);
674                         strlcat(quitmsg, Req->argv[0], sizeof quitmsg-1);
675                         strlcat(quitmsg, "\"", sizeof quitmsg );
676                 }
677
678                 /* User, Service, or not yet registered */
679                 Conn_Close(Client_Conn(Client), "Got QUIT command.",
680                            Req->argc == 1 ? quitmsg : NULL, true);
681
682                 return DISCONNECTED;
683         }
684 } /* IRC_QUIT */
685
686
687 #ifndef STRICT_RFC
688
689 /**
690  * Handler for HTTP command, e.g. GET and POST
691  *
692  * We handle these commands here to avoid the quite long timeout when
693  * some user tries to access this IRC daemon using an web browser ...
694  *
695  * @param Client        The client from which this command has been received.
696  * @param Req           Request structure with prefix and all parameters.
697  * @returns             CONNECTED or DISCONNECTED.
698  */
699 GLOBAL bool
700 IRC_QUIT_HTTP( CLIENT *Client, REQUEST *Req )
701 {
702         Req->argc = 1;
703         Req->argv[0] = "Oops, HTTP request received? This is IRC!";
704         return IRC_QUIT(Client, Req);
705 } /* IRC_QUIT_HTTP */
706
707 #endif
708
709
710 /**
711  * Handler for the IRC "PING" command.
712  *
713  * See RFC 2812, 3.7.2 "Ping message".
714  *
715  * @param Client        The client from which this command has been received.
716  * @param Req           Request structure with prefix and all parameters.
717  * @returns             CONNECTED or DISCONNECTED.
718  */
719 GLOBAL bool
720 IRC_PING(CLIENT *Client, REQUEST *Req)
721 {
722         CLIENT *target, *from;
723
724         assert(Client != NULL);
725         assert(Req != NULL);
726
727         if (Req->argc < 1)
728                 return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
729                                           Client_ID(Client));
730 #ifdef STRICT_RFC
731         /* Don't ignore additional arguments when in "strict" mode */
732         if (Req->argc > 2)
733                  return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
734                                            Client_ID(Client), Req->command);
735 #endif
736
737         if (Req->argc > 1) {
738                 /* A target has been specified ... */
739                 target = Client_Search(Req->argv[1]);
740
741                 if (!target || Client_Type(target) != CLIENT_SERVER)
742                         return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
743                                         Client_ID(Client), Req->argv[1]);
744
745                 if (target != Client_ThisServer()) {
746                         /* Ok, we have to forward the PING */
747                         if (Client_Type(Client) == CLIENT_SERVER)
748                                 from = Client_Search(Req->prefix);
749                         else
750                                 from = Client;
751                         if (!from)
752                                 return IRC_WriteStrClient(Client,
753                                                 ERR_NOSUCHSERVER_MSG,
754                                                 Client_ID(Client), Req->prefix);
755
756                         return IRC_WriteStrClientPrefix(target, from,
757                                         "PING %s :%s", Req->argv[0],
758                                         Req->argv[1] );
759                 }
760         }
761
762         if (Client_Type(Client) == CLIENT_SERVER) {
763                 if (Req->prefix)
764                         from = Client_Search(Req->prefix);
765                 else
766                         from = Client;
767         } else
768                 from = Client_ThisServer();
769         if (!from)
770                 return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
771                                         Client_ID(Client), Req->prefix);
772
773         Log(LOG_DEBUG, "Connection %d: got PING, sending PONG ...",
774             Client_Conn(Client));
775
776 #ifdef STRICT_RFC
777         return IRC_WriteStrClient(Client, "PONG %s :%s",
778                 Client_ID(from), Client_ID(Client));
779 #else
780         /* Some clients depend on the argument being returned in the PONG
781          * reply (not mentioned in any RFC, though) */
782         return IRC_WriteStrClient(Client, "PONG %s :%s",
783                 Client_ID(from), Req->argv[0]);
784 #endif
785 } /* IRC_PING */
786
787
788 /**
789  * Handler for the IRC "PONG" command.
790  *
791  * See RFC 2812, 3.7.3 "Pong message".
792  *
793  * @param Client        The client from which this command has been received.
794  * @param Req           Request structure with prefix and all parameters.
795  * @returns             CONNECTED or DISCONNECTED.
796  */
797 GLOBAL bool
798 IRC_PONG(CLIENT *Client, REQUEST *Req)
799 {
800         CLIENT *target, *from;
801         CONN_ID conn;
802 #ifndef STRICT_RFC
803         long auth_ping;
804 #endif
805         char *s;
806
807         assert(Client != NULL);
808         assert(Req != NULL);
809
810         /* Wrong number of arguments? */
811         if (Req->argc < 1) {
812                 if (Client_Type(Client) == CLIENT_USER)
813                         return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
814                                                   Client_ID(Client));
815                 else
816                         return CONNECTED;
817         }
818         if (Req->argc > 2) {
819                 if (Client_Type(Client) == CLIENT_USER)
820                         return IRC_WriteStrClient(Client,
821                                                   ERR_NEEDMOREPARAMS_MSG,
822                                                   Client_ID(Client),
823                                                   Req->command);
824                 else
825                         return CONNECTED;
826         }
827
828         /* Forward? */
829         if (Req->argc == 2 && Client_Type(Client) == CLIENT_SERVER) {
830                 target = Client_Search(Req->argv[0]);
831                 if (!target)
832                         return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
833                                         Client_ID(Client), Req->argv[0]);
834
835                 from = Client_Search(Req->prefix);
836
837                 if (target != Client_ThisServer() && target != from) {
838                         /* Ok, we have to forward the message. */
839                         if (!from)
840                                 return IRC_WriteStrClient(Client,
841                                                 ERR_NOSUCHSERVER_MSG,
842                                                 Client_ID(Client), Req->prefix);
843
844                         if (Client_Type(Client_NextHop(target)) != CLIENT_SERVER)
845                                 s = Client_ID(from);
846                         else
847                                 s = Req->argv[0];
848                         return IRC_WriteStrClientPrefix(target, from,
849                                  "PONG %s :%s", s, Req->argv[1]);
850                 }
851         }
852
853         /* The connection timestamp has already been updated when the data has
854          * been read from so socket, so we don't need to update it here. */
855
856         conn = Client_Conn(Client);
857
858 #ifndef STRICT_RFC
859         /* Check authentication PING-PONG ... */
860         auth_ping = Conn_GetAuthPing(conn);
861         if (auth_ping) {
862                 LogDebug("AUTH PONG: waiting for token \"%ld\", got \"%s\" ...",
863                          auth_ping, Req->argv[0]);
864                 if (auth_ping == atoi(Req->argv[0])) {
865                         Conn_SetAuthPing(conn, 0);
866                         if (Client_Type(Client) == CLIENT_WAITAUTHPING)
867                                 Login_User(Client);
868                 } else
869                         if (!IRC_WriteStrClient(Client,
870                                         "To connect, type /QUOTE PONG %ld",
871                                         auth_ping))
872                                 return DISCONNECTED;
873         }
874 #endif
875
876         if (Client_Type(Client) == CLIENT_SERVER && Conn_LastPing(conn) == 0) {
877                 Log(LOG_INFO,
878                     "Synchronization with \"%s\" done (connection %d): %ld seconds [%ld users, %ld channels]",
879                     Client_ID(Client), conn, time(NULL) - Conn_GetSignon(conn),
880                     Client_UserCount(), Channel_CountVisible(NULL));
881                 Conn_UpdatePing(conn);
882         } else
883                 LogDebug("Connection %d: received PONG. Lag: %ld seconds.",
884                          conn, time(NULL) - Conn_LastPing(conn));
885
886         return CONNECTED;
887 } /* IRC_PONG */
888
889
890 /**
891  * Kill all users with a specific nick name in the network.
892  *
893  * @param Nick          Nick name.
894  * @param Reason        Reason for the KILL.
895  */
896 static void
897 Kill_Nick(char *Nick, char *Reason)
898 {
899         REQUEST r;
900
901         assert (Nick != NULL);
902         assert (Reason != NULL);
903
904         r.prefix = NULL;
905         r.argv[0] = Nick;
906         r.argv[1] = Reason;
907         r.argc = 2;
908
909         Log(LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s",
910             Nick, Reason);
911
912         IRC_KILL(Client_ThisServer(), &r);
913 } /* Kill_Nick */
914
915
916 /* -eof- */