]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc-login.c
Code cleanup: mostly removing empty lines
[ngircd-alex.git] / src / ngircd / irc-login.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2010 Alexander Barton (alex@barton.de)
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 <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <strings.h>
25 #include <signal.h>
26 #include <unistd.h>
27
28 #include "ngircd.h"
29 #include "conn-func.h"
30 #include "conf.h"
31 #include "channel.h"
32 #include "io.h"
33 #include "log.h"
34 #include "messages.h"
35 #include "pam.h"
36 #include "parse.h"
37 #include "irc.h"
38 #include "irc-info.h"
39 #include "irc-write.h"
40
41 #include "exp.h"
42 #include "irc-login.h"
43
44
45 static bool Hello_User PARAMS(( CLIENT *Client ));
46 static bool Hello_User_PostAuth PARAMS(( CLIENT *Client ));
47 static void Kill_Nick PARAMS(( char *Nick, char *Reason ));
48 static void Introduce_Client PARAMS((CLIENT *To, CLIENT *Client, int Type));
49 static void Reject_Client PARAMS((CLIENT *Client));
50
51 static void cb_introduceClient PARAMS((CLIENT *Client, CLIENT *Prefix,
52                                        void *i));
53
54 #ifdef PAM
55 static void cb_Read_Auth_Result PARAMS((int r_fd, UNUSED short events));
56 #endif
57
58 /**
59  * Handler for the IRC command "PASS".
60  * See RFC 2813 section 4.1.1, and RFC 2812 section 3.1.1.
61  */
62 GLOBAL bool
63 IRC_PASS( CLIENT *Client, REQUEST *Req )
64 {
65         char *type, *orig_flags;
66         int protohigh, protolow;
67
68         assert( Client != NULL );
69         assert( Req != NULL );
70
71         /* Return an error if this is not a local client */
72         if (Client_Conn(Client) <= NONE)
73                 return IRC_WriteStrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
74                                           Client_ID(Client), Req->command);
75
76         if (Client_Type(Client) == CLIENT_UNKNOWN && Req->argc == 1) {
77                 /* Not yet registered "unknown" connection, PASS with one
78                  * argument: either a regular client, service, or server
79                  * using the old RFC 1459 section 4.1.1 syntax. */
80                 LogDebug("Connection %d: got PASS command (RFC 1459) ...",
81                          Client_Conn(Client));
82         } else if ((Client_Type(Client) == CLIENT_UNKNOWN ||
83                     Client_Type(Client) == CLIENT_UNKNOWNSERVER) &&
84                    (Req->argc == 3 || Req->argc == 4)) {
85                 /* Not yet registered "unknown" connection or outgoing server
86                  * link, PASS with three or four argument: server using the
87                  * RFC 2813 section 4.1.1 syntax. */
88                 LogDebug("Connection %d: got PASS command (RFC 2813, new server link) ...",
89                          Client_Conn(Client));
90         } else if (Client_Type(Client) == CLIENT_UNKNOWN ||
91                    Client_Type(Client) == CLIENT_UNKNOWNSERVER) {
92                 /* Unregistered connection, but wrong number of arguments: */
93                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
94                                           Client_ID(Client), Req->command);
95         } else {
96                 /* Registered connection, PASS command is not allowed! */
97                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
98                                           Client_ID(Client));
99         }
100
101         Client_SetPassword(Client, Req->argv[0]);
102
103         /* Protocol version */
104         if (Req->argc >= 2 && strlen(Req->argv[1]) >= 4) {
105                 int c2, c4;
106
107                 c2 = Req->argv[1][2];
108                 c4 = Req->argv[1][4];
109
110                 Req->argv[1][4] = '\0';
111                 protolow = atoi(&Req->argv[1][2]);
112                 Req->argv[1][2] = '\0';
113                 protohigh = atoi(Req->argv[1]);
114
115                 Req->argv[1][2] = c2;
116                 Req->argv[1][4] = c4;
117
118                 Client_SetType(Client, CLIENT_GOTPASS_2813);
119         } else {
120                 protohigh = protolow = 0;
121                 Client_SetType(Client, CLIENT_GOTPASS);
122         }
123
124         /* Protocol type, see doc/Protocol.txt */
125         if (Req->argc >= 2 && strlen(Req->argv[1]) > 4)
126                 type = &Req->argv[1][4];
127         else
128                 type = NULL;
129
130         /* Protocol flags/options */
131         if (Req->argc >= 4)
132                 orig_flags = Req->argv[3];
133         else
134                 orig_flags = "";
135
136         /* Implementation, version and IRC+ flags */
137         if (Req->argc >= 3) {
138                 char *impl, *ptr, *serverver, *flags;
139
140                 impl = Req->argv[2];
141                 ptr = strchr(impl, '|');
142                 if (ptr)
143                         *ptr = '\0';
144
145                 if (type && strcmp(type, PROTOIRCPLUS) == 0) {
146                         /* The peer seems to be a server which supports the
147                          * IRC+ protocol (see doc/Protocol.txt). */
148                         serverver = ptr ? ptr + 1 : "?";
149                         flags = strchr(ptr ? serverver : impl, ':');
150                         if (flags) {
151                                 *flags = '\0';
152                                 flags++;
153                         } else
154                                 flags = "";
155                         Log(LOG_INFO,
156                             "Peer on conenction %d announces itself as %s-%s using protocol %d.%d/IRC+ (flags: \"%s\").",
157                             Client_Conn(Client), impl, serverver,
158                             protohigh, protolow, flags);
159                 } else {
160                         /* The peer seems to be a server supporting the
161                          * "original" IRC protocol (RFC 2813). */
162                         if (strchr(orig_flags, 'Z'))
163                                 flags = "Z";
164                         else
165                                 flags = "";
166                         Log(LOG_INFO,
167                             "Peer on connection %d announces itself as \"%s\" using protocol %d.%d (flags: \"%s\").",
168                             Client_Conn(Client), impl,
169                             protohigh, protolow, flags);
170                 }
171                 Client_SetFlags(Client, flags);
172         }
173
174         return CONNECTED;
175 } /* IRC_PASS */
176
177
178 /**
179  * IRC "NICK" command.
180  * This function implements the IRC command "NICK" which is used to register
181  * with the server, to change already registered nicknames and to introduce
182  * new users which are connected to other servers.
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                         /* If we received a valid USER command already then
263                          * register the new client! */
264                         if( Client_Type( Client ) == CLIENT_GOTUSER )
265                                 return Hello_User( Client );
266                         else
267                                 Client_SetType( Client, CLIENT_GOTNICK );
268                 } else {
269                         /* Nickname change */
270                         if (Client_Conn(target) > NONE) {
271                                 /* Local client */
272                                 Log(LOG_INFO,
273                                     "%s \"%s\" changed nick (connection %d): \"%s\" -> \"%s\".",
274                                     Client_TypeText(target), Client_Mask(target),
275                                     Client_Conn(target), Client_ID(target),
276                                     Req->argv[0]);
277                                 Conn_UpdateIdle(Client_Conn(target));
278                         } else {
279                                 /* Remote client */
280                                 LogDebug("%s \"%s\" changed nick: \"%s\" -> \"%s\".",
281                                          Client_TypeText(target),
282                                          Client_Mask(target), Client_ID(target),
283                                          Req->argv[0]);
284                         }
285
286                         /* Inform all users and servers (which have to know)
287                          * of this nickname change */
288                         if( Client_Type( Client ) == CLIENT_USER )
289                                 IRC_WriteStrClientPrefix( Client, Client,
290                                                           "NICK :%s",
291                                                           Req->argv[0] );
292                         IRC_WriteStrServersPrefix( Client, target,
293                                                    "NICK :%s", Req->argv[0] );
294                         IRC_WriteStrRelatedPrefix( target, target, false,
295                                                    "NICK :%s", Req->argv[0] );
296
297                         /* Register old nickname for WHOWAS queries */
298                         Client_RegisterWhowas( target );
299
300                         /* Save new nickname */
301                         Client_SetID( target, Req->argv[0] );
302
303                         IRC_SetPenalty( target, 2 );
304                 }
305
306                 return CONNECTED;
307         } else if(Client_Type(Client) == CLIENT_SERVER ||
308                   Client_Type(Client) == CLIENT_SERVICE) {
309                 /* Server or service introduces new client */
310
311                 /* Bad number of parameters? */
312                 if (Req->argc != 2 && Req->argc != 7)
313                         return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
314                                                   Client_ID(Client), Req->command);
315
316                 if (Req->argc >= 7) {
317                         /* RFC 2813 compatible syntax */
318                         nick = Req->argv[0];
319                         hops = atoi(Req->argv[1]);
320                         user = Req->argv[2];
321                         hostname = Req->argv[3];
322                         token = atoi(Req->argv[4]);
323                         modes = Req->argv[5] + 1;
324                         info = Req->argv[6];
325                 } else {
326                         /* RFC 1459 compatible syntax */
327                         nick = Req->argv[0];
328                         hops = 1;
329                         user = Req->argv[0];
330                         hostname = Client_ID(Client);
331                         token = atoi(Req->argv[1]);
332                         modes = "";
333                         info = Req->argv[0];
334                 }
335
336                 c = Client_Search(nick);
337                 if(c) {
338                         /*
339                          * the new nick is already present on this server:
340                          * the new and the old one have to be disconnected now.
341                          */
342                         Log( LOG_ERR, "Server %s introduces already registered nick \"%s\"!", Client_ID( Client ), Req->argv[0] );
343                         Kill_Nick( Req->argv[0], "Nick collision" );
344                         return CONNECTED;
345                 }
346
347                 /* Find the Server this client is connected to */
348                 intr_c = Client_GetFromToken(Client, token);
349                 if( ! intr_c )
350                 {
351                         Log( LOG_ERR, "Server %s introduces nick \"%s\" on unknown server!?", Client_ID( Client ), Req->argv[0] );
352                         Kill_Nick( Req->argv[0], "Unknown server" );
353                         return CONNECTED;
354                 }
355
356                 c = Client_NewRemoteUser(intr_c, nick, hops, user, hostname,
357                                          token, modes, info, true);
358                 if( ! c )
359                 {
360                         /* out of memory, need to disconnect client to keep network state consistent */
361                         Log( LOG_ALERT, "Can't create client structure! (on connection %d)", Client_Conn( Client ));
362                         Kill_Nick( Req->argv[0], "Server error" );
363                         return CONNECTED;
364                 }
365
366                 /* RFC 2813: client is now fully registered, inform all the
367                  * other servers about the new user.
368                  * RFC 1459: announce the new client only after receiving the
369                  * USER command, first we need more information! */
370                 if (Req->argc < 7) {
371                         LogDebug("Client \"%s\" is being registered (RFC 1459) ...",
372                                  Client_Mask(c));
373                         Client_SetType(c, CLIENT_GOTNICK);
374                 } else
375                         Introduce_Client(Client, c, CLIENT_USER);
376
377                 return CONNECTED;
378         }
379         else return IRC_WriteStrClient( Client, ERR_ALREADYREGISTRED_MSG, Client_ID( Client ));
380 } /* IRC_NICK */
381
382
383 /**
384  * Handler for the IRC command "USER".
385  */
386 GLOBAL bool
387 IRC_USER(CLIENT * Client, REQUEST * Req)
388 {
389         CLIENT *c;
390 #ifdef IDENTAUTH
391         char *ptr;
392 #endif
393
394         assert(Client != NULL);
395         assert(Req != NULL);
396
397         if (Client_Type(Client) == CLIENT_GOTNICK ||
398 #ifndef STRICT_RFC
399             Client_Type(Client) == CLIENT_UNKNOWN ||
400 #endif
401             Client_Type(Client) == CLIENT_GOTPASS)
402         {
403                 /* New connection */
404                 if (Req->argc != 4)
405                         return IRC_WriteStrClient(Client,
406                                                   ERR_NEEDMOREPARAMS_MSG,
407                                                   Client_ID(Client),
408                                                   Req->command);
409
410                 /* User name */
411 #ifdef IDENTAUTH
412                 ptr = Client_User(Client);
413                 if (!ptr || !*ptr || *ptr == '~')
414                         Client_SetUser(Client, Req->argv[0], false);
415 #else
416                 Client_SetUser(Client, Req->argv[0], false);
417 #endif
418                 Client_SetOrigUser(Client, Req->argv[0]);
419
420                 /* "Real name" or user info text: Don't set it to the empty
421                  * string, the original ircd can't deal with such "real names"
422                  * (e. g. "USER user * * :") ... */
423                 if (*Req->argv[3])
424                         Client_SetInfo(Client, Req->argv[3]);
425                 else
426                         Client_SetInfo(Client, "-");
427
428                 LogDebug("Connection %d: got valid USER command ...",
429                     Client_Conn(Client));
430                 if (Client_Type(Client) == CLIENT_GOTNICK)
431                         return Hello_User(Client);
432                 else
433                         Client_SetType(Client, CLIENT_GOTUSER);
434                 return CONNECTED;
435
436         } else if (Client_Type(Client) == CLIENT_SERVER ||
437                    Client_Type(Client) == CLIENT_SERVICE) {
438                 /* Server/service updating an user */
439                 if (Req->argc != 4)
440                         return IRC_WriteStrClient(Client,
441                                                   ERR_NEEDMOREPARAMS_MSG,
442                                                   Client_ID(Client),
443                                                   Req->command);
444                 c = Client_Search(Req->prefix);
445                 if (!c)
446                         return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
447                                                   Client_ID(Client),
448                                                   Req->prefix);
449
450                 Client_SetUser(c, Req->argv[0], true);
451                 Client_SetOrigUser(c, Req->argv[0]);
452                 Client_SetHostname(c, Req->argv[1]);
453                 Client_SetInfo(c, Req->argv[3]);
454
455                 LogDebug("Connection %d: got valid USER command for \"%s\".",
456                          Client_Conn(Client), Client_Mask(c));
457
458                 /* RFC 1459 style user registration?
459                  * Introduce client to network: */
460                 if (Client_Type(c) == CLIENT_GOTNICK)
461                         Introduce_Client(Client, c, CLIENT_USER);
462
463                 return CONNECTED;
464         } else if (Client_Type(Client) == CLIENT_USER) {
465                 /* Already registered connection */
466                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
467                                           Client_ID(Client));
468         } else {
469                 /* Unexpected/invalid connection state? */
470                 return IRC_WriteStrClient(Client, ERR_NOTREGISTERED_MSG,
471                                           Client_ID(Client));
472         }
473 } /* IRC_USER */
474
475
476 /**
477  * Handler for the IRC command "SERVICE".
478  * This function implements IRC Services registration using the SERVICE command
479  * defined in RFC 2812 3.1.6 and RFC 2813 4.1.4.
480  * At the moment ngIRCd doesn't support directly linked services, so this
481  * function returns ERR_ERRONEUSNICKNAME when the SERVICE command has not been
482  * received from a peer server.
483  */
484 GLOBAL bool
485 IRC_SERVICE(CLIENT *Client, REQUEST *Req)
486 {
487         CLIENT *c, *intr_c;
488         char *nick, *user, *host, *info, *modes, *ptr;
489         int token, hops;
490
491         assert(Client != NULL);
492         assert(Req != NULL);
493
494         if (Client_Type(Client) != CLIENT_GOTPASS &&
495             Client_Type(Client) != CLIENT_SERVER)
496                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
497                                           Client_ID(Client));
498
499         if (Req->argc != 6)
500                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
501                                           Client_ID(Client), Req->command);
502
503         if (Client_Type(Client) != CLIENT_SERVER)
504                 return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
505                                   Client_ID(Client), Req->argv[0]);
506
507         /* Bad number of parameters? */
508         if (Req->argc != 6)
509                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
510                                           Client_ID(Client), Req->command);
511
512         nick = Req->argv[0];
513         user = NULL; host = NULL;
514         token = atoi(Req->argv[1]);
515         hops = atoi(Req->argv[4]);
516         info = Req->argv[5];
517
518         /* Validate service name ("nick name") */
519         c = Client_Search(nick);
520         if(c) {
521                 /* Nick name collission: disconnect (KILL) both clients! */
522                 Log(LOG_ERR, "Server %s introduces already registered service \"%s\"!",
523                     Client_ID(Client), nick);
524                 Kill_Nick(nick, "Nick collision");
525                 return CONNECTED;
526         }
527
528         /* Get the server to which the service is connected */
529         intr_c = Client_GetFromToken(Client, token);
530         if (! intr_c) {
531                 Log(LOG_ERR, "Server %s introduces service \"%s\" on unknown server!?",
532                     Client_ID(Client), nick);
533                 Kill_Nick(nick, "Unknown server");
534                 return CONNECTED;
535         }
536
537         /* Get user and host name */
538         ptr = strchr(nick, '@');
539         if (ptr) {
540                 *ptr = '\0';
541                 host = ++ptr;
542         }
543         if (!host)
544                 host = Client_Hostname(intr_c);
545         ptr = strchr(nick, '!');
546         if (ptr) {
547                 *ptr = '\0';
548                 user = ++ptr;
549         }
550         if (!user)
551                 user = nick;
552
553         /* According to RFC 2812/2813 parameter 4 <type> "is currently reserved
554          * for future usage"; but we use it to transfer the modes and check
555          * that the first character is a '+' sign and ignore it otherwise. */
556         modes = (Req->argv[3][0] == '+') ? ++Req->argv[3] : "";
557
558         c = Client_NewRemoteUser(intr_c, nick, hops, user, host,
559                                  token, modes, info, true);
560         if (! c) {
561                 /* Couldn't create client structure, so KILL the service to
562                  * keep network status consistent ... */
563                 Log(LOG_ALERT, "Can't create client structure! (on connection %d)",
564                     Client_Conn(Client));
565                 Kill_Nick(nick, "Server error");
566                 return CONNECTED;
567         }
568
569         Introduce_Client(Client, c, CLIENT_SERVICE);
570         return CONNECTED;
571 } /* IRC_SERVICE */
572
573
574 /**
575  * Handler for the IRC command "WEBIRC".
576  * Syntax: WEBIRC <password> <username> <real-hostname> <real-IP-address>
577  */
578 GLOBAL bool
579 IRC_WEBIRC(CLIENT *Client, REQUEST *Req)
580 {
581         /* Exactly 4 parameters are requited */
582         if (Req->argc != 4)
583                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
584                                           Client_ID(Client), Req->command);
585
586         if (!Conf_WebircPwd[0] || strcmp(Req->argv[0], Conf_WebircPwd) != 0)
587                 return IRC_WriteStrClient(Client, ERR_PASSWDMISMATCH_MSG,
588                                           Client_ID(Client));
589
590         LogDebug("Connection %d: got valid WEBIRC command: user=%s, host=%s, ip=%s",
591                  Client_Conn(Client), Req->argv[1], Req->argv[2], Req->argv[3]);
592
593         Client_SetUser(Client, Req->argv[1], true);
594         Client_SetOrigUser(Client, Req->argv[1]);
595         Client_SetHostname(Client, Req->argv[2]);
596         return CONNECTED;
597 } /* IRC_WEBIRC */
598
599
600 GLOBAL bool
601 IRC_QUIT( CLIENT *Client, REQUEST *Req )
602 {
603         CLIENT *target;
604         char quitmsg[LINE_LEN];
605
606         assert( Client != NULL );
607         assert( Req != NULL );
608
609         /* Wrong number of arguments? */
610         if( Req->argc > 1 )
611                 return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
612
613         if (Req->argc == 1)
614                 strlcpy(quitmsg, Req->argv[0], sizeof quitmsg);
615
616         if ( Client_Type( Client ) == CLIENT_SERVER )
617         {
618                 /* Server */
619                 target = Client_Search( Req->prefix );
620                 if( ! target )
621                 {
622                         Log( LOG_WARNING, "Got QUIT from %s for unknown client!?", Client_ID( Client ));
623                         return CONNECTED;
624                 }
625
626                 Client_Destroy( target, "Got QUIT command.", Req->argc == 1 ? quitmsg : NULL, true);
627
628                 return CONNECTED;
629         }
630         else
631         {
632                 if (Req->argc == 1 && quitmsg[0] != '\"') {
633                         /* " " to avoid confusion */
634                         strlcpy(quitmsg, "\"", sizeof quitmsg);
635                         strlcat(quitmsg, Req->argv[0], sizeof quitmsg-1);
636                         strlcat(quitmsg, "\"", sizeof quitmsg );
637                 }
638
639                 /* User, Service, or not yet registered */
640                 Conn_Close( Client_Conn( Client ), "Got QUIT command.", Req->argc == 1 ? quitmsg : NULL, true);
641
642                 return DISCONNECTED;
643         }
644 } /* IRC_QUIT */
645
646
647 GLOBAL bool
648 IRC_PING(CLIENT *Client, REQUEST *Req)
649 {
650         CLIENT *target, *from;
651
652         assert(Client != NULL);
653         assert(Req != NULL);
654
655         if (Req->argc < 1)
656                 return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
657                                           Client_ID(Client));
658 #ifdef STRICT_RFC
659         /* Don't ignore additional arguments when in "strict" mode */
660         if (Req->argc > 2)
661                  return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
662                                            Client_ID(Client), Req->command);
663 #endif
664
665         if (Req->argc > 1) {
666                 /* A target has been specified ... */
667                 target = Client_Search(Req->argv[1]);
668
669                 if (!target || Client_Type(target) != CLIENT_SERVER)
670                         return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
671                                         Client_ID(Client), Req->argv[1]);
672
673                 if (target != Client_ThisServer()) {
674                         /* Ok, we have to forward the PING */
675                         if (Client_Type(Client) == CLIENT_SERVER)
676                                 from = Client_Search(Req->prefix);
677                         else
678                                 from = Client;
679                         if (!from)
680                                 return IRC_WriteStrClient(Client,
681                                                 ERR_NOSUCHSERVER_MSG,
682                                                 Client_ID(Client), Req->prefix);
683
684                         return IRC_WriteStrClientPrefix(target, from,
685                                         "PING %s :%s", Req->argv[0],
686                                         Req->argv[1] );
687                 }
688         }
689
690         if (Client_Type(Client) == CLIENT_SERVER) {
691                 if (Req->prefix)
692                         from = Client_Search(Req->prefix);
693                 else
694                         from = Client;
695         } else
696                 from = Client_ThisServer();
697         if (!from)
698                 return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
699                                         Client_ID(Client), Req->prefix);
700
701         Log(LOG_DEBUG, "Connection %d: got PING, sending PONG ...",
702             Client_Conn(Client));
703
704 #ifdef STRICT_RFC
705         return IRC_WriteStrClient(Client, "PONG %s :%s",
706                 Client_ID(from), Client_ID(Client));
707 #else
708         /* Some clients depend on the argument being returned in the PONG
709          * reply (not mentioned in any RFC, though) */
710         return IRC_WriteStrClient(Client, "PONG %s :%s",
711                 Client_ID(from), Req->argv[0]);
712 #endif
713 } /* IRC_PING */
714
715
716 GLOBAL bool
717 IRC_PONG(CLIENT *Client, REQUEST *Req)
718 {
719         CLIENT *target, *from;
720         char *s;
721
722         assert(Client != NULL);
723         assert(Req != NULL);
724
725         /* Wrong number of arguments? */
726         if (Req->argc < 1)
727                 return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
728                                           Client_ID(Client));
729         if (Req->argc > 2)
730                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
731                                           Client_ID(Client), Req->command);
732
733         /* Forward? */
734         if (Req->argc == 2 && Client_Type(Client) == CLIENT_SERVER) {
735                 target = Client_Search(Req->argv[0]);
736                 if (!target)
737                         return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
738                                         Client_ID(Client), Req->argv[0]);
739
740                 from = Client_Search(Req->prefix);
741
742                 if (target != Client_ThisServer() && target != from) {
743                         /* Ok, we have to forward the message. */
744                         if (!from)
745                                 return IRC_WriteStrClient(Client,
746                                                 ERR_NOSUCHSERVER_MSG,
747                                                 Client_ID(Client), Req->prefix);
748
749                         if (Client_Type(Client_NextHop(target)) != CLIENT_SERVER)
750                                 s = Client_ID(from);
751                         else
752                                 s = Req->argv[0];
753                         return IRC_WriteStrClientPrefix(target, from,
754                                  "PONG %s :%s", s, Req->argv[1]);
755                 }
756         }
757
758         /* The connection timestamp has already been updated when the data has
759          * been read from so socket, so we don't need to update it here. */
760 #ifdef DEBUG
761         if (Client_Conn(Client) > NONE)
762                 Log(LOG_DEBUG,
763                         "Connection %d: received PONG. Lag: %ld seconds.",
764                         Client_Conn(Client),
765                         time(NULL) - Conn_LastPing(Client_Conn(Client)));
766         else
767                  Log(LOG_DEBUG,
768                         "Connection %d: received PONG.", Client_Conn(Client));
769 #endif
770         return CONNECTED;
771 } /* IRC_PONG */
772
773
774 static bool
775 Hello_User(CLIENT * Client)
776 {
777 #ifdef PAM
778         int pipefd[2], result;
779         CONN_ID conn;
780         pid_t pid;
781
782         assert(Client != NULL);
783         conn = Client_Conn(Client);
784
785         if (!Conf_PAM) {
786                 /* Don't do any PAM authentication at all, instead emulate
787                  * the beahiour of the daemon compiled without PAM support:
788                  * because there can't be any "server password", all
789                  * passwords supplied are classified as "wrong". */
790                 if(Client_Password(Client)[0] == '\0')
791                         return Hello_User_PostAuth(Client);
792                 Reject_Client(Client);
793                 return DISCONNECTED;
794         }
795
796         /* Fork child process for PAM authentication; and make sure that the
797          * process timeout is set higher than the login timeout! */
798         pid = Proc_Fork(Conn_GetProcStat(conn), pipefd,
799                         cb_Read_Auth_Result, Conf_PongTimeout + 1);
800         if (pid > 0) {
801                 LogDebug("Authenticator for connection %d created (PID %d).",
802                          conn, pid);
803                 return CONNECTED;
804         } else {
805                 /* Sub process */
806                 Log_Init_Subprocess("Auth");
807                 result = PAM_Authenticate(Client);
808                 write(pipefd[1], &result, sizeof(result));
809                 Log_Exit_Subprocess("Auth");
810                 exit(0);
811         }
812 #else
813         assert(Client != NULL);
814
815         /* Check global server password ... */
816         if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0) {
817                 /* Bad password! */
818                 Reject_Client(Client);
819                 return DISCONNECTED;
820         }
821         return Hello_User_PostAuth(Client);
822 #endif
823 }
824
825
826 #ifdef PAM
827
828 /**
829  * Read result of the authenticatior sub-process from pipe
830  */
831 static void
832 cb_Read_Auth_Result(int r_fd, UNUSED short events)
833 {
834         CONN_ID conn;
835         CLIENT *client;
836         int result;
837         size_t len;
838         PROC_STAT *proc;
839
840         LogDebug("Auth: Got callback on fd %d, events %d", r_fd, events);
841         conn = Conn_GetFromProc(r_fd);
842         if (conn == NONE) {
843                 /* Ops, none found? Probably the connection has already
844                  * been closed!? We'll ignore that ... */
845                 io_close(r_fd);
846                 LogDebug("Auth: Got callback for unknown connection!?");
847                 return;
848         }
849         proc = Conn_GetProcStat(conn);
850         client = Conn_GetClient(conn);
851
852         /* Read result from pipe */
853         len = Proc_Read(proc, &result, sizeof(result));
854         if (len == 0)
855                 return;
856
857         if (len != sizeof(result)) {
858                 Log(LOG_CRIT, "Auth: Got malformed result!");
859                 Reject_Client(client);
860                 return;
861         }
862
863         if (result == true) {
864                 Client_SetUser(client, Client_OrigUser(client), true);
865                 (void)Hello_User_PostAuth(client);
866         } else
867                 Reject_Client(client);
868 }
869
870 #endif
871
872
873 static void
874 Reject_Client(CLIENT *Client)
875 {
876         Log(LOG_ERR,
877             "User \"%s\" rejected (connection %d): Access denied!",
878             Client_Mask(Client), Client_Conn(Client));
879         Conn_Close(Client_Conn(Client), NULL,
880                    "Access denied! Bad password?", true);
881 }
882
883
884 static bool
885 Hello_User_PostAuth(CLIENT *Client)
886 {
887         Introduce_Client(NULL, Client, CLIENT_USER);
888
889         if (!IRC_WriteStrClient
890             (Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client)))
891                 return false;
892         if (!IRC_WriteStrClient
893             (Client, RPL_YOURHOST_MSG, Client_ID(Client),
894              Client_ID(Client_ThisServer()), PACKAGE_VERSION, TARGET_CPU,
895              TARGET_VENDOR, TARGET_OS))
896                 return false;
897         if (!IRC_WriteStrClient
898             (Client, RPL_CREATED_MSG, Client_ID(Client), NGIRCd_StartStr))
899                 return false;
900         if (!IRC_WriteStrClient
901             (Client, RPL_MYINFO_MSG, Client_ID(Client),
902              Client_ID(Client_ThisServer()), PACKAGE_VERSION, USERMODES,
903              CHANMODES))
904                 return false;
905
906         /* Features supported by this server (005 numeric, ISUPPORT),
907          * see <http://www.irc.org/tech_docs/005.html> for details. */
908         if (!IRC_Send_ISUPPORT(Client))
909                 return DISCONNECTED;
910
911         if (!IRC_Send_LUSERS(Client))
912                 return DISCONNECTED;
913         if (!IRC_Show_MOTD(Client))
914                 return DISCONNECTED;
915
916         /* Suspend the client for a second ... */
917         IRC_SetPenalty(Client, 1);
918
919         return CONNECTED;
920 }
921
922
923 static void
924 Kill_Nick( char *Nick, char *Reason )
925 {
926         REQUEST r;
927
928         assert( Nick != NULL );
929         assert( Reason != NULL );
930
931         r.prefix = (char *)Client_ThisServer( );
932         r.argv[0] = Nick;
933         r.argv[1] = Reason;
934         r.argc = 2;
935
936         Log( LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s", Nick, Reason );
937         IRC_KILL( Client_ThisServer( ), &r );
938 } /* Kill_Nick */
939
940
941 static void
942 Introduce_Client(CLIENT *From, CLIENT *Client, int Type)
943 {
944         /* Set client type (user or service) */
945         Client_SetType(Client, Type);
946
947         if (From) {
948                 if (Conf_IsService(Conf_GetServer(Client_Conn(From)),
949                                    Client_ID(Client)))
950                         Client_SetType(Client, CLIENT_SERVICE);
951                 LogDebug("%s \"%s\" (+%s) registered (via %s, on %s, %d hop%s).",
952                          Client_TypeText(Client), Client_Mask(Client),
953                          Client_Modes(Client), Client_ID(From),
954                          Client_ID(Client_Introducer(Client)),
955                          Client_Hops(Client), Client_Hops(Client) > 1 ? "s": "");
956         } else {
957                 Log(LOG_NOTICE, "%s \"%s\" registered (connection %d).",
958                     Client_TypeText(Client), Client_Mask(Client),
959                     Client_Conn(Client));
960                 Log_ServerNotice('c', "Client connecting: %s (%s@%s) [%s] - %s",
961                                  Client_ID(Client), Client_User(Client),
962                                  Client_Hostname(Client),
963                                  Conn_IPA(Client_Conn(Client)),
964                                  Client_TypeText(Client));
965         }
966
967         /* Inform other servers */
968         IRC_WriteStrServersPrefixFlag_CB(From,
969                                 From != NULL ? From : Client_ThisServer(),
970                                 '\0', cb_introduceClient, (void *)Client);
971 } /* Introduce_Client */
972
973
974 static void
975 cb_introduceClient(CLIENT *To, CLIENT *Prefix, void *data)
976 {
977         CLIENT *c = (CLIENT *)data;
978         CONN_ID conn;
979         char *modes, *user, *host;
980
981         modes = Client_Modes(c);
982         user = Client_User(c) ? Client_User(c) : "-";
983         host = Client_Hostname(c) ? Client_Hostname(c) : "-";
984
985         conn = Client_Conn(To);
986         if (Conn_Options(conn) & CONN_RFC1459) {
987                 /* RFC 1459 mode: separate NICK and USER commands */
988                 Conn_WriteStr(conn, "NICK %s :%d", Client_ID(c),
989                               Client_Hops(c) + 1);
990                 Conn_WriteStr(conn, ":%s USER %s %s %s :%s",
991                               Client_ID(c), user, host,
992                               Client_ID(Client_Introducer(c)), Client_Info(c));
993                 if (modes[0])
994                         Conn_WriteStr(conn, ":%s MODE %s +%s",
995                                       Client_ID(c), Client_ID(c), modes);
996         } else {
997                 /* RFC 2813 mode: one combined NICK or SERVICE command */
998                 if (Client_Type(c) == CLIENT_SERVICE
999                     && strchr(Client_Flags(To), 'S'))
1000                         IRC_WriteStrClientPrefix(To, Prefix,
1001                                          "SERVICE %s %d * +%s %d :%s",
1002                                          Client_Mask(c),
1003                                          Client_MyToken(Client_Introducer(c)),
1004                                          Client_Modes(c), Client_Hops(c) + 1,
1005                                          Client_Info(c));
1006                 else
1007                         IRC_WriteStrClientPrefix(To, Prefix,
1008                                          "NICK %s %d %s %s %d +%s :%s",
1009                                          Client_ID(c), Client_Hops(c) + 1,
1010                                          user, host,
1011                                          Client_MyToken(Client_Introducer(c)),
1012                                          modes, Client_Info(c));
1013         }
1014 } /* cb_introduceClient */
1015
1016
1017 /* -eof- */