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