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