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