]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc-login.c
New function Introduce_Client() to announce new local and remote users.
[ngircd-alex.git] / src / ngircd / irc-login.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2008 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  * Login and logout
12  */
13
14
15 #include "portab.h"
16
17 #include "imp.h"
18 #include <assert.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <strings.h>
23
24 #include "ngircd.h"
25 #include "resolve.h"
26 #include "conn-func.h"
27 #include "conf.h"
28 #include "client.h"
29 #include "channel.h"
30 #include "log.h"
31 #include "messages.h"
32 #include "parse.h"
33 #include "irc.h"
34 #include "irc-info.h"
35 #include "irc-write.h"
36
37 #include "exp.h"
38 #include "irc-login.h"
39
40
41 static bool Hello_User PARAMS(( CLIENT *Client ));
42 static void Kill_Nick PARAMS(( char *Nick, char *Reason ));
43 static void Introduce_Client PARAMS(( CLIENT *From, const char *Nick,
44                                      const int HopCount, const char *User,
45                                      const char *Host, const int Token,
46                                      const char *Mode, const char *Name ));
47
48
49 /**
50  * Handler for the IRC command "PASS".
51  * See RFC 2813 section 4.1.1, and RFC 2812 section 3.1.1.
52  */
53 GLOBAL bool
54 IRC_PASS( CLIENT *Client, REQUEST *Req )
55 {
56         char *type, *orig_flags;
57         int protohigh, protolow;
58
59         assert( Client != NULL );
60         assert( Req != NULL );
61
62         /* Return an error if this is not a local client */
63         if (Client_Conn(Client) <= NONE)
64                 return IRC_WriteStrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
65                                           Client_ID(Client), Req->command);
66
67         if (Client_Type(Client) == CLIENT_UNKNOWN && Req->argc == 1) {
68                 /* Not yet registered "unknown" connection, PASS with one
69                  * argument: either a regular client, service, or server
70                  * using the old RFC 1459 section 4.1.1 syntax. */
71                 LogDebug("Connection %d: got PASS command ...",
72                          Client_Conn(Client));
73         } else if ((Client_Type(Client) == CLIENT_UNKNOWN ||
74                     Client_Type(Client) == CLIENT_UNKNOWNSERVER) &&
75                    (Req->argc == 3 || Req->argc == 4)) {
76                 /* Not yet registered "unknown" connection or outgoing server
77                  * link, PASS with three or four argument: server using the
78                  * RFC 2813 section 4.1.1 syntax. */
79                 LogDebug("Connection %d: got PASS command (new server link) ...",
80                          Client_Conn(Client));
81         } else if (Client_Type(Client) == CLIENT_UNKNOWN ||
82                    Client_Type(Client) == CLIENT_UNKNOWNSERVER) {
83                 /* Unregistered connection, but wrong number of arguments: */
84                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
85                                           Client_ID(Client), Req->command);
86         } else {
87                 /* Registered connection, PASS command is not allowed! */
88                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
89                                           Client_ID(Client));
90         }
91
92         Client_SetPassword(Client, Req->argv[0]);
93         Client_SetType(Client, CLIENT_GOTPASS);
94
95         /* Protocol version */
96         if (Req->argc >= 2 && strlen(Req->argv[1]) >= 4) {
97                 int c2, c4;
98
99                 c2 = Req->argv[1][2];
100                 c4 = Req->argv[1][4];
101
102                 Req->argv[1][4] = '\0';
103                 protolow = atoi(&Req->argv[1][2]);
104                 Req->argv[1][2] = '\0';
105                 protohigh = atoi(Req->argv[1]);
106
107                 Req->argv[1][2] = c2;
108                 Req->argv[1][4] = c4;
109         } else
110                 protohigh = protolow = 0;
111
112         /* Protocol type, see doc/Protocol.txt */
113         if (Req->argc >= 2 && strlen(Req->argv[1]) > 4)
114                 type = &Req->argv[1][4];
115         else
116                 type = NULL;
117
118         /* Protocol flags/options */
119         if (Req->argc >= 4)
120                 orig_flags = Req->argv[3];
121         else
122                 orig_flags = "";
123
124         /* Implementation, version and IRC+ flags */
125         if (Req->argc >= 3) {
126                 char *impl, *ptr, *serverver, *flags;
127
128                 impl = Req->argv[2];
129                 ptr = strchr(impl, '|');
130                 if (ptr)
131                         *ptr = '\0';
132
133                 if (type && strcmp(type, PROTOIRCPLUS) == 0) {
134                         /* The peer seems to be a server which supports the
135                          * IRC+ protocol (see doc/Protocol.txt). */
136                         serverver = ptr + 1;
137                         flags = strchr(serverver, ':');
138                         if (flags) {
139                                 *flags = '\0';
140                                 flags++;
141                         } else
142                                 flags = "";
143                         Log(LOG_INFO,
144                             "Peer announces itself as %s-%s using protocol %d.%d/IRC+ (flags: \"%s\").",
145                             impl, serverver, protohigh, protolow, flags);
146                 } else {
147                         /* The peer seems to be a server supporting the
148                          * "original" IRC protocol (RFC 2813). */
149                         serverver = "";
150                         if (strchr(orig_flags, 'Z'))
151                                 flags = "Z";
152                         else
153                                 flags = "";
154                         Log(LOG_INFO,
155                             "Peer announces itself as \"%s\" using protocol %d.%d (flags: \"%s\").",
156                             impl, protohigh, protolow, flags);
157                 }
158                 Client_SetFlags(Client, flags);
159         }
160
161         return CONNECTED;
162 } /* IRC_PASS */
163
164
165 /**
166  * IRC "NICK" command.
167  * This function implements the IRC command "NICK" which is used to register
168  * with the server, to change already registered nicknames and to introduce
169  * new users which are connected to other servers.
170  */
171 GLOBAL bool
172 IRC_NICK( CLIENT *Client, REQUEST *Req )
173 {
174         CLIENT *intr_c, *target, *c;
175         CONN_ID conn;
176         char *nick, *user, *hostname, *modes, *info;
177         int token, hops;
178
179         assert( Client != NULL );
180         assert( Req != NULL );
181
182 #ifndef STRICT_RFC
183         /* Some IRC clients, for example BitchX, send the NICK and USER
184          * commands in the wrong order ... */
185         if( Client_Type( Client ) == CLIENT_UNKNOWN
186             || Client_Type( Client ) == CLIENT_GOTPASS
187             || Client_Type( Client ) == CLIENT_GOTNICK
188             || Client_Type( Client ) == CLIENT_GOTUSER
189             || Client_Type( Client ) == CLIENT_USER
190             || ( Client_Type( Client ) == CLIENT_SERVER && Req->argc == 1 ))
191 #else
192         if( Client_Type( Client ) == CLIENT_UNKNOWN
193             || Client_Type( Client ) == CLIENT_GOTPASS
194             || Client_Type( Client ) == CLIENT_GOTNICK
195             || Client_Type( Client ) == CLIENT_USER
196             || ( Client_Type( Client ) == CLIENT_SERVER && Req->argc == 1 ))
197 #endif
198         {
199                 /* User registration or change of nickname */
200
201                 /* Wrong number of arguments? */
202                 if( Req->argc != 1 )
203                         return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG,
204                                                    Client_ID( Client ),
205                                                    Req->command );
206
207                 /* Search "target" client */
208                 if( Client_Type( Client ) == CLIENT_SERVER )
209                 {
210                         target = Client_Search( Req->prefix );
211                         if( ! target )
212                                 return IRC_WriteStrClient( Client,
213                                                            ERR_NOSUCHNICK_MSG,
214                                                            Client_ID( Client ),
215                                                            Req->argv[0] );
216                 }
217                 else
218                 {
219                         /* Is this a restricted client? */
220                         if( Client_HasMode( Client, 'r' ))
221                                 return IRC_WriteStrClient( Client,
222                                                            ERR_RESTRICTED_MSG,
223                                                            Client_ID( Client ));
224
225                         target = Client;
226                 }
227
228 #ifndef STRICT_RFC
229                 /* If the clients tries to change to its own nickname we won't
230                  * do anything. This is how the original ircd behaves and some
231                  * clients (for example Snak) expect it to be like this.
232                  * But I doubt that this is "really the right thing" ... */
233                 if( strcmp( Client_ID( target ), Req->argv[0] ) == 0 )
234                         return CONNECTED;
235 #endif
236
237                 /* Check that the new nickname is available. Special case:
238                  * the client only changes from/to upper to lower case. */
239                 if( strcasecmp( Client_ID( target ), Req->argv[0] ) != 0 )
240                 {
241                         if( ! Client_CheckNick( target, Req->argv[0] ))
242                                 return CONNECTED;
243                 }
244
245                 if(( Client_Type( target ) != CLIENT_USER )
246                    && ( Client_Type( target ) != CLIENT_SERVER ))
247                 {
248                         /* New client */
249                         Log( LOG_DEBUG, "Connection %d: got valid NICK command ...", 
250                              Client_Conn( Client ));
251
252                         /* Register new nickname of this client */
253                         Client_SetID( target, Req->argv[0] );
254
255                         /* If we received a valid USER command already then
256                          * register the new client! */
257                         if( Client_Type( Client ) == CLIENT_GOTUSER )
258                                 return Hello_User( Client );
259                         else
260                                 Client_SetType( Client, CLIENT_GOTNICK );
261                 }
262                 else
263                 {
264                         /* Nickname change */
265                         if (Client_Conn(target) > NONE) {
266                                 /* Local client */
267                                 Log(LOG_INFO,
268                                     "User \"%s\" changed nick (connection %d): \"%s\" -> \"%s\".",
269                                     Client_Mask(target), Client_Conn(target),
270                                     Client_ID(target), Req->argv[0]);
271                                 Conn_UpdateIdle(Client_Conn(target));
272                         }
273                         else
274                         {
275                                 /* Remote client */
276                                 Log( LOG_DEBUG,
277                                      "User \"%s\" changed nick: \"%s\" -> \"%s\".",
278                                      Client_Mask( target ), Client_ID( target ),
279                                      Req->argv[0] );
280                         }
281
282                         /* Inform all users and servers (which have to know)
283                          * of this nickname change */
284                         if( Client_Type( Client ) == CLIENT_USER )
285                                 IRC_WriteStrClientPrefix( Client, Client,
286                                                           "NICK :%s",
287                                                           Req->argv[0] );
288                         IRC_WriteStrServersPrefix( Client, target,
289                                                    "NICK :%s", Req->argv[0] );
290                         IRC_WriteStrRelatedPrefix( target, target, false,
291                                                    "NICK :%s", Req->argv[0] );
292
293                         /* Register old nickname for WHOWAS queries */
294                         Client_RegisterWhowas( target );
295
296                         /* Save new nickname */
297                         Client_SetID( target, Req->argv[0] );
298
299                         IRC_SetPenalty( target, 2 );
300                 }
301
302                 return CONNECTED;
303         } else if(Client_Type(Client) == CLIENT_SERVER ||
304                   Client_Type(Client) == CLIENT_SERVICE) {
305                 /* Server or service introduces new client */
306
307                 /* Bad number of parameters? */
308                 if (Req->argc != 2 && Req->argc != 7)
309                         return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
310                                                   Client_ID(Client), Req->command);
311
312                 if (Req->argc >= 7) {
313                         /* RFC 2813 compatible syntax */
314                         nick = Req->argv[0];
315                         hops = atoi(Req->argv[1]);
316                         user = Req->argv[2];
317                         hostname = Req->argv[3];
318                         token = atoi(Req->argv[4]);
319                         modes = Req->argv[5] + 1;
320                         info = Req->argv[6];
321                 } else {
322                         /* RFC 1459 compatible syntax */
323                         nick = Req->argv[0];
324                         hops = 1;
325                         user = Req->argv[0];
326                         hostname = Client_ID(Client);
327                         token = atoi(Req->argv[1]);
328                         modes = "";
329                         info = Req->argv[0];
330
331                         conn = Client_Conn(Client);
332                         if (conn != NONE &&
333                             !(Conn_Options(conn) & CONN_RFC1459)) {
334                                 Log(LOG_INFO,
335                                     "Switching connection %d (\"%s\") to RFC 1459 compatibility mode.",
336                                     conn, Client_ID(Client));
337                                 Conn_SetOption(conn, CONN_RFC1459);
338                         }
339                 }
340
341                 /* Nick ueberpruefen */
342                 c = Client_Search(nick);
343                 if(c) {
344                         /* Der neue Nick ist auf diesem Server bereits registriert:
345                          * sowohl der neue, als auch der alte Client muessen nun
346                          * disconnectiert werden. */
347                         Log( LOG_ERR, "Server %s introduces already registered nick \"%s\"!", Client_ID( Client ), Req->argv[0] );
348                         Kill_Nick( Req->argv[0], "Nick collision" );
349                         return CONNECTED;
350                 }
351
352                 /* Server, zu dem der Client connectiert ist, suchen */
353                 intr_c = Client_GetFromToken(Client, token);
354                 if( ! intr_c )
355                 {
356                         Log( LOG_ERR, "Server %s introduces nick \"%s\" on unknown server!?", Client_ID( Client ), Req->argv[0] );
357                         Kill_Nick( Req->argv[0], "Unknown server" );
358                         return CONNECTED;
359                 }
360
361                 /* Neue Client-Struktur anlegen */
362                 c = Client_NewRemoteUser(intr_c, nick, hops, user, hostname,
363                                          token, modes, info, true);
364                 if( ! c )
365                 {
366                         /* Eine neue Client-Struktur konnte nicht angelegt werden.
367                          * Der Client muss disconnectiert werden, damit der Netz-
368                          * status konsistent bleibt. */
369                         Log( LOG_ALERT, "Can't create client structure! (on connection %d)", Client_Conn( Client ));
370                         Kill_Nick( Req->argv[0], "Server error" );
371                         return CONNECTED;
372                 }
373
374                 modes = Client_Modes( c );
375                 if( *modes ) Log( LOG_DEBUG, "User \"%s\" (+%s) registered (via %s, on %s, %d hop%s).", Client_Mask( c ), modes, Client_ID( Client ), Client_ID( intr_c ), Client_Hops( c ), Client_Hops( c ) > 1 ? "s": "" );
376                 else Log( LOG_DEBUG, "User \"%s\" registered (via %s, on %s, %d hop%s).", Client_Mask( c ), Client_ID( Client ), Client_ID( intr_c ), Client_Hops( c ), Client_Hops( c ) > 1 ? "s": "" );
377
378                 /* Inform other servers about the new client */
379                 Introduce_Client(Client, Req->argv[0], atoi(Req->argv[1]) + 1,
380                                  Req->argv[2], Req->argv[3],
381                                  Client_MyToken(intr_c), Req->argv[5],
382                                  Req->argv[6]);
383
384                 return CONNECTED;
385         }
386         else return IRC_WriteStrClient( Client, ERR_ALREADYREGISTRED_MSG, Client_ID( Client ));
387 } /* IRC_NICK */
388
389
390 /**
391  * Handler for the IRC command "USER".
392  */
393 GLOBAL bool
394 IRC_USER(CLIENT * Client, REQUEST * Req)
395 {
396         CLIENT *c;
397 #ifdef IDENTAUTH
398         char *ptr;
399 #endif
400
401         assert(Client != NULL);
402         assert(Req != NULL);
403
404         if (Client_Type(Client) == CLIENT_GOTNICK ||
405 #ifndef STRICT_RFC
406             Client_Type(Client) == CLIENT_UNKNOWN ||
407 #endif
408             Client_Type(Client) == CLIENT_GOTPASS)
409         {
410                 /* New connection */
411                 if (Req->argc != 4)
412                         return IRC_WriteStrClient(Client,
413                                                   ERR_NEEDMOREPARAMS_MSG,
414                                                   Client_ID(Client),
415                                                   Req->command);
416
417                 /* User name */
418 #ifdef IDENTAUTH
419                 ptr = Client_User(Client);
420                 if (!ptr || !*ptr || *ptr == '~')
421                         Client_SetUser(Client, Req->argv[0], false);
422 #else
423                 Client_SetUser(Client, Req->argv[0], false);
424 #endif
425
426                 /* "Real name" or user info text: Don't set it to the empty
427                  * string, the original ircd can't deal with such "real names"
428                  * (e. g. "USER user * * :") ... */
429                 if (*Req->argv[3])
430                         Client_SetInfo(Client, Req->argv[3]);
431                 else
432                         Client_SetInfo(Client, "-");
433
434                 LogDebug("Connection %d: got valid USER command ...",
435                     Client_Conn(Client));
436                 if (Client_Type(Client) == CLIENT_GOTNICK)
437                         return Hello_User(Client);
438                 else
439                         Client_SetType(Client, CLIENT_GOTUSER);
440                 return CONNECTED;
441
442         } else if (Client_Type(Client) == CLIENT_SERVER ||
443                    Client_Type(Client) == CLIENT_SERVICE) {
444                 /* Server/service updating an user */
445                 if (Req->argc != 4)
446                         return IRC_WriteStrClient(Client,
447                                                   ERR_NEEDMOREPARAMS_MSG,
448                                                   Client_ID(Client),
449                                                   Req->command);
450                 c = Client_Search(Req->prefix);
451                 if (!c)
452                         return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
453                                                   Client_ID(Client),
454                                                   Req->prefix);
455
456                 Client_SetUser(c, Req->argv[0], true);
457                 Client_SetHostname(c, Req->argv[1]);
458                 Client_SetInfo(c, Req->argv[3]);
459
460                 LogDebug("Connection %d: got valid USER command for \"%s\".",
461                          Client_Conn(Client), Client_Mask(c));
462                 return CONNECTED;
463         } else if (Client_Type(Client) == CLIENT_USER) {
464                 /* Already registered connection */
465                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
466                                           Client_ID(Client));
467         } else {
468                 /* Unexpected/invalid connection state? */
469                 return IRC_WriteStrClient(Client, ERR_NOTREGISTERED_MSG,
470                                           Client_ID(Client));
471         }
472 } /* IRC_USER */
473
474
475 /**
476  * Service registration.
477  * ngIRCd does not support services at the moment, so this function is a
478  * dummy that returns ERR_ERRONEUSNICKNAME on each call.
479  */
480 GLOBAL bool
481 IRC_SERVICE(CLIENT *Client, REQUEST *Req)
482 {
483         assert(Client != NULL);
484         assert(Req != NULL);
485
486         if (Client_Type(Client) != CLIENT_GOTPASS)
487                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
488                                           Client_ID(Client));
489
490         if (Req->argc != 6)
491                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
492                                           Client_ID(Client), Req->command);
493
494         return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
495                                   Client_ID(Client), Req->argv[0]);
496 } /* IRC_SERVICE */
497
498
499 GLOBAL bool
500 IRC_QUIT( CLIENT *Client, REQUEST *Req )
501 {
502         CLIENT *target;
503         char quitmsg[LINE_LEN];
504
505         assert( Client != NULL );
506         assert( Req != NULL );
507
508         /* Wrong number of arguments? */
509         if( Req->argc > 1 )
510                 return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
511
512         if (Req->argc == 1)
513                 strlcpy(quitmsg, Req->argv[0], sizeof quitmsg);
514
515         if ( Client_Type( Client ) == CLIENT_SERVER )
516         {
517                 /* Server */
518                 target = Client_Search( Req->prefix );
519                 if( ! target )
520                 {
521                         /* Den Client kennen wir nicht (mehr), also nichts zu tun. */
522                         Log( LOG_WARNING, "Got QUIT from %s for unknown client!?", Client_ID( Client ));
523                         return CONNECTED;
524                 }
525
526                 Client_Destroy( target, "Got QUIT command.", Req->argc == 1 ? quitmsg : NULL, true);
527
528                 return CONNECTED;
529         }
530         else
531         {
532                 if (Req->argc == 1 && quitmsg[0] != '\"') {
533                         /* " " to avoid confusion */
534                         strlcpy(quitmsg, "\"", sizeof quitmsg);
535                         strlcat(quitmsg, Req->argv[0], sizeof quitmsg-1);
536                         strlcat(quitmsg, "\"", sizeof quitmsg );
537                 }
538
539                 /* User, Service, oder noch nicht registriert */
540                 Conn_Close( Client_Conn( Client ), "Got QUIT command.", Req->argc == 1 ? quitmsg : NULL, true);
541
542                 return DISCONNECTED;
543         }
544 } /* IRC_QUIT */
545
546
547 GLOBAL bool
548 IRC_PING(CLIENT *Client, REQUEST *Req)
549 {
550         CLIENT *target, *from;
551
552         assert(Client != NULL);
553         assert(Req != NULL);
554
555         /* Wrong number of arguments? */
556         if (Req->argc < 1)
557                 return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
558                                           Client_ID(Client));
559 #ifdef STRICT_RFC
560         /* Don't ignore additional arguments when in "strict" mode */
561         if (Req->argc > 2)
562                  return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
563                                            Client_ID(Client), Req->command);
564 #endif
565
566         if (Req->argc > 1) {
567                 /* A target has been specified ... */
568                 target = Client_Search(Req->argv[1]);
569
570                 if (!target || Client_Type(target) != CLIENT_SERVER)
571                         return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
572                                         Client_ID(Client), Req->argv[1]);
573
574                 if (target != Client_ThisServer()) {
575                         /* Ok, we have to forward the PING */
576                         if (Client_Type(Client) == CLIENT_SERVER)
577                                 from = Client_Search(Req->prefix);
578                         else
579                                 from = Client;
580                         if (!from)
581                                 return IRC_WriteStrClient(Client,
582                                                 ERR_NOSUCHSERVER_MSG,
583                                                 Client_ID(Client), Req->prefix);
584
585                         return IRC_WriteStrClientPrefix(target, from,
586                                         "PING %s :%s", Req->argv[0],
587                                         Req->argv[1] );
588                 }
589         }
590
591         if (Client_Type(Client) == CLIENT_SERVER) {
592                 if (Req->prefix)
593                         from = Client_Search(Req->prefix);
594                 else
595                         from = Client;
596         } else
597                 from = Client_ThisServer();
598         if (!from)
599                 return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
600                                         Client_ID(Client), Req->prefix);
601
602         Log(LOG_DEBUG, "Connection %d: got PING, sending PONG ...",
603             Client_Conn(Client));
604
605 #ifdef STRICT_RFC
606         return IRC_WriteStrClient(Client, "PONG %s :%s",
607                 Client_ID(from), Client_ID(Client));
608 #else
609         /* Some clients depend on the argument being returned in the PONG
610          * reply (not mentioned in any RFC, though) */
611         return IRC_WriteStrClient(Client, "PONG %s :%s",
612                 Client_ID(from), Req->argv[0]);
613 #endif
614 } /* IRC_PING */
615
616
617 GLOBAL bool
618 IRC_PONG(CLIENT *Client, REQUEST *Req)
619 {
620         CLIENT *target, *from;
621         char *s;
622
623         assert(Client != NULL);
624         assert(Req != NULL);
625
626         /* Wrong number of arguments? */
627         if (Req->argc < 1)
628                 return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
629                                           Client_ID(Client));
630         if (Req->argc > 2)
631                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
632                                           Client_ID(Client), Req->command);
633
634         /* Forward? */
635         if (Req->argc == 2 && Client_Type(Client) == CLIENT_SERVER) {
636                 target = Client_Search(Req->argv[0]);
637                 if (!target)
638                         return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
639                                         Client_ID(Client), Req->argv[0]);
640
641                 from = Client_Search(Req->prefix);
642
643                 if (target != Client_ThisServer() && target != from) {
644                         /* Ok, we have to forward the message. */
645                         if (!from)
646                                 return IRC_WriteStrClient(Client,
647                                                 ERR_NOSUCHSERVER_MSG,
648                                                 Client_ID(Client), Req->prefix);
649
650                         if (Client_Type(Client_NextHop(target)) != CLIENT_SERVER)
651                                 s = Client_ID(from);
652                         else
653                                 s = Req->argv[0];
654                         return IRC_WriteStrClientPrefix(target, from,
655                                  "PONG %s :%s", s, Req->argv[1]);
656                 }
657         }
658
659         /* The connection timestamp has already been updated when the data has
660          * been read from so socket, so we don't need to update it here. */
661
662         if (Client_Conn(Client) > NONE)
663                 Log(LOG_DEBUG,
664                         "Connection %d: received PONG. Lag: %ld seconds.",
665                         Client_Conn(Client),
666                         time(NULL) - Conn_LastPing(Client_Conn(Client)));
667         else
668                  Log(LOG_DEBUG,
669                         "Connection %d: received PONG.", Client_Conn(Client));
670
671         return CONNECTED;
672 } /* IRC_PONG */
673
674
675 static bool
676 Hello_User(CLIENT * Client)
677 {
678         char modes[CLIENT_MODE_LEN + 1] = "+";
679
680         assert(Client != NULL);
681
682         /* Check password ... */
683         if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0) {
684                 /* Bad password! */
685                 Log(LOG_ERR,
686                     "User \"%s\" rejected (connection %d): Bad password!",
687                     Client_Mask(Client), Client_Conn(Client));
688                 Conn_Close(Client_Conn(Client), NULL, "Bad password", true);
689                 return DISCONNECTED;
690         }
691
692         Log(LOG_NOTICE, "User \"%s\" registered (connection %d).",
693             Client_Mask(Client), Client_Conn(Client));
694
695         /* Inform other servers */
696         strlcat(modes, Client_Modes(Client), sizeof(modes));
697         Introduce_Client(NULL, Client_ID(Client), 1, Client_User(Client),
698                          Client_Hostname(Client), 1, modes, Client_Info(Client));
699
700         if (!IRC_WriteStrClient
701             (Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client)))
702                 return false;
703         if (!IRC_WriteStrClient
704             (Client, RPL_YOURHOST_MSG, Client_ID(Client),
705              Client_ID(Client_ThisServer()), PACKAGE_VERSION, TARGET_CPU,
706              TARGET_VENDOR, TARGET_OS))
707                 return false;
708         if (!IRC_WriteStrClient
709             (Client, RPL_CREATED_MSG, Client_ID(Client), NGIRCd_StartStr))
710                 return false;
711         if (!IRC_WriteStrClient
712             (Client, RPL_MYINFO_MSG, Client_ID(Client),
713              Client_ID(Client_ThisServer()), PACKAGE_VERSION, USERMODES,
714              CHANMODES))
715                 return false;
716
717         /* Features supported by this server (005 numeric, ISUPPORT),
718          * see <http://www.irc.org/tech_docs/005.html> for details. */
719         if (!IRC_Send_ISUPPORT(Client))
720                 return DISCONNECTED;
721
722         Client_SetType(Client, CLIENT_USER);
723
724         if (!IRC_Send_LUSERS(Client))
725                 return DISCONNECTED;
726         if (!IRC_Show_MOTD(Client))
727                 return DISCONNECTED;
728
729         /* Suspend the client for a second ... */
730         IRC_SetPenalty(Client, 1);
731
732         return CONNECTED;
733 } /* Hello_User */
734
735
736 static void
737 Kill_Nick( char *Nick, char *Reason )
738 {
739         REQUEST r;
740
741         assert( Nick != NULL );
742         assert( Reason != NULL );
743
744         r.prefix = (char *)Client_ThisServer( );
745         r.argv[0] = Nick;
746         r.argv[1] = Reason;
747         r.argc = 2;
748
749         Log( LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s", Nick, Reason );
750         IRC_KILL( Client_ThisServer( ), &r );
751 } /* Kill_Nick */
752
753
754 static void
755 Introduce_Client(CLIENT *From, const char *Nick, const int HopCount,
756 const char *User, const char *Host, const int Token, const char *Mode,
757 const char *Name)
758 {
759         IRC_WriteStrServersPrefix(From,
760                                   From != NULL ? From : Client_ThisServer(),
761                                   "NICK %s %d %s %s %d %s :%s",
762                                   Nick, HopCount, User, Host, Token, Mode, Name);
763 } /* Introduce_Client */
764
765
766 /* -eof- */