- Logging von doppelten ID's verbessert.
[ngircd-alex.git] / src / ngircd / irc-login.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
4  *
5  * Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen
6  * der GNU General Public License (GPL), wie von der Free Software Foundation
7  * herausgegeben, weitergeben und/oder modifizieren, entweder unter Version 2
8  * der Lizenz oder (wenn Sie es wuenschen) jeder spaeteren Version.
9  * Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste
10  * der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS.
11  *
12  * $Id: irc-login.c,v 1.26 2002/12/03 18:56:33 alex Exp $
13  *
14  * irc-login.c: Anmeldung und Abmeldung im IRC
15  */
16
17
18 #include "portab.h"
19
20 #include "imp.h"
21 #include <assert.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "ngircd.h"
27 #include "resolve.h"
28 #include "conf.h"
29 #include "conn.h"
30 #include "client.h"
31 #include "channel.h"
32 #include "log.h"
33 #include "messages.h"
34 #include "parse.h"
35 #include "irc-info.h"
36 #include "irc-write.h"
37
38 #include "exp.h"
39 #include "irc-login.h"
40
41
42 LOCAL BOOLEAN Hello_User PARAMS(( CLIENT *Client ));
43 LOCAL VOID Kill_Nick PARAMS(( CHAR *Nick, CHAR *Reason ));
44
45
46 GLOBAL BOOLEAN
47 IRC_PASS( CLIENT *Client, REQUEST *Req )
48 {
49         assert( Client != NULL );
50         assert( Req != NULL );
51
52         /* Fehler liefern, wenn kein lokaler Client */
53         if( Client_Conn( Client ) <= NONE ) return IRC_WriteStrClient( Client, ERR_UNKNOWNCOMMAND_MSG, Client_ID( Client ), Req->command );
54         
55         if(( Client_Type( Client ) == CLIENT_UNKNOWN ) && ( Req->argc == 1))
56         {
57                 /* noch nicht registrierte unbekannte Verbindung */
58                 Log( LOG_DEBUG, "Connection %d: got PASS command ...", Client_Conn( Client ));
59
60                 /* Passwort speichern */
61                 Client_SetPassword( Client, Req->argv[0] );
62
63                 Client_SetType( Client, CLIENT_GOTPASS );
64                 return CONNECTED;
65         }
66         else if((( Client_Type( Client ) == CLIENT_UNKNOWN ) || ( Client_Type( Client ) == CLIENT_UNKNOWNSERVER )) && (( Req->argc == 3 ) || ( Req->argc == 4 )))
67         {
68                 CHAR c2, c4, *type, *impl, *serverver, *flags, *ptr, *ircflags;
69                 INT protohigh, protolow;
70
71                 /* noch nicht registrierte Server-Verbindung */
72                 Log( LOG_DEBUG, "Connection %d: got PASS command (new server link) ...", Client_Conn( Client ));
73
74                 /* Passwort speichern */
75                 Client_SetPassword( Client, Req->argv[0] );
76
77                 /* Protokollversion ermitteln */
78                 if( strlen( Req->argv[1] ) >= 4 )
79                 {
80                         c2 = Req->argv[1][2];
81                         c4 = Req->argv[1][4];
82
83                         Req->argv[1][4] = '\0';
84                         protolow = atoi( &Req->argv[1][2] );
85                         Req->argv[1][2] = '\0';
86                         protohigh = atoi( Req->argv[1] );
87                         
88                         Req->argv[1][2] = c2;
89                         Req->argv[1][4] = c4;
90                 }                       
91                 else protohigh = protolow = 0;
92
93                 /* Protokoll-Typ */
94                 if( strlen( Req->argv[1] ) > 4 ) type = &Req->argv[1][4];
95                 else type = NULL;
96
97                 /* IRC-Flags (nach RFC 2813) */
98                 if( Req->argc >= 4 ) ircflags = Req->argv[3];
99                 else ircflags = "";
100
101                 /* Implementation, Version und ngIRCd-Flags */
102                 impl = Req->argv[2];
103                 ptr = strchr( impl, '|' );
104                 if( ptr ) *ptr = '\0';
105
106                 if( type && ( strcmp( type, PROTOIRCPLUS ) == 0 ))
107                 {
108                         /* auf der anderen Seite laeuft ein Server, der
109                          * ebenfalls das IRC+-Protokoll versteht */
110                         serverver = ptr + 1;
111                         flags = strchr( serverver, ':' );
112                         if( flags )
113                         {
114                                 *flags = '\0';
115                                 flags++;
116                         }
117                         else flags = "";
118                         Log( LOG_INFO, "Peer announces itself as %s-%s using protocol %d.%d/IRC+ (flags: \"%s\").", impl, serverver, protohigh, protolow, flags );
119                 }
120                 else
121                 {
122                         /* auf der anderen Seite laeuft ein Server, der
123                          * nur das Originalprotokoll unterstuetzt */
124                         serverver = "";
125                         if( strchr( ircflags, 'Z' )) flags = "Z";
126                         else flags = "";
127                         Log( LOG_INFO, "Peer announces itself as \"%s\" using protocol %d.%d (flags: \"%s\").", impl, protohigh, protolow, flags );
128                 }
129
130                 Client_SetType( Client, CLIENT_GOTPASSSERVER );
131                 Client_SetFlags( Client, flags );
132
133                 return CONNECTED;
134         }
135         else if(( Client_Type( Client ) == CLIENT_UNKNOWN  ) || ( Client_Type( Client ) == CLIENT_UNKNOWNSERVER ))
136         {
137                 /* Falsche Anzahl Parameter? */
138                 return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
139         }
140         else return IRC_WriteStrClient( Client, ERR_ALREADYREGISTRED_MSG, Client_ID( Client ));
141 } /* IRC_PASS */
142
143
144 GLOBAL BOOLEAN
145 IRC_NICK( CLIENT *Client, REQUEST *Req )
146 {
147         CLIENT *intr_c, *target, *c;
148         CHAR *modes;
149
150         assert( Client != NULL );
151         assert( Req != NULL );
152
153         /* Zumindest BitchX sendet NICK-USER in der falschen Reihenfolge. */
154 #ifndef STRICT_RFC
155         if( Client_Type( Client ) == CLIENT_UNKNOWN || Client_Type( Client ) == CLIENT_GOTPASS || Client_Type( Client ) == CLIENT_GOTNICK || Client_Type( Client ) == CLIENT_GOTUSER || Client_Type( Client ) == CLIENT_USER || ( Client_Type( Client ) == CLIENT_SERVER && Req->argc == 1 ))
156 #else
157         if( Client_Type( Client ) == CLIENT_UNKNOWN || Client_Type( Client ) == CLIENT_GOTPASS || Client_Type( Client ) == CLIENT_GOTNICK || Client_Type( Client ) == CLIENT_USER || ( Client_Type( Client ) == CLIENT_SERVER && Req->argc == 1 ))
158 #endif
159         {
160                 /* User-Registrierung bzw. Nick-Aenderung */
161
162                 /* Falsche Anzahl Parameter? */
163                 if( Req->argc != 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
164
165                 /* "Ziel-Client" ermitteln */
166                 if( Client_Type( Client ) == CLIENT_SERVER )
167                 {
168                         target = Client_Search( Req->prefix );
169                         if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->argv[0] );
170                 }
171                 else
172                 {
173                         /* Ist der Client "restricted"? */
174                         if( Client_HasMode( Client, 'r' )) return IRC_WriteStrClient( Client, ERR_RESTRICTED_MSG, Client_ID( Client ));
175                         target = Client;
176                 }
177
178 #ifndef STRICT_RFC
179                 /* Wenn der Client zu seinem eigenen Nick wechseln will, so machen
180                  * wir nichts. So macht es das Original und mind. Snak hat probleme,
181                  * wenn wir es nicht so machen. Ob es so okay ist? Hm ... */
182                 if( strcmp( Client_ID( target ), Req->argv[0] ) == 0 ) return CONNECTED;
183 #endif
184
185                 /* pruefen, ob Nick bereits vergeben. Speziallfall: der Client
186                  * will nur die Gross- und Kleinschreibung aendern. Das darf
187                  * er natuerlich machen :-) */
188                 if( strcasecmp( Client_ID( target ), Req->argv[0] ) != 0 )
189                 {
190                         if( ! Client_CheckNick( target, Req->argv[0] )) return CONNECTED;
191                 }
192
193                 if(( Client_Type( target ) != CLIENT_USER ) && ( Client_Type( target ) != CLIENT_SERVER ))
194                 {
195                         /* Neuer Client */
196                         Log( LOG_DEBUG, "Connection %d: got valid NICK command ...", Client_Conn( Client ));
197
198                         /* Client-Nick registrieren */
199                         Client_SetID( target, Req->argv[0] );
200
201                         /* schon ein USER da? Dann registrieren! */
202                         if( Client_Type( Client ) == CLIENT_GOTUSER ) return Hello_User( Client );
203                         else Client_SetType( Client, CLIENT_GOTNICK );
204                 }
205                 else
206                 {
207                         /* Nick-Aenderung */
208                         if( Client_Conn( target ) > NONE )
209                         {
210                                 /* lokaler Client */
211                                 Log( LOG_INFO, "User \"%s\" changed nick (connection %d): \"%s\" -> \"%s\".", Client_Mask( target ), Client_Conn( target ), Client_ID( target ), Req->argv[0] );
212                         }
213                         else
214                         {
215                                 /* Remote-Client */
216                                 Log( LOG_DEBUG, "User \"%s\" changed nick: \"%s\" -> \"%s\".", Client_Mask( target ), Client_ID( target ), Req->argv[0] );
217                         }
218
219                         /* alle betroffenen User und Server ueber Nick-Aenderung informieren */
220                         if( Client_Type( Client ) == CLIENT_USER ) IRC_WriteStrClientPrefix( Client, Client, "NICK :%s", Req->argv[0] );
221                         IRC_WriteStrServersPrefix( Client, target, "NICK :%s", Req->argv[0] );
222                         IRC_WriteStrRelatedPrefix( target, target, FALSE, "NICK :%s", Req->argv[0] );
223                         
224                         /* neuen Client-Nick speichern */
225                         Client_SetID( target, Req->argv[0] );
226                 }
227
228                 return CONNECTED;
229         }
230         else if( Client_Type( Client ) == CLIENT_SERVER )
231         {
232                 /* Server fuehrt neuen Client ein */
233
234                 /* Falsche Anzahl Parameter? */
235                 if( Req->argc != 7 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
236
237                 /* Nick ueberpruefen */
238                 c = Client_Search( Req->argv[0] );
239                 if( c )
240                 {
241                         /* Der neue Nick ist auf diesem Server bereits registriert:
242                          * sowohl der neue, als auch der alte Client muessen nun
243                          * disconnectiert werden. */
244                         Log( LOG_ERR, "Server %s introduces already registered nick \"%s\"!", Client_ID( Client ), Req->argv[0] );
245                         Kill_Nick( Req->argv[0], "Nick collision" );
246                         return CONNECTED;
247                 }
248
249                 /* Server, zu dem der Client connectiert ist, suchen */
250                 intr_c = Client_GetFromToken( Client, atoi( Req->argv[4] ));
251                 if( ! intr_c )
252                 {
253                         Log( LOG_ERR, "Server %s introduces nick \"%s\" on unknown server!?", Client_ID( Client ), Req->argv[0] );
254                         Kill_Nick( Req->argv[0], "Unknown server" );
255                         return CONNECTED;
256                 }
257
258                 /* Neue Client-Struktur anlegen */
259                 c = Client_NewRemoteUser( intr_c, Req->argv[0], atoi( Req->argv[1] ), Req->argv[2], Req->argv[3], atoi( Req->argv[4] ), Req->argv[5] + 1, Req->argv[6], TRUE );
260                 if( ! c )
261                 {
262                         /* Eine neue Client-Struktur konnte nicht angelegt werden.
263                          * Der Client muss disconnectiert werden, damit der Netz-
264                          * status konsistent bleibt. */
265                         Log( LOG_ALERT, "Can't create client structure! (on connection %d)", Client_Conn( Client ));
266                         Kill_Nick( Req->argv[0], "Server error" );
267                         return CONNECTED;
268                 }
269
270                 modes = Client_Modes( c );
271                 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": "" );
272                 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": "" );
273
274                 /* Andere Server, ausser dem Introducer, informieren */
275                 IRC_WriteStrServersPrefix( Client, Client, "NICK %s %d %s %s %d %s :%s", Req->argv[0], atoi( Req->argv[1] ) + 1, Req->argv[2], Req->argv[3], Client_MyToken( intr_c ), Req->argv[5], Req->argv[6] );
276
277                 return CONNECTED;
278         }
279         else return IRC_WriteStrClient( Client, ERR_ALREADYREGISTRED_MSG, Client_ID( Client ));
280 } /* IRC_NICK */
281
282
283 GLOBAL BOOLEAN
284 IRC_USER( CLIENT *Client, REQUEST *Req )
285 {
286         assert( Client != NULL );
287         assert( Req != NULL );
288
289 #ifndef STRICT_RFC
290         if( Client_Type( Client ) == CLIENT_GOTNICK || Client_Type( Client ) == CLIENT_GOTPASS || Client_Type( Client ) == CLIENT_UNKNOWN )
291 #else
292         if( Client_Type( Client ) == CLIENT_GOTNICK || Client_Type( Client ) == CLIENT_GOTPASS )
293 #endif
294         {
295                 /* Falsche Anzahl Parameter? */
296                 if( Req->argc != 4 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
297
298                 Client_SetUser( Client, Req->argv[0], FALSE );
299                 Client_SetInfo( Client, Req->argv[3] );
300
301                 Log( LOG_DEBUG, "Connection %d: got valid USER command ...", Client_Conn( Client ));
302                 if( Client_Type( Client ) == CLIENT_GOTNICK ) return Hello_User( Client );
303                 else Client_SetType( Client, CLIENT_GOTUSER );
304                 return CONNECTED;
305         }
306         else if( Client_Type( Client ) == CLIENT_USER || Client_Type( Client ) == CLIENT_SERVER || Client_Type( Client ) == CLIENT_SERVICE )
307         {
308                 return IRC_WriteStrClient( Client, ERR_ALREADYREGISTRED_MSG, Client_ID( Client ));
309         }
310         else return IRC_WriteStrClient( Client, ERR_NOTREGISTERED_MSG, Client_ID( Client ));
311 } /* IRC_USER */
312
313
314 GLOBAL BOOLEAN
315 IRC_QUIT( CLIENT *Client, REQUEST *Req )
316 {
317         CLIENT *target;
318         
319         assert( Client != NULL );
320         assert( Req != NULL );
321
322         if ( Client_Type( Client ) == CLIENT_SERVER )
323         {
324                 /* Server */
325
326                 /* Falsche Anzahl Parameter? */
327                 if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
328
329                 target = Client_Search( Req->prefix );
330                 if( ! target )
331                 {
332                         /* Den Client kennen wir nicht (mehr), also nichts zu tun. */
333                         Log( LOG_WARNING, "Got QUIT from %s for unknown client!?", Client_ID( Client ));
334                         return CONNECTED;
335                 }
336
337                 if( Req->argc == 0 ) Client_Destroy( target, "Got QUIT command.", NULL, TRUE );
338                 else Client_Destroy( target, "Got QUIT command.", Req->argv[0], TRUE );
339
340                 return CONNECTED;
341         }
342         else
343         {
344                 /* User, Service, oder noch nicht registriert */
345                 
346                 /* Falsche Anzahl Parameter? */
347                 if( Req->argc > 1 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
348
349                 if( Req->argc == 0 ) Conn_Close( Client_Conn( Client ), "Got QUIT command.", NULL, TRUE );
350                 else Conn_Close( Client_Conn( Client ), "Got QUIT command.", Req->argv[0], TRUE );
351                 
352                 return DISCONNECTED;
353         }
354 } /* IRC_QUIT */
355
356
357 GLOBAL BOOLEAN
358 IRC_PING( CLIENT *Client, REQUEST *Req )
359 {
360         CLIENT *target, *from;
361
362         assert( Client != NULL );
363         assert( Req != NULL );
364
365         /* Falsche Anzahl Parameter? */
366         if( Req->argc < 1 ) return IRC_WriteStrClient( Client, ERR_NOORIGIN_MSG, Client_ID( Client ));
367 #ifdef STRICT_RFC
368         if( Req->argc > 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
369 #endif
370
371         if( Req->argc > 1 )
372         {
373                 /* es wurde ein Ziel-Client angegeben */
374                 target = Client_Search( Req->argv[1] );
375                 if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->argv[1] );
376                 if( target != Client_ThisServer( ))
377                 {
378                         /* ok, forwarden */
379                         if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
380                         else from = Client;
381                         if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->prefix );
382                         return IRC_WriteStrClientPrefix( target, from, "PING %s :%s", Client_ID( from ), Req->argv[1] );
383                 }
384         }
385
386         Log( LOG_DEBUG, "Connection %d: got PING, sending PONG ...", Client_Conn( Client ));
387         return IRC_WriteStrClient( Client, "PONG %s :%s", Client_ID( Client_ThisServer( )), Client_ID( Client ));
388 } /* IRC_PING */
389
390
391 GLOBAL BOOLEAN
392 IRC_PONG( CLIENT *Client, REQUEST *Req )
393 {
394         CLIENT *target, *from;
395
396         assert( Client != NULL );
397         assert( Req != NULL );
398
399         /* Falsche Anzahl Parameter? */
400         if( Req->argc < 1 ) return IRC_WriteStrClient( Client, ERR_NOORIGIN_MSG, Client_ID( Client ));
401         if( Req->argc > 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
402
403         /* forwarden? */
404         if( Req->argc == 2 )
405         {
406                 target = Client_Search( Req->argv[1] );
407                 if( ! target ) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->argv[1] );
408                 if( target != Client_ThisServer( ))
409                 {
410                         /* ok, forwarden */
411                         if( Client_Type( Client ) == CLIENT_SERVER ) from = Client_Search( Req->prefix );
412                         else from = Client;
413                         if( ! from ) return IRC_WriteStrClient( Client, ERR_NOSUCHSERVER_MSG, Client_ID( Client ), Req->prefix );
414                         return IRC_WriteStrClientPrefix( target, from, "PONG %s :%s", Client_ID( from ), Req->argv[1] );
415                 }
416         }
417
418         /* Der Connection-Timestamp wurde schon beim Lesen aus dem Socket
419          * aktualisiert, daher muss das hier nicht mehr gemacht werden. */
420
421         if( Client_Conn( Client ) > NONE ) Log( LOG_DEBUG, "Connection %d: received PONG. Lag: %ld seconds.", Client_Conn( Client ), time( NULL ) - Conn_LastPing( Client_Conn( Client )));
422         else Log( LOG_DEBUG, "Connection %d: received PONG.", Client_Conn( Client ));
423
424         return CONNECTED;
425 } /* IRC_PONG */
426
427
428 LOCAL BOOLEAN
429 Hello_User( CLIENT *Client )
430 {
431         assert( Client != NULL );
432
433         /* Passwort ueberpruefen */
434         if( strcmp( Client_Password( Client ), Conf_ServerPwd ) != 0 )
435         {
436                 /* Falsches Passwort */
437                 Log( LOG_ERR, "User \"%s\" rejected (connection %d): Bad password!", Client_Mask( Client ), Client_Conn( Client ));
438                 Conn_Close( Client_Conn( Client ), NULL, "Bad password", TRUE );
439                 return DISCONNECTED;
440         }
441
442         Log( LOG_NOTICE, "User \"%s\" registered (connection %d).", Client_Mask( Client ), Client_Conn( Client ));
443
444         /* Andere Server informieren */
445         IRC_WriteStrServers( NULL, "NICK %s 1 %s %s 1 +%s :%s", Client_ID( Client ), Client_User( Client ), Client_Hostname( Client ), Client_Modes( Client ), Client_Info( Client ));
446
447         if( ! IRC_WriteStrClient( Client, RPL_WELCOME_MSG, Client_ID( Client ), Client_Mask( Client ))) return FALSE;
448         if( ! IRC_WriteStrClient( Client, RPL_YOURHOST_MSG, Client_ID( Client ), Client_ID( Client_ThisServer( )), VERSION, TARGET_CPU, TARGET_VENDOR, TARGET_OS )) return FALSE;
449         if( ! IRC_WriteStrClient( Client, RPL_CREATED_MSG, Client_ID( Client ), NGIRCd_StartStr )) return FALSE;
450         if( ! IRC_WriteStrClient( Client, RPL_MYINFO_MSG, Client_ID( Client ), Client_ID( Client_ThisServer( )), VERSION, USERMODES, CHANMODES )) return FALSE;
451
452         Client_SetType( Client, CLIENT_USER );
453
454         if( ! IRC_Send_LUSERS( Client )) return DISCONNECTED;
455         if( ! IRC_Show_MOTD( Client )) return DISCONNECTED;
456
457         return CONNECTED;
458 } /* Hello_User */
459
460
461 LOCAL VOID
462 Kill_Nick( CHAR *Nick, CHAR *Reason )
463 {
464         CLIENT *c;
465
466         assert( Nick != NULL );
467         assert( Reason != NULL );
468
469         Log( LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s", Nick, Reason );
470
471         /* andere Server benachrichtigen */
472         IRC_WriteStrServers( NULL, "KILL %s :%s", Nick, Reason );
473
474         /* Ggf. einen eigenen Client toeten */
475         c = Client_Search( Nick );
476         if( c && ( Client_Conn( c ) != NONE )) Conn_Close( Client_Conn( c ), NULL, Reason, TRUE );
477 } /* Kill_Nick */
478
479
480 /* -eof- */