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