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