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