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