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