]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/client.c
e36dafd5678c30c268189774ff81ddb060ce5d0b
[ngircd-alex.git] / src / ngircd / client.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: client.c,v 1.44 2002/03/06 14:30:43 alex Exp $
13  *
14  * client.c: Management aller Clients
15  *
16  * Der Begriff "Client" ist in diesem Fall evtl. etwas verwirrend: Clients sind
17  * alle Verbindungen, die im gesamten(!) IRC-Netzwerk bekannt sind. Das sind IRC-
18  * Clients (User), andere Server und IRC-Services.
19  * Ueber welchen IRC-Server die Verbindung nun tatsaechlich in das Netzwerk her-
20  * gestellt wurde, muss der jeweiligen Struktur entnommen werden. Ist es dieser
21  * Server gewesen, so existiert eine entsprechende CONNECTION-Struktur.
22  *
23  * $Log: client.c,v $
24  * Revision 1.44  2002/03/06 14:30:43  alex
25  * - ein paar assert()-Tests ergaenzt.
26  *
27  * Revision 1.43  2002/03/04 01:04:46  alex
28  * - neuen Clients mit Mode "a" wird nun auch der Default-Away-Text gesetzt.
29  *
30  * Revision 1.42  2002/03/03 17:17:01  alex
31  * - strncpy() und vsnprintf() kopieren nun etwas "optimierter" (1 Byte weniger) :-)
32  *
33  * Revision 1.41  2002/03/02 01:35:50  alex
34  * - Channel- und Nicknames werden nun ordentlich validiert.
35  *
36  * Revision 1.40  2002/02/27 23:23:53  alex
37  * - Includes fuer einige Header bereinigt.
38  *
39  * Revision 1.39  2002/02/27 18:22:09  alex
40  * - neue Funktion Client_SetAway() und Client_Away() implementiert.
41  *
42  * Revision 1.38  2002/02/27 14:47:53  alex
43  * - Logging beim Abmelden von Clients (erneut) geaendert: nun ist's aber gut ;-)
44  *
45  * Revision 1.37  2002/02/17 19:02:49  alex
46  * - Client_CheckNick() und Client_CheckID() lieferten u.U. falsche Ergebnisse.
47  *
48  * Revision 1.36  2002/02/06 16:49:41  alex
49  * - neue Funktion Client_IsValidNick(), Nicknames werden besser validiert.
50  *
51  * Revision 1.35  2002/01/29 00:14:49  alex
52  * - neue Funktion Client_TopServer(), Client_NewXXX() angepasst.
53  *
54  * Revision 1.34  2002/01/27 22:07:36  alex
55  * - Client_GetFromID() besser dokumentiert, kleinere Aenderungen.
56  *
57  * Revision 1.33  2002/01/27 21:56:54  alex
58  * - weitere Anpassungen an Chennals, v.a. ueber Server-Links.
59  *
60  * Revision 1.32  2002/01/27 18:27:12  alex
61  * - Client_GetFromID() kommt nun auch mit Host-Masken zurecht.
62  *
63  * Revision 1.31  2002/01/21 00:08:50  alex
64  * - wird ein Client entfernt, so wird er auch aus allen Channels geloescht.
65  *
66  * Revision 1.30  2002/01/18 15:32:01  alex
67  * - bei Client_SetModes() wurde das NULL-Byte falsch gesetzt. Opsa.
68  *
69  * Revision 1.29  2002/01/16 22:10:35  alex
70  * - neue Funktionen Client_xxxCount().
71  *
72  * Revision 1.28  2002/01/11 23:50:40  alex
73  * - Hop-Count fuer den Server selber (0) wird korrekt initialisiert.
74  *
75  * Revision 1.27  2002/01/09 01:08:08  alex
76  * - wird ein Server abgemeldet, so wird anderen Server ein SQUIT geschickt.
77  *
78  * Revision 1.26  2002/01/07 23:42:12  alex
79  * - Es werden fuer alle Server eigene Token generiert,
80  * - QUIT von einem Server fuer einen User wird an andere Server geforwarded,
81  * - ebenso NICK-Befehle, die "fremde" User einfuehren.
82  *
83  * Revision 1.25  2002/01/07 15:31:00  alex
84  * - Bei Log-Meldungen ueber Clients wird nun immer die "Client Mask" verwendet.
85  *
86  * Revision 1.24  2002/01/06 15:18:14  alex
87  * - Loglevel und Meldungen nochmals geaendert. Level passen nun besser.
88  *
89  * Revision 1.23  2002/01/05 23:26:05  alex
90  * - Vorbereitungen fuer Ident-Abfragen in Client-Strukturen.
91  *
92  * Revision 1.22  2002/01/05 20:08:17  alex
93  * - neue Funktion Client_NextHop().
94  *
95  * Revision 1.21  2002/01/05 19:15:03  alex
96  * - Fehlerpruefung bei select() in der "Hauptschleife" korrigiert.
97  *
98  * Revision 1.20  2002/01/04 17:57:08  alex
99  * - Client_Destroy() an Server-Links angepasst.
100  *
101  * Revision 1.19  2002/01/04 01:21:22  alex
102  * - Client-Strukturen koennen von anderen Modulen nun nur noch ueber die
103  *   enstprechenden (zum Teil neuen) Funktionen angesprochen werden.
104  *
105  * Revision 1.18  2002/01/03 02:28:06  alex
106  * - neue Funktion Client_CheckID(), diverse Aenderungen fuer Server-Links.
107  *
108  * Revision 1.17  2002/01/02 02:42:58  alex
109  * - Copyright-Texte aktualisiert.
110  *
111  * Revision 1.16  2002/01/01 18:25:44  alex
112  * - #include's fuer stdlib.h ergaenzt.
113  *
114  * Revision 1.15  2001/12/31 15:33:13  alex
115  * - neuer Befehl NAMES, kleinere Bugfixes.
116  * - Bug bei PING behoben: war zu restriktiv implementiert :-)
117  *
118  * Revision 1.14  2001/12/31 02:18:51  alex
119  * - viele neue Befehle (WHOIS, ISON, OPER, DIE, RESTART),
120  * - neuen Header "defines.h" mit (fast) allen Konstanten.
121  * - Code Cleanups und viele "kleine" Aenderungen & Bugfixes.
122  *
123  * Revision 1.13  2001/12/30 19:26:11  alex
124  * - Unterstuetzung fuer die Konfigurationsdatei eingebaut.
125  *
126  * Revision 1.12  2001/12/29 20:18:18  alex
127  * - neue Funktion Client_SetHostname().
128  *
129  * Revision 1.11  2001/12/29 03:10:47  alex
130  * - Client-Modes implementiert; Loglevel mal wieder angepasst.
131  *
132  * Revision 1.10  2001/12/27 19:13:47  alex
133  * - neue Funktion Client_Search(), besseres Logging.
134  *
135  * Revision 1.9  2001/12/27 17:15:29  alex
136  * - der eigene Hostname wird nun komplet (als FQDN) ermittelt.
137  *
138  * Revision 1.8  2001/12/27 16:54:51  alex
139  * - neue Funktion Client_GetID(), liefert die "Client ID".
140  *
141  * Revision 1.7  2001/12/26 14:45:37  alex
142  * - "Code Cleanups".
143  *
144  * Revision 1.6  2001/12/26 03:19:16  alex
145  * - neue Funktion Client_Nick().
146  *
147  * Revision 1.5  2001/12/25 22:04:26  alex
148  * - Aenderungen an den Debug- und Logging-Funktionen.
149  *
150  * Revision 1.4  2001/12/25 19:21:26  alex
151  * - Client-Typ ("Status") besser unterteilt, My_Clients ist zudem nun global.
152  *
153  * Revision 1.3  2001/12/24 01:31:14  alex
154  * - einige assert()'s eingestraeut.
155  *
156  * Revision 1.2  2001/12/23 22:04:37  alex
157  * - einige neue Funktionen,
158  * - CLIENT-Struktur erweitert.
159  *
160  * Revision 1.1  2001/12/14 08:13:43  alex
161  * - neues Modul begonnen :-)
162  */
163
164
165 #define __client_c__
166
167
168 #include <portab.h>
169 #include "global.h"
170
171 #include <imp.h>
172 #include <assert.h>
173 #include <unistd.h>
174 #include <stdio.h>
175 #include <stdlib.h>
176 #include <string.h>
177 #include <netdb.h>
178
179 #include <exp.h>
180 #include "client.h"
181
182 #include <imp.h>
183 #include "ngircd.h"
184 #include "channel.h"
185 #include "conf.h"
186 #include "conn.h"
187 #include "irc-write.h"
188 #include "log.h"
189 #include "messages.h"
190
191 #include <exp.h>
192
193
194 LOCAL CLIENT *This_Server, *My_Clients;
195 LOCAL CHAR GetID_Buffer[CLIENT_ID_LEN];
196
197
198 LOCAL INT Count( CLIENT_TYPE Type );
199 LOCAL INT MyCount( CLIENT_TYPE Type );
200
201 LOCAL CLIENT *New_Client_Struct( VOID );
202 LOCAL VOID Generate_MyToken( CLIENT *Client );
203
204
205 GLOBAL VOID Client_Init( VOID )
206 {
207         struct hostent *h;
208         
209         This_Server = New_Client_Struct( );
210         if( ! This_Server )
211         {
212                 Log( LOG_EMERG, "Can't allocate client structure for server! Going down." );
213                 Log( LOG_ALERT, PACKAGE" exiting due to fatal errors!" );
214                 exit( 1 );
215         }
216
217         /* Client-Struktur dieses Servers */
218         This_Server->next = NULL;
219         This_Server->type = CLIENT_SERVER;
220         This_Server->conn_id = NONE;
221         This_Server->introducer = This_Server;
222         This_Server->mytoken = 1;
223         This_Server->hops = 0;
224
225         gethostname( This_Server->host, CLIENT_HOST_LEN );
226         h = gethostbyname( This_Server->host );
227         if( h ) strcpy( This_Server->host, h->h_name );
228
229         strcpy( This_Server->id, Conf_ServerName );
230         strcpy( This_Server->info, Conf_ServerInfo );
231
232         My_Clients = This_Server;
233 } /* Client_Init */
234
235
236 GLOBAL VOID Client_Exit( VOID )
237 {
238         CLIENT *c, *next;
239         INT cnt;
240
241         Client_Destroy( This_Server, "Server going down.", NULL );
242         
243         cnt = 0;
244         c = My_Clients;
245         while( c )
246         {
247                 cnt++;
248                 next = c->next;
249                 free( c );
250                 c = next;
251         }
252         if( cnt ) Log( LOG_INFO, "Freed %d client structure%s.", cnt, cnt == 1 ? "" : "s" );
253 } /* Client_Exit */
254
255
256 GLOBAL CLIENT *Client_ThisServer( VOID )
257 {
258         return This_Server;
259 } /* Client_ThisServer */
260
261
262 GLOBAL CLIENT *Client_NewLocal( CONN_ID Idx, CHAR *Hostname, INT Type, BOOLEAN Idented )
263 {
264         /* Neuen lokalen Client erzeugen: Wrapper-Funktion fuer Client_New(). */
265         return Client_New( Idx, This_Server, NULL, Type, NULL, NULL, Hostname, NULL, 0, 0, NULL, Idented );
266 } /* Client_NewLocal */
267
268
269 GLOBAL CLIENT *Client_NewRemoteServer( CLIENT *Introducer, CHAR *Hostname, CLIENT *TopServer, INT Hops, INT Token, CHAR *Info, BOOLEAN Idented )
270 {
271         /* Neuen Remote-Client erzeugen: Wrapper-Funktion fuer Client_New (). */
272         return Client_New( NONE, Introducer, TopServer, CLIENT_SERVER, Hostname, NULL, Hostname, Info, Hops, Token, NULL, Idented );
273 } /* Client_NewRemoteServer */
274
275
276 GLOBAL CLIENT *Client_NewRemoteUser( CLIENT *Introducer, CHAR *Nick, INT Hops, CHAR *User, CHAR *Hostname, INT Token, CHAR *Modes, CHAR *Info, BOOLEAN Idented )
277 {
278         /* Neuen Remote-Client erzeugen: Wrapper-Funktion fuer Client_New (). */
279         return Client_New( NONE, Introducer, NULL, CLIENT_USER, Nick, User, Hostname, Info, Hops, Token, Modes, Idented );
280 } /* Client_NewRemoteUser */
281
282
283 GLOBAL CLIENT *Client_New( CONN_ID Idx, CLIENT *Introducer, CLIENT *TopServer, INT Type, CHAR *ID, CHAR *User, CHAR *Hostname, CHAR *Info, INT Hops, INT Token, CHAR *Modes, BOOLEAN Idented )
284 {
285         CLIENT *client;
286
287         assert( Idx >= NONE );
288         assert( Introducer != NULL );
289         assert( Hostname != NULL );
290
291         client = New_Client_Struct( );
292         if( ! client ) return NULL;
293
294         /* Initialisieren */
295         client->conn_id = Idx;
296         client->introducer = Introducer;
297         client->topserver = TopServer;
298         client->type = Type;
299         if( ID ) Client_SetID( client, ID );
300         if( User ) Client_SetUser( client, User, Idented );
301         if( Hostname ) Client_SetHostname( client, Hostname );
302         if( Info ) Client_SetInfo( client, Info );
303         client->hops = Hops;
304         client->token = Token;
305         if( Modes ) Client_SetModes( client, Modes );
306         if( Type == CLIENT_SERVER ) Generate_MyToken( client );
307
308         /* ist der User away? */
309         if( strchr( client->modes, 'a' )) strcpy( client->away, DEFAULT_AWAY_MSG );
310
311         /* Verketten */
312         client->next = My_Clients;
313         My_Clients = client;
314
315         return client;
316 } /* Client_New */
317
318
319 GLOBAL VOID Client_Destroy( CLIENT *Client, CHAR *LogMsg, CHAR *FwdMsg )
320 {
321         /* Client entfernen. */
322         
323         CLIENT *last, *c;
324         CHAR *txt;
325
326         assert( Client != NULL );
327
328         if( LogMsg ) txt = LogMsg;
329         else txt = FwdMsg;
330         if( ! txt ) txt = "Reason unknown.";
331
332         last = NULL;
333         c = My_Clients;
334         while( c )
335         {
336                 if(( Client->type == CLIENT_SERVER ) && ( c->introducer == Client ) && ( c != Client ))
337                 {
338                         Client_Destroy( c, LogMsg, FwdMsg );
339                         last = NULL;
340                         c = My_Clients;
341                         continue;
342                 }
343                 if( c == Client )
344                 {
345                         if( last ) last->next = c->next;
346                         else My_Clients = c->next;
347
348                         if( c->type == CLIENT_USER )
349                         {
350                                 if( c->conn_id != NONE )
351                                 {
352                                         /* Ein lokaler User. Alle andere Server informieren! */
353                                         Log( LOG_NOTICE, "User \"%s\" unregistered (connection %d): %s", Client_Mask( c ), c->conn_id, txt );
354
355                                         if( FwdMsg ) IRC_WriteStrServersPrefix( NULL, c, "QUIT :%s", FwdMsg );
356                                         else IRC_WriteStrServersPrefix( NULL, c, "QUIT :" );
357                                 }
358                                 else
359                                 {
360                                         /* Remote User. Andere Server informieren, ausser denen,
361                                          * die "in Richtung dem liegen", auf dem der User registriert
362                                          * ist. Von denen haben wir das QUIT ja wohl bekommen. */
363                                         Log( LOG_DEBUG, "User \"%s\" unregistered: %s", Client_Mask( c ), txt );
364                                         
365                                         if( FwdMsg ) IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "QUIT :%s", FwdMsg );
366                                         else IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "QUIT :" );
367                                 }
368                                 Channel_RemoveClient( c, FwdMsg ? FwdMsg : c->id );
369                         }
370                         else if( c->type == CLIENT_SERVER )
371                         {
372                                 if( c != This_Server )
373                                 {
374                                         if( c->conn_id != NONE ) Log( LOG_NOTICE, "Server \"%s\" unregistered (connection %d): %s", c->id, c->conn_id, txt );
375                                         else Log( LOG_NOTICE, "Server \"%s\" unregistered: %s", c->id, txt );
376                                 }
377
378                                 /* andere Server informieren */
379                                 if( ! NGIRCd_Quit )
380                                 {
381                                         if( FwdMsg ) IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "SQUIT %s :%s", c->id, FwdMsg );
382                                         else IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "SQUIT %s :", c->id );
383                                 }
384                         }
385                         else
386                         {
387                                 if( c->conn_id != NONE )
388                                 {
389                                         if( c->id[0] ) Log( LOG_NOTICE, "Client \"%s\" unregistered (connection %d): %s", c->id, c->conn_id, txt );
390                                         else Log( LOG_NOTICE, "Client unregistered (connection %d): %s", c->conn_id, txt );
391                                 }
392                                 else
393                                 {
394                                         if( c->id[0] ) Log( LOG_WARNING, "Unregistered unknown client \"%s\": %s", c->id, txt );
395                                         else Log( LOG_WARNING, "Unregistered unknown client: %s", c->id, txt );
396                                 }
397                         }
398
399                         free( c );
400                         break;
401                 }
402                 last = c;
403                 c = c->next;
404         }
405 } /* Client_Destroy */
406
407
408 GLOBAL VOID Client_SetHostname( CLIENT *Client, CHAR *Hostname )
409 {
410         /* Hostname eines Clients setzen */
411         
412         assert( Client != NULL );
413         assert( Hostname != NULL );
414         
415         strncpy( Client->host, Hostname, CLIENT_HOST_LEN - 1 );
416         Client->host[CLIENT_HOST_LEN - 1] = '\0';
417 } /* Client_SetHostname */
418
419
420 GLOBAL VOID Client_SetID( CLIENT *Client, CHAR *ID )
421 {
422         /* Hostname eines Clients setzen */
423
424         assert( Client != NULL );
425         assert( ID != NULL );
426         
427         strncpy( Client->id, ID, CLIENT_ID_LEN - 1 );
428         Client->id[CLIENT_ID_LEN - 1] = '\0';
429 } /* Client_SetID */
430
431
432 GLOBAL VOID Client_SetUser( CLIENT *Client, CHAR *User, BOOLEAN Idented )
433 {
434         /* Username eines Clients setzen */
435
436         assert( Client != NULL );
437         assert( User != NULL );
438         
439         if( Idented ) strncpy( Client->user, User, CLIENT_USER_LEN - 1 );
440         else
441         {
442                 Client->user[0] = '~';
443                 strncpy( Client->user + 1, User, CLIENT_USER_LEN - 2 );
444         }
445         Client->user[CLIENT_USER_LEN - 1] = '\0';
446 } /* Client_SetUser */
447
448
449 GLOBAL VOID Client_SetInfo( CLIENT *Client, CHAR *Info )
450 {
451         /* Hostname eines Clients setzen */
452
453         assert( Client != NULL );
454         assert( Info != NULL );
455         
456         strncpy( Client->info, Info, CLIENT_INFO_LEN - 1 );
457         Client->info[CLIENT_INFO_LEN - 1] = '\0';
458 } /* Client_SetInfo */
459
460
461 GLOBAL VOID Client_SetModes( CLIENT *Client, CHAR *Modes )
462 {
463         /* Hostname eines Clients setzen */
464
465         assert( Client != NULL );
466         assert( Modes != NULL );
467
468         strncpy( Client->modes, Modes, CLIENT_MODE_LEN - 1 );
469         Client->modes[CLIENT_MODE_LEN - 1] = '\0';
470 } /* Client_SetModes */
471
472
473 GLOBAL VOID Client_SetPassword( CLIENT *Client, CHAR *Pwd )
474 {
475         /* Von einem Client geliefertes Passwort */
476
477         assert( Client != NULL );
478         assert( Pwd != NULL );
479         
480         strncpy( Client->pwd, Pwd, CLIENT_PASS_LEN - 1 );
481         Client->pwd[CLIENT_PASS_LEN - 1] = '\0';
482 } /* Client_SetPassword */
483
484
485 GLOBAL VOID Client_SetAway( CLIENT *Client, CHAR *Txt )
486 {
487         /* Von einem Client gelieferte AWAY-Nachricht */
488
489         assert( Client != NULL );
490
491         if( Txt )
492         {
493                 /* Client AWAY setzen */
494                 strncpy( Client->away, Txt, CLIENT_AWAY_LEN - 1 );
495                 Client->away[CLIENT_AWAY_LEN - 1] = '\0';
496                 Client_ModeAdd( Client, 'a' );
497                 Log( LOG_DEBUG, "User \"%s\" is away: %s", Client_Mask( Client ), Txt );
498         }
499         else
500         {
501                 /* AWAY loeschen */
502                 Client_ModeDel( Client, 'a' );
503                 Log( LOG_DEBUG, "User \"%s\" is no longer away.", Client_Mask( Client ));
504         }
505 } /* Client_SetAway */
506
507
508 GLOBAL VOID Client_SetType( CLIENT *Client, INT Type )
509 {
510         assert( Client != NULL );
511         Client->type = Type;
512         if( Type == CLIENT_SERVER ) Generate_MyToken( Client );
513 } /* Client_SetType */
514
515
516 GLOBAL VOID Client_SetHops( CLIENT *Client, INT Hops )
517 {
518         assert( Client != NULL );
519         Client->hops = Hops;
520 } /* Client_SetHops */
521
522
523 GLOBAL VOID Client_SetToken( CLIENT *Client, INT Token )
524 {
525         assert( Client != NULL );
526         Client->token = Token;
527 } /* Client_SetToken */
528
529
530 GLOBAL VOID Client_SetIntroducer( CLIENT *Client, CLIENT *Introducer )
531 {
532         assert( Client != NULL );
533         assert( Introducer != NULL );
534         Client->introducer = Introducer;
535 } /* Client_SetIntroducer */
536
537
538 GLOBAL VOID Client_SetOperByMe( CLIENT *Client, BOOLEAN OperByMe )
539 {
540         assert( Client != NULL );
541         Client->oper_by_me = OperByMe;
542 } /* Client_SetOperByMe */
543
544
545 GLOBAL BOOLEAN Client_ModeAdd( CLIENT *Client, CHAR Mode )
546 {
547         /* Mode soll gesetzt werden. TRUE wird geliefert, wenn der
548          * Mode neu gesetzt wurde, FALSE, wenn der Client den Mode
549          * bereits hatte. */
550
551         CHAR x[2];
552         
553         assert( Client != NULL );
554
555         x[0] = Mode; x[1] = '\0';
556         if( ! strchr( Client->modes, x[0] ))
557         {
558                 /* Client hat den Mode noch nicht -> setzen */
559                 strcat( Client->modes, x );
560                 return TRUE;
561         }
562         else return FALSE;
563 } /* Client_ModeAdd */
564
565
566 GLOBAL BOOLEAN Client_ModeDel( CLIENT *Client, CHAR Mode )
567 {
568         /* Mode soll geloescht werden. TRUE wird geliefert, wenn der
569         * Mode entfernt wurde, FALSE, wenn der Client den Mode
570         * ueberhaupt nicht hatte. */
571
572         CHAR x[2], *p;
573
574         assert( Client != NULL );
575
576         x[0] = Mode; x[1] = '\0';
577
578         p = strchr( Client->modes, x[0] );
579         if( ! p ) return FALSE;
580
581         /* Client hat den Mode -> loeschen */
582         while( *p )
583         {
584                 *p = *(p + 1);
585                 p++;
586         }
587         return TRUE;
588 } /* Client_ModeDel */
589
590
591 GLOBAL CLIENT *Client_GetFromConn( CONN_ID Idx )
592 {
593         /* Client-Struktur, die zur lokalen Verbindung Idx gehoert,
594          * liefern. Wird keine gefunden, so wird NULL geliefert. */
595
596         CLIENT *c;
597
598         assert( Idx >= 0 );
599         
600         c = My_Clients;
601         while( c )
602         {
603                 if( c->conn_id == Idx ) return c;
604                 c = c->next;
605         }
606         return NULL;
607 } /* Client_GetFromConn */
608
609
610 GLOBAL CLIENT *Client_GetFromID( CHAR *Nick )
611 {
612         /* Client-Struktur, die den entsprechenden Nick hat, liefern.
613          * Wird keine gefunden, so wird NULL geliefert. */
614
615         CHAR n[CLIENT_ID_LEN], *ptr;
616         CLIENT *c = NULL;
617
618         assert( Nick != NULL );
619
620         /* Nick kopieren und ggf. Host-Mask abschneiden */
621         strncpy( n, Nick, CLIENT_ID_LEN - 1 );
622         n[CLIENT_ID_LEN - 1] = '\0';
623         ptr = strchr( n, '!' );
624         if( ptr ) *ptr = '\0';
625
626         c = My_Clients;
627         while( c )
628         {
629                 if( strcasecmp( c->id, n ) == 0 ) return c;
630                 c = c->next;
631         }
632         return NULL;
633 } /* Client_GetFromID */
634
635
636 GLOBAL CLIENT *Client_GetFromToken( CLIENT *Client, INT Token )
637 {
638         /* Client-Struktur, die den entsprechenden Introducer (=Client)
639          * und das gegebene Token hat, liefern. Wird keine gefunden,
640          * so wird NULL geliefert. */
641
642         CLIENT *c;
643
644         assert( Client != NULL );
645         assert( Token > 0 );
646
647         c = My_Clients;
648         while( c )
649         {
650                 if(( c->type == CLIENT_SERVER ) && ( c->introducer == Client ) && ( c->token == Token )) return c;
651                 c = c->next;
652         }
653         return NULL;
654 } /* Client_GetFromToken */
655
656
657 GLOBAL INT Client_Type( CLIENT *Client )
658 {
659         assert( Client != NULL );
660         return Client->type;
661 } /* Client_Type */
662
663
664 GLOBAL CONN_ID Client_Conn( CLIENT *Client )
665 {
666         assert( Client != NULL );
667         return Client->conn_id;
668 } /* Client_Conn */
669
670
671 GLOBAL CHAR *Client_ID( CLIENT *Client )
672 {
673         assert( Client != NULL );
674
675 #ifdef DEBUG
676         if( Client->type == CLIENT_USER ) assert( strlen( Client->id ) < CLIENT_NICK_LEN );
677 #endif
678                                                    
679         if( Client->id[0] ) return Client->id;
680         else return "*";
681 } /* Client_ID */
682
683
684 GLOBAL CHAR *Client_Info( CLIENT *Client )
685 {
686         assert( Client != NULL );
687         return Client->info;
688 } /* Client_Info */
689
690
691 GLOBAL CHAR *Client_User( CLIENT *Client )
692 {
693         assert( Client != NULL );
694         if( Client->user ) return Client->user;
695         else return "~";
696 } /* Client_User */
697
698
699 GLOBAL CHAR *Client_Hostname( CLIENT *Client )
700 {
701         assert( Client != NULL );
702         return Client->host;
703 } /* Client_Hostname */
704
705
706 GLOBAL CHAR *Client_Password( CLIENT *Client )
707 {
708         assert( Client != NULL );
709         return Client->pwd;
710 } /* Client_Password */
711
712
713 GLOBAL CHAR *Client_Modes( CLIENT *Client )
714 {
715         assert( Client != NULL );
716         return Client->modes;
717 } /* Client_Modes */
718
719
720 GLOBAL BOOLEAN Client_OperByMe( CLIENT *Client )
721 {
722         assert( Client != NULL );
723         return Client->oper_by_me;
724 } /* Client_OperByMe */
725
726
727 GLOBAL INT Client_Hops( CLIENT *Client )
728 {
729         assert( Client != NULL );
730         return Client->hops;
731 } /* Client_Hops */
732
733
734 GLOBAL INT Client_Token( CLIENT *Client )
735 {
736         assert( Client != NULL );
737         return Client->token;
738 } /* Client_Token */
739
740
741 GLOBAL INT Client_MyToken( CLIENT *Client )
742 {
743         assert( Client != NULL );
744         return Client->mytoken;
745 } /* Client_MyToken */
746
747
748 GLOBAL CLIENT *Client_NextHop( CLIENT *Client )
749 {
750         CLIENT *c;
751         
752         assert( Client != NULL );
753
754         c = Client;
755         while( c->introducer && ( c->introducer != c ) && ( c->introducer != This_Server )) c = c->introducer;
756         return c;
757 } /* Client_NextHop */
758
759
760 GLOBAL CHAR *Client_Mask( CLIENT *Client )
761 {
762         /* Client-"ID" liefern, wie sie z.B. fuer
763          * Prefixe benoetigt wird. */
764
765         assert( Client != NULL );
766         
767         if( Client->type == CLIENT_SERVER ) return Client->id;
768
769         sprintf( GetID_Buffer, "%s!%s@%s", Client->id, Client->user, Client->host );
770         return GetID_Buffer;
771 } /* Client_Mask */
772
773
774 GLOBAL CLIENT *Client_Introducer( CLIENT *Client )
775 {
776         assert( Client != NULL );
777         return Client->introducer;
778 } /* Client_Introducer */
779
780
781 GLOBAL CLIENT *Client_TopServer( CLIENT *Client )
782 {
783         assert( Client != NULL );
784         return Client->topserver;
785 } /* Client_TopServer */
786
787
788 GLOBAL BOOLEAN Client_HasMode( CLIENT *Client, CHAR Mode )
789 {
790         assert( Client != NULL );
791         return strchr( Client->modes, Mode ) != NULL;
792 } /* Client_HasMode */
793
794
795 GLOBAL CHAR *Client_Away( CLIENT *Client )
796 {
797         /* AWAY-Text liefern */
798
799         assert( Client != NULL );
800         return Client->away;
801 } /* Client_Away */
802
803
804 GLOBAL BOOLEAN Client_CheckNick( CLIENT *Client, CHAR *Nick )
805 {
806         /* Nick ueberpruefen */
807
808         CLIENT *c;
809         
810         assert( Client != NULL );
811         assert( Nick != NULL );
812         
813         /* Nick ungueltig? */
814         if( ! Client_IsValidNick( Nick ))
815         {
816                 IRC_WriteStrClient( Client, ERR_ERRONEUSNICKNAME_MSG, Client_ID( Client ), Nick );
817                 return FALSE;
818         }
819
820         /* Nick bereits vergeben? */
821         c = My_Clients;
822         while( c )
823         {
824                 if( strcasecmp( c->id, Nick ) == 0 )
825                 {
826                         /* den Nick gibt es bereits */
827                         IRC_WriteStrClient( Client, ERR_NICKNAMEINUSE_MSG, Client_ID( Client ), Nick );
828                         return FALSE;
829                 }
830                 c = c->next;
831         }
832
833         return TRUE;
834 } /* Client_CheckNick */
835
836
837 GLOBAL BOOLEAN Client_CheckID( CLIENT *Client, CHAR *ID )
838 {
839         /* Nick ueberpruefen */
840
841         CHAR str[COMMAND_LEN];
842         CLIENT *c;
843
844         assert( Client != NULL );
845         assert( Client->conn_id > NONE );
846         assert( ID != NULL );
847
848         /* Nick zu lang? */
849         if( strlen( ID ) > CLIENT_ID_LEN )
850         {
851                 IRC_WriteStrClient( Client, ERR_ERRONEUSNICKNAME_MSG, Client_ID( Client ), ID );
852                 return FALSE;
853         }
854
855         /* ID bereits vergeben? */
856         c = My_Clients;
857         while( c )
858         {
859                 if( strcasecmp( c->id, ID ) == 0 )
860                 {
861                         /* die Server-ID gibt es bereits */
862                         sprintf( str, "ID \"%s\" already registered!", ID );
863                         Log( LOG_ERR, "%s (on connection %d)", str, Client->conn_id );
864                         Conn_Close( Client->conn_id, str, str, TRUE );
865                         return FALSE;
866                 }
867                 c = c->next;
868         }
869
870         return TRUE;
871 } /* Client_CheckID */
872
873
874 GLOBAL CLIENT *Client_Search( CHAR *ID )
875 {
876         /* Client suchen, auf den ID passt */
877
878         CLIENT *c;
879
880         assert( ID != NULL );
881
882         c = My_Clients;
883         while( c )
884         {
885                 if( strcasecmp( c->id, ID ) == 0 ) return c;
886                 c = c->next;
887         }
888         
889         return NULL;
890 } /* Client_Search */
891
892
893 GLOBAL CLIENT *Client_First( VOID )
894 {
895         /* Ersten Client liefern. */
896
897         return My_Clients;
898 } /* Client_First */
899
900
901 GLOBAL CLIENT *Client_Next( CLIENT *c )
902 {
903         /* Naechsten Client liefern. Existiert keiner,
904          * so wird NULL geliefert. */
905
906         assert( c != NULL );
907         return c->next;
908 } /* Client_Next */
909
910
911 GLOBAL INT Client_UserCount( VOID )
912 {
913         return Count( CLIENT_USER );
914 } /* Client_UserCount */
915
916
917 GLOBAL INT Client_ServiceCount( VOID )
918 {
919         return Count( CLIENT_SERVICE );;
920 } /* Client_ServiceCount */
921
922
923 GLOBAL INT Client_ServerCount( VOID )
924 {
925         return Count( CLIENT_SERVER );
926 } /* Client_ServerCount */
927
928
929 GLOBAL INT Client_MyUserCount( VOID )
930 {
931         return MyCount( CLIENT_USER );
932 } /* Client_MyUserCount */
933
934
935 GLOBAL INT Client_MyServiceCount( VOID )
936 {
937         return MyCount( CLIENT_SERVICE );
938 } /* Client_MyServiceCount */
939
940
941 GLOBAL INT Client_MyServerCount( VOID )
942 {
943         return MyCount( CLIENT_SERVER );
944 } /* Client_MyServerCount */
945
946
947 GLOBAL INT Client_OperCount( VOID )
948 {
949         CLIENT *c;
950         INT cnt;
951
952         cnt = 0;
953         c = My_Clients;
954         while( c )
955         {
956                 if( c && ( c->type == CLIENT_USER ) && ( strchr( c->modes, 'o' ))) cnt++;
957                 c = c->next;
958         }
959         return cnt;
960 } /* Client_OperCount */
961
962
963 GLOBAL INT Client_UnknownCount( VOID )
964 {
965         CLIENT *c;
966         INT cnt;
967
968         cnt = 0;
969         c = My_Clients;
970         while( c )
971         {
972                 if( c && ( c->type != CLIENT_USER ) && ( c->type != CLIENT_SERVICE ) && ( c->type != CLIENT_SERVER )) cnt++;
973                 c = c->next;
974         }
975         return cnt;
976 } /* Client_UnknownCount */
977
978
979 GLOBAL BOOLEAN Client_IsValidNick( CHAR *Nick )
980 {
981         /* Ist der Nick gueltig? */
982
983         CHAR *ptr, goodchars[] = ";0123456789";
984         
985         assert( Nick != NULL );
986
987         if( Nick[0] == '#' ) return FALSE;
988         if( strchr( goodchars, Nick[0] )) return FALSE;
989         if( strlen( Nick ) >= CLIENT_NICK_LEN ) return FALSE;
990
991         ptr = Nick;
992         while( *ptr )
993         {
994                 if(( *ptr < 'A' ) && ( ! strchr( goodchars, *ptr ))) return FALSE;
995                 if(( *ptr > '}' ) && ( ! strchr( goodchars, *ptr ))) return FALSE;
996                 ptr++;
997         }
998         
999         return TRUE;
1000 } /* Client_IsValidNick */
1001
1002
1003 LOCAL INT Count( CLIENT_TYPE Type )
1004 {
1005         CLIENT *c;
1006         INT cnt;
1007
1008         cnt = 0;
1009         c = My_Clients;
1010         while( c )
1011         {
1012                 if( c && ( c->type == Type )) cnt++;
1013                 c = c->next;
1014         }
1015         return cnt;
1016 } /* Count */
1017
1018
1019 LOCAL INT MyCount( CLIENT_TYPE Type )
1020 {
1021         CLIENT *c;
1022         INT cnt;
1023
1024         cnt = 0;
1025         c = My_Clients;
1026         while( c )
1027         {
1028                 if( c && ( c->introducer == This_Server ) && ( c->type == Type )) cnt++;
1029                 c = c->next;
1030         }
1031         return cnt;
1032 } /* MyCount */
1033
1034
1035 LOCAL CLIENT *New_Client_Struct( VOID )
1036 {
1037         /* Neue CLIENT-Struktur pre-initialisieren */
1038         
1039         CLIENT *c;
1040         
1041         c = malloc( sizeof( CLIENT ));
1042         if( ! c )
1043         {
1044                 Log( LOG_EMERG, "Can't allocate memory!" );
1045                 return NULL;
1046         }
1047
1048         c->next = NULL;
1049         c->type = CLIENT_UNKNOWN;
1050         c->conn_id = NONE;
1051         c->introducer = NULL;
1052         c->topserver = NULL;
1053         strcpy( c->id, "" );
1054         strcpy( c->pwd, "" );
1055         strcpy( c->host, "" );
1056         strcpy( c->user, "" );
1057         strcpy( c->info, "" );
1058         strcpy( c->modes, "" );
1059         c->oper_by_me = FALSE;
1060         c->hops = -1;
1061         c->token = -1;
1062         c->mytoken = -1;
1063         strcpy( c->away, "" );
1064
1065         return c;
1066 } /* New_Client */
1067
1068
1069 LOCAL VOID Generate_MyToken( CLIENT *Client )
1070 {
1071         CLIENT *c;
1072         INT token;
1073
1074         c = My_Clients;
1075         token = 2;
1076         while( c )
1077         {
1078                 if( c->mytoken == token )
1079                 {
1080                         /* Das Token wurde bereits vergeben */
1081                         token++;
1082                         c = My_Clients;
1083                         continue;
1084                 }
1085                 else c = c->next;
1086         }
1087         Client->mytoken = token;
1088         Log( LOG_DEBUG, "Assigned token %d to server \"%s\".", token, Client->id );
1089 } /* Generate_MyToken */
1090
1091
1092 /* -eof- */