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