Annotation of ircnowd/src/ngircd/client.c, Revision 1.1.1.1
1.1 tomglok 1: /*
2: * ngIRCd -- The Next Generation IRC Daemon
3: * Copyright (c)2001-2014 Alexander Barton (alex@barton.de) and Contributors.
4: *
5: * This program is free software; you can redistribute it and/or modify
6: * it under the terms of the GNU General Public License as published by
7: * the Free Software Foundation; either version 2 of the License, or
8: * (at your option) any later version.
9: * Please read the file COPYING, README and AUTHORS for more information.
10: */
11:
12: #define __client_c__
13:
14: #include "portab.h"
15:
16: /**
17: * @file
18: * Client management.
19: */
20:
21: #include <assert.h>
22: #include <unistd.h>
23: #include <stdio.h>
24: #include <stdlib.h>
25: #include <string.h>
26: #include <strings.h>
27: #include <time.h>
28: #include <netdb.h>
29:
30: #include "conn.h"
31: #include "ngircd.h"
32: #include "channel.h"
33: #include "conf.h"
34: #include "conn-func.h"
35: #include "hash.h"
36: #include "irc-write.h"
37: #include "log.h"
38: #include "match.h"
39: #include "messages.h"
40:
41: #define GETID_LEN (CLIENT_NICK_LEN-1) + 1 + (CLIENT_USER_LEN-1) + 1 + (CLIENT_HOST_LEN-1) + 1
42:
43: static CLIENT *This_Server, *My_Clients;
44:
45: static WHOWAS My_Whowas[MAX_WHOWAS];
46: static int Last_Whowas = -1;
47: static long Max_Users, My_Max_Users;
48:
49:
50: static unsigned long Count PARAMS(( CLIENT_TYPE Type ));
51: static unsigned long MyCount PARAMS(( CLIENT_TYPE Type ));
52:
53: static CLIENT *New_Client_Struct PARAMS(( void ));
54: static void Generate_MyToken PARAMS(( CLIENT *Client ));
55: static void Adjust_Counters PARAMS(( CLIENT *Client ));
56:
57: static void Free_Client PARAMS(( CLIENT **Client ));
58:
59: static CLIENT *Init_New_Client PARAMS((CONN_ID Idx, CLIENT *Introducer,
60: CLIENT *TopServer, int Type, const char *ID,
61: const char *User, const char *Hostname, const char *Info,
62: int Hops, int Token, const char *Modes,
63: bool Idented));
64:
65: static void Destroy_UserOrService PARAMS((CLIENT *Client,const char *Txt, const char *FwdMsg,
66: bool SendQuit));
67:
68: static void cb_introduceClient PARAMS((CLIENT *Client, CLIENT *Prefix,
69: void *i));
70:
71: GLOBAL void
72: Client_Init( void )
73: {
74: struct hostent *h;
75:
76: This_Server = New_Client_Struct( );
77: if( ! This_Server )
78: {
79: Log( LOG_EMERG, "Can't allocate client structure for server! Going down." );
80: Log( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
81: exit( 1 );
82: }
83:
84: /* Client structure for this server */
85: This_Server->next = NULL;
86: This_Server->type = CLIENT_SERVER;
87: This_Server->conn_id = NONE;
88: This_Server->introducer = This_Server;
89: This_Server->mytoken = 1;
90: This_Server->hops = 0;
91:
92: gethostname( This_Server->host, CLIENT_HOST_LEN );
93: if (Conf_DNS) {
94: h = gethostbyname( This_Server->host );
95: if (h) strlcpy(This_Server->host, h->h_name, sizeof(This_Server->host));
96: }
97: Client_SetID( This_Server, Conf_ServerName );
98: Client_SetInfo( This_Server, Conf_ServerInfo );
99:
100: My_Clients = This_Server;
101:
102: memset( &My_Whowas, 0, sizeof( My_Whowas ));
103: } /* Client_Init */
104:
105:
106: GLOBAL void
107: Client_Exit( void )
108: {
109: CLIENT *c, *next;
110: int cnt;
111:
112: if( NGIRCd_SignalRestart ) Client_Destroy( This_Server, "Server going down (restarting).", NULL, false );
113: else Client_Destroy( This_Server, "Server going down.", NULL, false );
114:
115: cnt = 0;
116: c = My_Clients;
117: while(c) {
118: cnt++;
119: next = (CLIENT *)c->next;
120: Free_Client(&c);
121: c = next;
122: }
123: if (cnt)
124: Log(LOG_INFO, "Freed %d client structure%s.",
125: cnt, cnt == 1 ? "" : "s");
126: } /* Client_Exit */
127:
128:
129: GLOBAL CLIENT *
130: Client_ThisServer( void )
131: {
132: return This_Server;
133: } /* Client_ThisServer */
134:
135:
136: /**
137: * Initialize new local client; wrapper function for Init_New_Client().
138: * @return New CLIENT structure.
139: */
140: GLOBAL CLIENT *
141: Client_NewLocal(CONN_ID Idx, const char *Hostname, int Type, bool Idented)
142: {
143: return Init_New_Client(Idx, This_Server, NULL, Type, NULL, NULL,
144: Hostname, NULL, 0, 0, NULL, Idented);
145: } /* Client_NewLocal */
146:
147:
148: /**
149: * Initialize new remote server; wrapper function for Init_New_Client().
150: * @return New CLIENT structure.
151: */
152: GLOBAL CLIENT *
153: Client_NewRemoteServer(CLIENT *Introducer, const char *Hostname, CLIENT *TopServer,
154: int Hops, int Token, const char *Info, bool Idented)
155: {
156: return Init_New_Client(NONE, Introducer, TopServer, CLIENT_SERVER,
157: Hostname, NULL, Hostname, Info, Hops, Token, NULL, Idented);
158: } /* Client_NewRemoteServer */
159:
160:
161: /**
162: * Initialize new remote client; wrapper function for Init_New_Client().
163: * @return New CLIENT structure.
164: */
165: GLOBAL CLIENT *
166: Client_NewRemoteUser(CLIENT *Introducer, const char *Nick, int Hops, const char *User,
167: const char *Hostname, int Token, const char *Modes, const char *Info, bool Idented)
168: {
169: return Init_New_Client(NONE, Introducer, NULL, CLIENT_USER, Nick,
170: User, Hostname, Info, Hops, Token, Modes, Idented);
171: } /* Client_NewRemoteUser */
172:
173:
174: /**
175: * Initialize new client and set up the given parameters like client type,
176: * user name, host name, introducing server etc. ...
177: * @return New CLIENT structure.
178: */
179: static CLIENT *
180: Init_New_Client(CONN_ID Idx, CLIENT *Introducer, CLIENT *TopServer,
181: int Type, const char *ID, const char *User, const char *Hostname,
182: const char *Info, int Hops, int Token, const char *Modes, bool Idented)
183: {
184: CLIENT *client;
185:
186: assert(Idx >= NONE);
187: assert(Introducer != NULL);
188:
189: client = New_Client_Struct();
190: if (!client)
191: return NULL;
192:
193: client->starttime = time(NULL);
194: client->conn_id = Idx;
195: client->introducer = Introducer;
196: client->topserver = TopServer;
197: client->type = Type;
198: if (ID)
199: Client_SetID(client, ID);
200: if (User) {
201: Client_SetUser(client, User, Idented);
202: Client_SetOrigUser(client, User);
203: }
204: if (Hostname)
205: Client_SetHostname(client, Hostname);
206: if (Info)
207: Client_SetInfo(client, Info);
208: client->hops = Hops;
209: client->token = Token;
210: if (Modes)
211: Client_SetModes(client, Modes);
212: if (Type == CLIENT_SERVER)
213: Generate_MyToken(client);
214:
215: if (Client_HasMode(client, 'a'))
216: client->away = strndup(DEFAULT_AWAY_MSG, CLIENT_AWAY_LEN - 1);
217:
218: client->next = (POINTER *)My_Clients;
219: My_Clients = client;
220:
221: Adjust_Counters(client);
222:
223: return client;
224: } /* Init_New_Client */
225:
226:
227: GLOBAL void
228: Client_Destroy( CLIENT *Client, const char *LogMsg, const char *FwdMsg, bool SendQuit )
229: {
230: /* remove a client */
231:
232: CLIENT *last, *c;
233: char msg[COMMAND_LEN];
234: const char *txt;
235:
236: assert( Client != NULL );
237:
238: txt = LogMsg ? LogMsg : FwdMsg;
239: if (!txt)
240: txt = "Reason unknown";
241:
242: /* netsplit message */
243: if( Client->type == CLIENT_SERVER ) {
244: strlcpy(msg, This_Server->id, sizeof (msg));
245: strlcat(msg, " ", sizeof (msg));
246: strlcat(msg, Client->id, sizeof (msg));
247: }
248:
249: last = NULL;
250: c = My_Clients;
251: while( c )
252: {
253: if(( Client->type == CLIENT_SERVER ) && ( c->introducer == Client ) && ( c != Client ))
254: {
255: /*
256: * The client that is about to be removed is a server,
257: * the client we are checking right now is a child of that
258: * server and thus has to be removed, too.
259: *
260: * Call Client_Destroy() recursively with the server as the
261: * new "object to be removed". This starts the cycle again, until
262: * all servers that are linked via the original server have been
263: * removed.
264: */
265: Client_Destroy( c, NULL, msg, false );
266: last = NULL;
267: c = My_Clients;
268: continue;
269: }
270: if( c == Client )
271: {
272: /* found the client: remove it */
273: if( last ) last->next = c->next;
274: else My_Clients = (CLIENT *)c->next;
275:
276: if(c->type == CLIENT_USER || c->type == CLIENT_SERVICE)
277: Destroy_UserOrService(c, txt, FwdMsg, SendQuit);
278: else if( c->type == CLIENT_SERVER )
279: {
280: if (c != This_Server) {
281: if (c->conn_id != NONE)
282: Log(LOG_NOTICE|LOG_snotice,
283: "Server \"%s\" unregistered (connection %d): %s.",
284: c->id, c->conn_id, txt);
285: else
286: Log(LOG_NOTICE|LOG_snotice,
287: "Server \"%s\" unregistered: %s.",
288: c->id, txt);
289: }
290:
291: /* inform other servers */
292: if( ! NGIRCd_SignalQuit )
293: {
294: if( FwdMsg ) IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "SQUIT %s :%s", c->id, FwdMsg );
295: else IRC_WriteStrServersPrefix( Client_NextHop( c ), c, "SQUIT %s :", c->id );
296: }
297: }
298: else
299: {
300: if (c->conn_id != NONE) {
301: if (c->id[0])
302: Log(LOG_NOTICE,
303: "Client \"%s\" unregistered (connection %d): %s.",
304: c->id, c->conn_id, txt);
305: else
306: Log(LOG_NOTICE,
307: "Client unregistered (connection %d): %s.",
308: c->conn_id, txt);
309: } else {
310: Log(LOG_WARNING,
311: "Unregistered unknown client \"%s\": %s",
312: c->id[0] ? c->id : "(No Nick)", txt);
313: }
314: }
315:
316: Free_Client(&c);
317: break;
318: }
319: last = c;
320: c = (CLIENT *)c->next;
321: }
322: } /* Client_Destroy */
323:
324:
325: /**
326: * Set client hostname.
327: *
328: * If global hostname cloaking is in effect, don't set the real hostname
329: * but the configured one.
330: *
331: * @param Client The client of which the hostname should be set.
332: * @param Hostname The new hostname.
333: */
334: GLOBAL void
335: Client_SetHostname( CLIENT *Client, const char *Hostname )
336: {
337: assert(Client != NULL);
338: assert(Hostname != NULL);
339:
340: /* Only cloak the hostmask if it has not yet been cloaked.
341: * The period or colon indicates it's still an IP address.
342: * An empty string means a rDNS lookup did not happen (yet). */
343: if (Conf_CloakHost[0] && (!Client->host[0] || strchr(Client->host, '.')
344: || strchr(Client->host, ':'))) {
345: char cloak[GETID_LEN];
346:
347: strlcpy(cloak, Hostname, GETID_LEN);
348: strlcat(cloak, Conf_CloakHostSalt, GETID_LEN);
349: snprintf(cloak, GETID_LEN, Conf_CloakHost, Hash(cloak));
350:
351: LogDebug("Updating hostname of \"%s\": \"%s\" -> \"%s\"",
352: Client_ID(Client), Client->host, cloak);
353: strlcpy(Client->host, cloak, sizeof(Client->host));
354: } else {
355: LogDebug("Updating hostname of \"%s\": \"%s\" -> \"%s\"",
356: Client_ID(Client), Client->host, Hostname);
357: strlcpy(Client->host, Hostname, sizeof(Client->host));
358: }
359: } /* Client_SetHostname */
360:
361:
362: /**
363: * Set IP address to display for a client.
364: *
365: * @param Client The client.
366: * @param IPAText Textual representation of the IP address or NULL to unset.
367: */
368: GLOBAL void
369: Client_SetIPAText(CLIENT *Client, const char *IPAText)
370: {
371: assert(Client != NULL);
372:
373: if (Client->ipa_text)
374: free(Client->ipa_text);
375:
376: if (*IPAText)
377: Client->ipa_text = strndup(IPAText, CLIENT_HOST_LEN - 1);
378: else
379: Client->ipa_text = NULL;
380: }
381:
382:
383: GLOBAL void
384: Client_SetID( CLIENT *Client, const char *ID )
385: {
386: assert( Client != NULL );
387: assert( ID != NULL );
388:
389: strlcpy( Client->id, ID, sizeof( Client->id ));
390:
391: if (Conf_CloakUserToNick) {
392: strlcpy( Client->user, ID, sizeof( Client->user ));
393: strlcpy( Client->info, ID, sizeof( Client->info ));
394: }
395:
396: /* Hash */
397: Client->hash = Hash( Client->id );
398: } /* Client_SetID */
399:
400:
401: GLOBAL void
402: Client_SetUser( CLIENT *Client, const char *User, bool Idented )
403: {
404: /* set clients username */
405:
406: assert( Client != NULL );
407: assert( User != NULL );
408:
409: if (Conf_CloakUserToNick) {
410: strlcpy(Client->user, Client->id, sizeof(Client->user));
411: } else if (Idented) {
412: strlcpy(Client->user, User, sizeof(Client->user));
413: } else {
414: Client->user[0] = '~';
415: strlcpy(Client->user + 1, User, sizeof(Client->user) - 1);
416: }
417: } /* Client_SetUser */
418:
419:
420: /**
421: * Set "original" user name of a client.
422: * This function saves the "original" user name, the user name specified by
423: * the peer using the USER command, into the CLIENT structure. This user
424: * name may be used for authentication, for example.
425: * @param Client The client.
426: * @param User User name to set.
427: */
428: GLOBAL void
429: Client_SetOrigUser(CLIENT UNUSED *Client, const char UNUSED *User)
430: {
431: assert(Client != NULL);
432: assert(User != NULL);
433:
434: #if defined(PAM)
435: strlcpy(Client->orig_user, User, sizeof(Client->orig_user));
436: #endif
437: } /* Client_SetOrigUser */
438:
439:
440: GLOBAL void
441: Client_SetInfo( CLIENT *Client, const char *Info )
442: {
443: /* set client hostname */
444:
445: assert( Client != NULL );
446: assert( Info != NULL );
447:
448: if (Conf_CloakUserToNick)
449: strlcpy(Client->info, Client->id, sizeof(Client->info));
450: else
451: strlcpy(Client->info, Info, sizeof(Client->info));
452: } /* Client_SetInfo */
453:
454:
455: GLOBAL void
456: Client_SetModes( CLIENT *Client, const char *Modes )
457: {
458: assert( Client != NULL );
459: assert( Modes != NULL );
460:
461: strlcpy(Client->modes, Modes, sizeof( Client->modes ));
462: } /* Client_SetModes */
463:
464:
465: GLOBAL void
466: Client_SetFlags( CLIENT *Client, const char *Flags )
467: {
468: assert( Client != NULL );
469: assert( Flags != NULL );
470:
471: strlcpy(Client->flags, Flags, sizeof(Client->flags));
472: } /* Client_SetFlags */
473:
474:
475: GLOBAL void
476: Client_SetAccountName(CLIENT *Client, const char *AccountName)
477: {
478: assert(Client != NULL);
479:
480: if (Client->account_name)
481: free(Client->account_name);
482:
483: if (*AccountName)
484: Client->account_name = strndup(AccountName,
485: CLIENT_NICK_LEN - 1);
486: else
487: Client->account_name = NULL;
488: }
489:
490:
491: GLOBAL void
492: Client_SetAway( CLIENT *Client, const char *Txt )
493: {
494: /* Set AWAY reason of client */
495:
496: assert( Client != NULL );
497: assert( Txt != NULL );
498:
499: if (Client->away)
500: free(Client->away);
501:
502: Client->away = strndup(Txt, CLIENT_AWAY_LEN - 1);
503:
504: LogDebug("%s \"%s\" is away: %s", Client_TypeText(Client),
505: Client_Mask(Client), Txt);
506: } /* Client_SetAway */
507:
508:
509: GLOBAL void
510: Client_SetType( CLIENT *Client, int Type )
511: {
512: assert( Client != NULL );
513: Client->type = Type;
514: if( Type == CLIENT_SERVER ) Generate_MyToken( Client );
515: Adjust_Counters( Client );
516: } /* Client_SetType */
517:
518:
519: GLOBAL void
520: Client_SetHops( CLIENT *Client, int Hops )
521: {
522: assert( Client != NULL );
523: Client->hops = Hops;
524: } /* Client_SetHops */
525:
526:
527: GLOBAL void
528: Client_SetToken( CLIENT *Client, int Token )
529: {
530: assert( Client != NULL );
531: Client->token = Token;
532: } /* Client_SetToken */
533:
534:
535: GLOBAL void
536: Client_SetIntroducer( CLIENT *Client, CLIENT *Introducer )
537: {
538: assert( Client != NULL );
539: assert( Introducer != NULL );
540: Client->introducer = Introducer;
541: } /* Client_SetIntroducer */
542:
543:
544: GLOBAL bool
545: Client_ModeAdd( CLIENT *Client, char Mode )
546: {
547: /* Set Mode.
548: * If Client already had Mode, return false.
549: * If the Mode was newly set, return true.
550: */
551:
552: char x[2];
553:
554: assert( Client != NULL );
555:
556: x[0] = Mode; x[1] = '\0';
557: if (!Client_HasMode(Client, x[0])) {
558: strlcat( Client->modes, x, sizeof( Client->modes ));
559: return true;
560: }
561: else return false;
562: } /* Client_ModeAdd */
563:
564:
565: GLOBAL bool
566: Client_ModeDel( CLIENT *Client, char Mode )
567: {
568: /* Delete Mode.
569: * If Mode was removed, return true.
570: * If Client did not have Mode, return false.
571: */
572:
573: char x[2], *p;
574:
575: assert( Client != NULL );
576:
577: x[0] = Mode; x[1] = '\0';
578:
579: p = strchr( Client->modes, x[0] );
580: if( ! p ) return false;
581:
582: /* Client has Mode -> delete */
583: while( *p )
584: {
585: *p = *(p + 1);
586: p++;
587: }
588: return true;
589: } /* Client_ModeDel */
590:
591:
592: /**
593: * Search CLIENT structure of a given nick name.
594: *
595: * @return Pointer to CLIENT structure or NULL if not found.
596: */
597: GLOBAL CLIENT *
598: Client_Search( const char *Nick )
599: {
600: char search_id[CLIENT_ID_LEN], *ptr;
601: CLIENT *c = NULL;
602: UINT32 search_hash;
603:
604: assert( Nick != NULL );
605:
606: /* copy Nick and truncate hostmask if necessary */
607: strlcpy( search_id, Nick, sizeof( search_id ));
608: ptr = strchr( search_id, '!' );
609: if( ptr ) *ptr = '\0';
610:
611: search_hash = Hash(search_id);
612:
613: c = My_Clients;
614: while (c) {
615: if (c->hash == search_hash && strcasecmp(c->id, search_id) == 0)
616: return c;
617: c = (CLIENT *)c->next;
618: }
619: return NULL;
620: }
621:
622:
623: /**
624: * Search first CLIENT structure matching a given mask of a server.
625: *
626: * The order of servers is arbitrary, but this function makes sure that the
627: * local server is always returned if the mask matches it.
628: *
629: * @return Pointer to CLIENT structure or NULL if no server could be found.
630: */
631: GLOBAL CLIENT *
632: Client_SearchServer(const char *Mask)
633: {
634: CLIENT *c;
635:
636: assert(Mask != NULL);
637:
638: /* First check if mask matches the local server */
639: if (MatchCaseInsensitive(Mask, Client_ID(Client_ThisServer())))
640: return Client_ThisServer();
641:
642: c = My_Clients;
643: while (c) {
644: if (Client_Type(c) == CLIENT_SERVER) {
645: /* This is a server: check if Mask matches */
646: if (MatchCaseInsensitive(Mask, c->id))
647: return c;
648: }
649: c = (CLIENT *)c->next;
650: }
651: return NULL;
652: }
653:
654:
655: /**
656: * Get client structure ("introducer") identfied by a server token.
657: * @return CLIENT structure or NULL if none could be found.
658: */
659: GLOBAL CLIENT *
660: Client_GetFromToken( CLIENT *Client, int Token )
661: {
662: CLIENT *c;
663:
664: assert( Client != NULL );
665:
666: if (!Token)
667: return NULL;
668:
669: c = My_Clients;
670: while (c) {
671: if ((c->type == CLIENT_SERVER) && (c->introducer == Client) &&
672: (c->token == Token))
673: return c;
674: c = (CLIENT *)c->next;
675: }
676: return NULL;
677: } /* Client_GetFromToken */
678:
679:
680: GLOBAL int
681: Client_Type( CLIENT *Client )
682: {
683: assert( Client != NULL );
684: return Client->type;
685: } /* Client_Type */
686:
687:
688: GLOBAL CONN_ID
689: Client_Conn( CLIENT *Client )
690: {
691: assert( Client != NULL );
692: return Client->conn_id;
693: } /* Client_Conn */
694:
695:
696: GLOBAL char *
697: Client_ID( CLIENT *Client )
698: {
699: assert( Client != NULL );
700:
701: #ifdef DEBUG
702: if(Client->type == CLIENT_USER)
703: assert(strlen(Client->id) < Conf_MaxNickLength);
704: #endif
705:
706: if( Client->id[0] ) return Client->id;
707: else return "*";
708: } /* Client_ID */
709:
710:
711: GLOBAL char *
712: Client_Info( CLIENT *Client )
713: {
714: assert( Client != NULL );
715: return Client->info;
716: } /* Client_Info */
717:
718:
719: GLOBAL char *
720: Client_User( CLIENT *Client )
721: {
722: assert( Client != NULL );
723: return Client->user[0] ? Client->user : "~";
724: } /* Client_User */
725:
726:
727: #ifdef PAM
728:
729: /**
730: * Get the "original" user name as supplied by the USER command.
731: * The user name as given by the client is used for authentication instead
732: * of the one detected using IDENT requests.
733: * @param Client The client.
734: * @return Original user name.
735: */
736: GLOBAL char *
737: Client_OrigUser(CLIENT *Client) {
738: return Client->orig_user;
739: } /* Client_OrigUser */
740:
741: #endif
742:
743: /**
744: * Return the hostname of a client.
745: * @param Client Pointer to client structure
746: * @return Pointer to client hostname
747: */
748: GLOBAL char *
749: Client_Hostname(CLIENT *Client)
750: {
751: assert (Client != NULL);
752: return Client->host;
753: }
754:
755: /**
756: * Return the cloaked hostname of a client, if set.
757: * @param Client Pointer to the client structure.
758: * @return Pointer to the cloaked hostname or NULL if not set.
759: */
760: GLOBAL char *
761: Client_HostnameCloaked(CLIENT *Client)
762: {
763: assert(Client != NULL);
764: return Client->cloaked;
765: }
766:
767: /**
768: * Get (potentially cloaked) hostname of a client to display it to other users.
769: *
770: * If the client has not enabled cloaking, the real hostname is used.
771: *
772: * @param Client Pointer to client structure
773: * @return Pointer to client hostname
774: */
775: GLOBAL char *
776: Client_HostnameDisplayed(CLIENT *Client)
777: {
778: assert(Client != NULL);
779:
780: /* Client isn't cloaked at all, return real hostname: */
781: if (!Client_HasMode(Client, 'x'))
782: return Client_Hostname(Client);
783:
784: /* Use an already saved cloaked hostname, if there is one */
785: if (Client->cloaked)
786: return Client->cloaked;
787:
788: Client_UpdateCloakedHostname(Client, NULL, NULL);
789: return Client->cloaked;
790: }
791:
792: GLOBAL const char *
793: Client_IPAText(CLIENT *Client)
794: {
795: assert(Client != NULL);
796:
797: /* Not a local client? */
798: if (Client_Conn(Client) <= NONE)
799: return "0.0.0.0";
800:
801: if (!Client->ipa_text)
802: return Conn_GetIPAInfo(Client_Conn(Client));
803: else
804: return Client->ipa_text;
805: }
806:
807: /**
808: * Update (and generate, if necessary) the cloaked hostname of a client.
809: *
810: * The newly set cloaked hostname is announced in the network using METADATA
811: * commands to peers that support this feature.
812: *
813: * @param Client The client of which the cloaked hostname should be updated.
814: * @param Origin The originator of the hostname change, or NULL if this server.
815: * @param Hostname The new cloaked hostname, or NULL if it should be generated.
816: */
817: GLOBAL void
818: Client_UpdateCloakedHostname(CLIENT *Client, CLIENT *Origin,
819: const char *Hostname)
820: {
821: char Cloak_Buffer[CLIENT_HOST_LEN];
822:
823: assert(Client != NULL);
824: if (!Origin)
825: Origin = Client_ThisServer();
826:
827: if (!Client->cloaked) {
828: Client->cloaked = malloc(CLIENT_HOST_LEN);
829: if (!Client->cloaked)
830: return;
831: }
832:
833: if (!Hostname) {
834: /* Generate new cloaked hostname */
835: if (*Conf_CloakHostModeX) {
836: strlcpy(Cloak_Buffer, Client->host,
837: sizeof(Cloak_Buffer));
838: strlcat(Cloak_Buffer, Conf_CloakHostSalt,
839: sizeof(Cloak_Buffer));
840: snprintf(Client->cloaked, CLIENT_HOST_LEN,
841: Conf_CloakHostModeX, Hash(Cloak_Buffer));
842: } else
843: strlcpy(Client->cloaked, Client_ID(Client->introducer),
844: CLIENT_HOST_LEN);
845: } else
846: strlcpy(Client->cloaked, Hostname, CLIENT_HOST_LEN);
847: LogDebug("Cloaked hostname of \"%s\" updated to \"%s\"",
848: Client_ID(Client), Client->cloaked);
849:
850: /* Inform other servers in the network */
851: IRC_WriteStrServersPrefixFlag(Client_NextHop(Origin), Origin, 'M',
852: "METADATA %s cloakhost :%s",
853: Client_ID(Client), Client->cloaked);
854: }
855:
856: GLOBAL char *
857: Client_Modes( CLIENT *Client )
858: {
859: assert( Client != NULL );
860: return Client->modes;
861: } /* Client_Modes */
862:
863:
864: GLOBAL char *
865: Client_Flags( CLIENT *Client )
866: {
867: assert( Client != NULL );
868: return Client->flags;
869: } /* Client_Flags */
870:
871:
872: GLOBAL int
873: Client_Hops( CLIENT *Client )
874: {
875: assert( Client != NULL );
876: return Client->hops;
877: } /* Client_Hops */
878:
879:
880: GLOBAL int
881: Client_Token( CLIENT *Client )
882: {
883: assert( Client != NULL );
884: return Client->token;
885: } /* Client_Token */
886:
887:
888: GLOBAL int
889: Client_MyToken( CLIENT *Client )
890: {
891: assert( Client != NULL );
892: return Client->mytoken;
893: } /* Client_MyToken */
894:
895:
896: GLOBAL CLIENT *
897: Client_NextHop( CLIENT *Client )
898: {
899: CLIENT *c;
900:
901: assert( Client != NULL );
902:
903: c = Client;
904: while( c->introducer && ( c->introducer != c ) && ( c->introducer != This_Server ))
905: c = c->introducer;
906:
907: return c;
908: } /* Client_NextHop */
909:
910:
911: /**
912: * Return ID of a client: "client!user@host"
913: * This client ID is used for IRC prefixes, for example.
914: * Please note that this function uses a global static buffer, so you can't
915: * nest invocations without overwriting earlier results!
916: * @param Client Pointer to client structure
917: * @return Pointer to global buffer containing the client ID
918: */
919: GLOBAL char *
920: Client_Mask( CLIENT *Client )
921: {
922: static char Mask_Buffer[GETID_LEN];
923:
924: assert (Client != NULL);
925:
926: /* Servers: return name only, there is no "mask" */
927: if (Client->type == CLIENT_SERVER)
928: return Client->id;
929:
930: snprintf(Mask_Buffer, GETID_LEN, "%s!%s@%s",
931: Client->id, Client->user, Client->host);
932: return Mask_Buffer;
933: } /* Client_Mask */
934:
935:
936: /**
937: * Return ID of a client with cloaked hostname: "client!user@server-name"
938: *
939: * This client ID is used for IRC prefixes, for example.
940: * Please note that this function uses a global static buffer, so you can't
941: * nest invocations without overwriting earlier results!
942: * If the client has not enabled cloaking, the real hostname is used.
943: *
944: * @param Client Pointer to client structure
945: * @return Pointer to global buffer containing the client ID
946: */
947: GLOBAL char *
948: Client_MaskCloaked(CLIENT *Client)
949: {
950: static char Mask_Buffer[GETID_LEN];
951:
952: assert (Client != NULL);
953:
954: /* Is the client using cloaking at all? */
955: if (!Client_HasMode(Client, 'x'))
956: return Client_Mask(Client);
957:
958: snprintf(Mask_Buffer, GETID_LEN, "%s!%s@%s", Client->id, Client->user,
959: Client_HostnameDisplayed(Client));
960:
961: return Mask_Buffer;
962: } /* Client_MaskCloaked */
963:
964:
965: GLOBAL CLIENT *
966: Client_Introducer( CLIENT *Client )
967: {
968: assert( Client != NULL );
969: return Client->introducer;
970: } /* Client_Introducer */
971:
972:
973: GLOBAL CLIENT *
974: Client_TopServer( CLIENT *Client )
975: {
976: assert( Client != NULL );
977: return Client->topserver;
978: } /* Client_TopServer */
979:
980:
981: GLOBAL bool
982: Client_HasMode( CLIENT *Client, char Mode )
983: {
984: assert( Client != NULL );
985: return strchr( Client->modes, Mode ) != NULL;
986: } /* Client_HasMode */
987:
988:
989: GLOBAL bool
990: Client_HasFlag( CLIENT *Client, char Flag )
991: {
992: assert( Client != NULL );
993: return strchr( Client->flags, Flag ) != NULL;
994: } /* Client_HasFlag */
995:
996:
997: GLOBAL char *
998: Client_Away( CLIENT *Client )
999: {
1000: assert( Client != NULL );
1001: return Client->away;
1002: } /* Client_Away */
1003:
1004:
1005: GLOBAL char *
1006: Client_AccountName(CLIENT *Client)
1007: {
1008: assert(Client != NULL);
1009: return Client->account_name;
1010: }
1011:
1012:
1013: /**
1014: * Make sure that a given nickname is valid.
1015: *
1016: * If the nickname is not valid for the given client, this function sends back
1017: * the appropriate error messages.
1018: *
1019: * @param Client Client that wants to change the nickname.
1020: * @param Nick New nickname.
1021: * @returns true if nickname is valid, false otherwise.
1022: */
1023: GLOBAL bool
1024: Client_CheckNick(CLIENT *Client, char *Nick)
1025: {
1026: assert(Client != NULL);
1027: assert(Nick != NULL);
1028:
1029: if (!Client_IsValidNick(Nick)) {
1030: if (strlen(Nick ) >= Conf_MaxNickLength)
1031: IRC_WriteErrClient(Client, ERR_NICKNAMETOOLONG_MSG,
1032: Client_ID(Client), Nick,
1033: Conf_MaxNickLength - 1);
1034: else
1035: IRC_WriteErrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
1036: Client_ID(Client), Nick);
1037: return false;
1038: }
1039:
1040: if (Client_Type(Client) != CLIENT_SERVER
1041: && Client_Type(Client) != CLIENT_SERVICE) {
1042: /* Make sure that this isn't a restricted/forbidden nickname */
1043: if (Conf_NickIsBlocked(Nick)) {
1044: IRC_WriteErrClient(Client, ERR_FORBIDDENNICKNAME_MSG,
1045: Client_ID(Client), Nick);
1046: return false;
1047: }
1048: }
1049:
1050: /* Nickname already registered? */
1051: if (Client_Search(Nick)) {
1052: IRC_WriteErrClient(Client, ERR_NICKNAMEINUSE_MSG,
1053: Client_ID(Client), Nick);
1054: return false;
1055: }
1056:
1057: return true;
1058: } /* Client_CheckNick */
1059:
1060:
1061: GLOBAL bool
1062: Client_CheckID( CLIENT *Client, char *ID )
1063: {
1064: char str[COMMAND_LEN];
1065: CLIENT *c;
1066:
1067: assert( Client != NULL );
1068: assert( Client->conn_id > NONE );
1069: assert( ID != NULL );
1070:
1071: /* ID too long? */
1072: if (strlen(ID) > CLIENT_ID_LEN) {
1073: IRC_WriteErrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
1074: Client_ID(Client), ID);
1075: return false;
1076: }
1077:
1078: /* ID already in use? */
1079: c = My_Clients;
1080: while (c) {
1081: if (strcasecmp(c->id, ID) == 0) {
1082: snprintf(str, sizeof(str), "ID \"%s\" already registered", ID);
1083: if (c->conn_id != NONE)
1084: Log(LOG_ERR, "%s (on connection %d)!", str, c->conn_id);
1085: else
1086: Log(LOG_ERR, "%s (via network)!", str);
1087: Conn_Close(Client->conn_id, str, str, true);
1088: return false;
1089: }
1090: c = (CLIENT *)c->next;
1091: }
1092:
1093: return true;
1094: } /* Client_CheckID */
1095:
1096:
1097: GLOBAL CLIENT *
1098: Client_First( void )
1099: {
1100: return My_Clients;
1101: } /* Client_First */
1102:
1103:
1104: GLOBAL CLIENT *
1105: Client_Next( CLIENT *c )
1106: {
1107: assert( c != NULL );
1108: return (CLIENT *)c->next;
1109: } /* Client_Next */
1110:
1111:
1112: GLOBAL long
1113: Client_UserCount( void )
1114: {
1115: return Count( CLIENT_USER );
1116: } /* Client_UserCount */
1117:
1118:
1119: GLOBAL long
1120: Client_ServiceCount( void )
1121: {
1122: return Count( CLIENT_SERVICE );;
1123: } /* Client_ServiceCount */
1124:
1125:
1126: GLOBAL long
1127: Client_ServerCount( void )
1128: {
1129: return Count( CLIENT_SERVER );
1130: } /* Client_ServerCount */
1131:
1132:
1133: GLOBAL long
1134: Client_MyUserCount( void )
1135: {
1136: return MyCount( CLIENT_USER );
1137: } /* Client_MyUserCount */
1138:
1139:
1140: GLOBAL long
1141: Client_MyServiceCount( void )
1142: {
1143: return MyCount( CLIENT_SERVICE );
1144: } /* Client_MyServiceCount */
1145:
1146:
1147: GLOBAL unsigned long
1148: Client_MyServerCount( void )
1149: {
1150: CLIENT *c;
1151: unsigned long cnt = 0;
1152:
1153: c = My_Clients;
1154: while( c )
1155: {
1156: if(( c->type == CLIENT_SERVER ) && ( c->hops == 1 )) cnt++;
1157: c = (CLIENT *)c->next;
1158: }
1159: return cnt;
1160: } /* Client_MyServerCount */
1161:
1162:
1163: GLOBAL unsigned long
1164: Client_OperCount( void )
1165: {
1166: CLIENT *c;
1167: unsigned long cnt = 0;
1168:
1169: c = My_Clients;
1170: while( c )
1171: {
1172: if (c && c->type == CLIENT_USER && Client_HasMode(c, 'o' ))
1173: cnt++;
1174: c = (CLIENT *)c->next;
1175: }
1176: return cnt;
1177: } /* Client_OperCount */
1178:
1179:
1180: GLOBAL unsigned long
1181: Client_UnknownCount( void )
1182: {
1183: CLIENT *c;
1184: unsigned long cnt = 0;
1185:
1186: c = My_Clients;
1187: while( c )
1188: {
1189: if( c && ( c->type != CLIENT_USER ) && ( c->type != CLIENT_SERVICE ) && ( c->type != CLIENT_SERVER )) cnt++;
1190: c = (CLIENT *)c->next;
1191: }
1192:
1193: return cnt;
1194: } /* Client_UnknownCount */
1195:
1196:
1197: GLOBAL long
1198: Client_MaxUserCount( void )
1199: {
1200: return Max_Users;
1201: } /* Client_MaxUserCount */
1202:
1203:
1204: GLOBAL long
1205: Client_MyMaxUserCount( void )
1206: {
1207: return My_Max_Users;
1208: } /* Client_MyMaxUserCount */
1209:
1210:
1211: /**
1212: * Check that a given nickname is valid.
1213: *
1214: * @param Nick the nickname to check.
1215: * @returns true if nickname is valid, false otherwise.
1216: */
1217: GLOBAL bool
1218: Client_IsValidNick(const char *Nick)
1219: {
1220: const char *ptr;
1221: static const char goodchars[] = ";0123456789-";
1222:
1223: assert (Nick != NULL);
1224:
1225: if (strchr(goodchars, Nick[0]))
1226: return false;
1227: if (strlen(Nick ) >= Conf_MaxNickLength)
1228: return false;
1229:
1230: ptr = Nick;
1231: while (*ptr) {
1232: if (*ptr < 'A' && !strchr(goodchars, *ptr ))
1233: return false;
1234: if (*ptr > '}')
1235: return false;
1236: ptr++;
1237: }
1238:
1239: return true;
1240: } /* Client_IsValidNick */
1241:
1242:
1243: /**
1244: * Return pointer to "My_Whowas" structure.
1245: */
1246: GLOBAL WHOWAS *
1247: Client_GetWhowas( void )
1248: {
1249: return My_Whowas;
1250: } /* Client_GetWhowas */
1251:
1252: /**
1253: * Return the index of the last used WHOWAS entry.
1254: */
1255: GLOBAL int
1256: Client_GetLastWhowasIndex( void )
1257: {
1258: return Last_Whowas;
1259: } /* Client_GetLastWhowasIndex */
1260:
1261:
1262: /**
1263: * Get the start time of this client.
1264: * The result is the start time in seconds since 1970-01-01, as reported
1265: * by the C function time(NULL).
1266: */
1267: GLOBAL time_t
1268: Client_StartTime(CLIENT *Client)
1269: {
1270: assert( Client != NULL );
1271: return Client->starttime;
1272: } /* Client_Uptime */
1273:
1274:
1275: /**
1276: * Reject a client when logging in.
1277: *
1278: * This function is called when a client isn't allowed to connect to this
1279: * server. Possible reasons are bad server password, bad PAM password,
1280: * or that the client is G/K-Line'd.
1281: *
1282: * After calling this function, the client isn't connected any more.
1283: *
1284: * @param Client The client to reject.
1285: * @param Reason The reason why the client has been rejected.
1286: * @param InformClient If true, send the exact reason to the client.
1287: */
1288: GLOBAL void
1289: Client_Reject(CLIENT *Client, const char *Reason, bool InformClient)
1290: {
1291: char info[COMMAND_LEN];
1292:
1293: assert(Client != NULL);
1294: assert(Reason != NULL);
1295:
1296: if (InformClient)
1297: snprintf(info, sizeof(info), "Access denied: %s", Reason);
1298: else
1299: strcpy(info, "Access denied: Bad password?");
1300:
1301: Log(LOG_ERR,
1302: "User \"%s\" rejected (connection %d): %s!",
1303: Client_Mask(Client), Client_Conn(Client), Reason);
1304: Conn_Close(Client_Conn(Client), Reason, info, true);
1305: }
1306:
1307:
1308: /**
1309: * Introduce a new user or service client in the network.
1310: *
1311: * @param From Remote server introducing the client or NULL (local).
1312: * @param Client New client.
1313: * @param Type Type of the client (CLIENT_USER or CLIENT_SERVICE).
1314: */
1315: GLOBAL void
1316: Client_Introduce(CLIENT *From, CLIENT *Client, int Type)
1317: {
1318: /* Set client type (user or service) */
1319: Client_SetType(Client, Type);
1320:
1321: if (From) {
1322: if (Conf_NickIsService(Conf_GetServer(Client_Conn(From)),
1323: Client_ID(Client)))
1324: Client_SetType(Client, CLIENT_SERVICE);
1325: LogDebug("%s \"%s\" (+%s) registered (via %s, on %s, %d hop%s).",
1326: Client_TypeText(Client), Client_Mask(Client),
1327: Client_Modes(Client), Client_ID(From),
1328: Client_ID(Client_Introducer(Client)),
1329: Client_Hops(Client), Client_Hops(Client) > 1 ? "s": "");
1330: } else {
1331: Log(LOG_NOTICE, "%s \"%s\" registered (connection %d).",
1332: Client_TypeText(Client), Client_Mask(Client),
1333: Client_Conn(Client));
1334: Log_ServerNotice('c', "Client connecting: %s (%s@%s) [%s] - %s",
1335: Client_ID(Client), Client_User(Client),
1336: Client_Hostname(Client),
1337: Conn_IPA(Client_Conn(Client)),
1338: Client_TypeText(Client));
1339: }
1340:
1341: /* Inform other servers */
1342: IRC_WriteStrServersPrefixFlag_CB(From,
1343: From != NULL ? From : Client_ThisServer(),
1344: '\0', cb_introduceClient, (void *)Client);
1345: } /* Client_Introduce */
1346:
1347:
1348: static unsigned long
1349: Count( CLIENT_TYPE Type )
1350: {
1351: CLIENT *c;
1352: unsigned long cnt = 0;
1353:
1354: c = My_Clients;
1355: while( c )
1356: {
1357: if( c->type == Type ) cnt++;
1358: c = (CLIENT *)c->next;
1359: }
1360: return cnt;
1361: } /* Count */
1362:
1363:
1364: static unsigned long
1365: MyCount( CLIENT_TYPE Type )
1366: {
1367: CLIENT *c;
1368: unsigned long cnt = 0;
1369:
1370: c = My_Clients;
1371: while( c )
1372: {
1373: if(( c->introducer == This_Server ) && ( c->type == Type )) cnt++;
1374: c = (CLIENT *)c->next;
1375: }
1376: return cnt;
1377: } /* MyCount */
1378:
1379:
1380: /**
1381: * Allocate and initialize new CLIENT strcuture.
1382: *
1383: * @return Pointer to CLIENT structure or NULL on error.
1384: */
1385: static CLIENT *
1386: New_Client_Struct( void )
1387: {
1388: CLIENT *c;
1389:
1390: c = (CLIENT *)malloc( sizeof( CLIENT ));
1391: if( ! c )
1392: {
1393: Log( LOG_EMERG, "Can't allocate memory! [New_Client_Struct]" );
1394: return NULL;
1395: }
1396:
1397: memset( c, 0, sizeof ( CLIENT ));
1398:
1399: c->type = CLIENT_UNKNOWN;
1400: c->conn_id = NONE;
1401: c->hops = -1;
1402: c->token = -1;
1403: c->mytoken = -1;
1404:
1405: return c;
1406: }
1407:
1408: /**
1409: * Free a CLIENT structure and its member variables.
1410: */
1411: static void
1412: Free_Client(CLIENT **Client)
1413: {
1414: assert(Client != NULL);
1415: assert(*Client != NULL);
1416:
1417: if ((*Client)->account_name)
1418: free((*Client)->account_name);
1419: if ((*Client)->away)
1420: free((*Client)->away);
1421: if ((*Client)->cloaked)
1422: free((*Client)->cloaked);
1423: if ((*Client)->ipa_text)
1424: free((*Client)->ipa_text);
1425:
1426: free(*Client);
1427: *Client = NULL;
1428: }
1429:
1430: static void
1431: Generate_MyToken( CLIENT *Client )
1432: {
1433: CLIENT *c;
1434: int token;
1435:
1436: c = My_Clients;
1437: token = 2;
1438: while( c )
1439: {
1440: if( c->mytoken == token )
1441: {
1442: /* The token is already in use */
1443: token++;
1444: c = My_Clients;
1445: continue;
1446: }
1447: else c = (CLIENT *)c->next;
1448: }
1449: Client->mytoken = token;
1450: LogDebug("Assigned token %d to server \"%s\".", token, Client->id);
1451: } /* Generate_MyToken */
1452:
1453:
1454: static void
1455: Adjust_Counters( CLIENT *Client )
1456: {
1457: long count;
1458:
1459: assert( Client != NULL );
1460:
1461: if( Client->type != CLIENT_USER ) return;
1462:
1463: if( Client->conn_id != NONE )
1464: {
1465: /* Local connection */
1466: count = Client_MyUserCount( );
1467: if( count > My_Max_Users ) My_Max_Users = count;
1468: }
1469: count = Client_UserCount( );
1470: if( count > Max_Users ) Max_Users = count;
1471: } /* Adjust_Counters */
1472:
1473:
1474: /**
1475: * Register client in My_Whowas structure for further recall by WHOWAS.
1476: * Note: Only clients that have been connected at least 30 seconds will be
1477: * registered to prevent automated IRC bots to "destroy" a nice server
1478: * history database.
1479: */
1480: GLOBAL void
1481: Client_RegisterWhowas( CLIENT *Client )
1482: {
1483: int slot;
1484: time_t now;
1485:
1486: assert( Client != NULL );
1487:
1488: /* Don't register WHOWAS information when "MorePrivacy" is enabled. */
1489: if (Conf_MorePrivacy)
1490: return;
1491:
1492: now = time(NULL);
1493: /* Don't register clients that were connected less than 30 seconds. */
1494: if( now - Client->starttime < 30 )
1495: return;
1496:
1497: slot = Last_Whowas + 1;
1498: if( slot >= MAX_WHOWAS || slot < 0 ) slot = 0;
1499:
1500: #ifdef DEBUG
1501: Log( LOG_DEBUG, "Saving WHOWAS information to slot %d ...", slot );
1502: #endif
1503:
1504: My_Whowas[slot].time = now;
1505: strlcpy( My_Whowas[slot].id, Client_ID( Client ),
1506: sizeof( My_Whowas[slot].id ));
1507: strlcpy( My_Whowas[slot].user, Client_User( Client ),
1508: sizeof( My_Whowas[slot].user ));
1509: strlcpy( My_Whowas[slot].host, Client_HostnameDisplayed( Client ),
1510: sizeof( My_Whowas[slot].host ));
1511: strlcpy( My_Whowas[slot].info, Client_Info( Client ),
1512: sizeof( My_Whowas[slot].info ));
1513: strlcpy( My_Whowas[slot].server, Client_ID( Client_Introducer( Client )),
1514: sizeof( My_Whowas[slot].server ));
1515:
1516: Last_Whowas = slot;
1517: } /* Client_RegisterWhowas */
1518:
1519:
1520: GLOBAL const char *
1521: Client_TypeText(CLIENT *Client)
1522: {
1523: assert(Client != NULL);
1524: switch (Client_Type(Client)) {
1525: case CLIENT_USER:
1526: return "User";
1527: break;
1528: case CLIENT_SERVICE:
1529: return "Service";
1530: break;
1531: case CLIENT_SERVER:
1532: return "Server";
1533: break;
1534: default:
1535: return "Client";
1536: }
1537: } /* Client_TypeText */
1538:
1539:
1540: /**
1541: * Destroy user or service client.
1542: */
1543: static void
1544: Destroy_UserOrService(CLIENT *Client, const char *Txt, const char *FwdMsg, bool SendQuit)
1545: {
1546: if(Client->conn_id != NONE) {
1547: /* Local (directly connected) client */
1548: Log(LOG_NOTICE,
1549: "%s \"%s\" unregistered (connection %d): %s.",
1550: Client_TypeText(Client), Client_Mask(Client),
1551: Client->conn_id, Txt);
1552: Log_ServerNotice('c', "Client exiting: %s (%s@%s) [%s]",
1553: Client_ID(Client), Client_User(Client),
1554: Client_Hostname(Client), Txt);
1555:
1556: if (SendQuit) {
1557: /* Inforam all the other servers */
1558: if (FwdMsg)
1559: IRC_WriteStrServersPrefix(NULL,
1560: Client, "QUIT :%s", FwdMsg );
1561: else
1562: IRC_WriteStrServersPrefix(NULL,
1563: Client, "QUIT :");
1564: }
1565: } else {
1566: /* Remote client */
1567: LogDebug("%s \"%s\" unregistered: %s.",
1568: Client_TypeText(Client), Client_Mask(Client), Txt);
1569:
1570: if(SendQuit) {
1571: /* Inform all the other servers, but the ones in the
1572: * direction we got the QUIT from */
1573: if(FwdMsg)
1574: IRC_WriteStrServersPrefix(Client_NextHop(Client),
1575: Client, "QUIT :%s", FwdMsg );
1576: else
1577: IRC_WriteStrServersPrefix(Client_NextHop(Client),
1578: Client, "QUIT :" );
1579: }
1580: }
1581:
1582: /* Unregister client from channels */
1583: Channel_Quit(Client, FwdMsg ? FwdMsg : Client->id);
1584:
1585: /* Register client in My_Whowas structure */
1586: Client_RegisterWhowas(Client);
1587: } /* Destroy_UserOrService */
1588:
1589:
1590: /**
1591: * Introduce a new user or service client to a remote server.
1592: *
1593: * @param To The remote server to inform.
1594: * @param Prefix Prefix for the generated commands.
1595: * @param data CLIENT structure of the new client.
1596: */
1597: static void
1598: cb_introduceClient(CLIENT *To, CLIENT *Prefix, void *data)
1599: {
1600: CLIENT *c = (CLIENT *)data;
1601:
1602: (void)Client_Announce(To, Prefix, c);
1603:
1604: } /* cb_introduceClient */
1605:
1606:
1607: /**
1608: * Announce an user or service to a server.
1609: *
1610: * This function differentiates between RFC1459 and RFC2813 server links and
1611: * generates the appropriate commands to register the user or service.
1612: *
1613: * @param Client Server
1614: * @param Prefix Prefix for the generated commands
1615: * @param User User to announce
1616: */
1617: GLOBAL bool
1618: Client_Announce(CLIENT * Client, CLIENT * Prefix, CLIENT * User)
1619: {
1620: CONN_ID conn;
1621: char *modes, *user, *host;
1622:
1623: modes = Client_Modes(User);
1624: user = Client_User(User) ? Client_User(User) : "-";
1625: host = Client_Hostname(User) ? Client_Hostname(User) : "-";
1626:
1627: conn = Client_Conn(Client);
1628: if (Conn_Options(conn) & CONN_RFC1459) {
1629: /* RFC 1459 mode: separate NICK and USER commands */
1630: if (! Conn_WriteStr(conn, "NICK %s :%d",
1631: Client_ID(User), Client_Hops(User) + 1))
1632: return DISCONNECTED;
1633: if (! Conn_WriteStr(conn, ":%s USER %s %s %s :%s",
1634: Client_ID(User), user, host,
1635: Client_ID(Client_Introducer(User)),
1636: Client_Info(User)))
1637: return DISCONNECTED;
1638: if (modes[0]) {
1639: if (! Conn_WriteStr(conn, ":%s MODE %s +%s",
1640: Client_ID(User), Client_ID(User),
1641: modes))
1642: return DISCONNECTED;
1643: }
1644: } else {
1645: /* RFC 2813 mode: one combined NICK or SERVICE command */
1646: if (Client_Type(User) == CLIENT_SERVICE
1647: && Client_HasFlag(Client, 'S')) {
1648: if (!IRC_WriteStrClientPrefix(Client, Prefix,
1649: "SERVICE %s %d * +%s %d :%s",
1650: Client_Mask(User),
1651: Client_MyToken(Client_Introducer(User)),
1652: modes, Client_Hops(User) + 1,
1653: Client_Info(User)))
1654: return DISCONNECTED;
1655: } else {
1656: if (!IRC_WriteStrClientPrefix(Client, Prefix,
1657: "NICK %s %d %s %s %d +%s :%s",
1658: Client_ID(User), Client_Hops(User) + 1,
1659: user, host,
1660: Client_MyToken(Client_Introducer(User)),
1661: modes, Client_Info(User)))
1662: return DISCONNECTED;
1663: }
1664: }
1665:
1666: if (Client_HasFlag(Client, 'M')) {
1667: /* Synchronize metadata */
1668: if (Client_HostnameCloaked(User)) {
1669: if (!IRC_WriteStrClientPrefix(Client, Prefix,
1670: "METADATA %s cloakhost :%s",
1671: Client_ID(User),
1672: Client_HostnameCloaked(User)))
1673: return DISCONNECTED;
1674: }
1675:
1676: if (Client_AccountName(User)) {
1677: if (!IRC_WriteStrClientPrefix(Client, Prefix,
1678: "METADATA %s accountname :%s",
1679: Client_ID(User),
1680: Client_AccountName(User)))
1681: return DISCONNECTED;
1682: }
1683:
1684: if (Conn_GetCertFp(Client_Conn(User))) {
1685: if (!IRC_WriteStrClientPrefix(Client, Prefix,
1686: "METADATA %s certfp :%s",
1687: Client_ID(User),
1688: Conn_GetCertFp(Client_Conn(User))))
1689: return DISCONNECTED;
1690: }
1691: }
1692:
1693: return CONNECTED;
1694: } /* Client_Announce */
1695:
1696:
1697: #ifdef DEBUG
1698:
1699: GLOBAL void
1700: Client_DebugDump(void)
1701: {
1702: CLIENT *c;
1703:
1704: Log(LOG_DEBUG, "Client status:");
1705: c = My_Clients;
1706: while (c) {
1707: Log(LOG_DEBUG,
1708: " - %s: type=%d, host=%s, user=%s, conn=%d, start=%ld, flags=%s",
1709: Client_ID(c), Client_Type(c), Client_Hostname(c),
1710: Client_User(c), Client_Conn(c), Client_StartTime(c),
1711: Client_Flags(c));
1712: c = (CLIENT *)c->next;
1713: }
1714: } /* Client_DumpClients */
1715:
1716: #endif
1717:
1718:
1719: /* -eof- */
CVSweb