/*
* ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2012 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2013 Alexander Barton (alex@barton.de) and Contributors.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include "hash.h"
#include "irc-write.h"
#include "log.h"
+#include "match.h"
#include "messages.h"
#include <exp.h>
static void Generate_MyToken PARAMS(( CLIENT *Client ));
static void Adjust_Counters PARAMS(( CLIENT *Client ));
+static void Free_Client PARAMS(( CLIENT **Client ));
+
static CLIENT *Init_New_Client PARAMS((CONN_ID Idx, CLIENT *Introducer,
CLIENT *TopServer, int Type, const char *ID,
const char *User, const char *Hostname, const char *Info,
exit( 1 );
}
- /* Client-Struktur dieses Servers */
+ /* Client structure for this server */
This_Server->next = NULL;
This_Server->type = CLIENT_SERVER;
This_Server->conn_id = NONE;
cnt = 0;
c = My_Clients;
- while( c )
- {
+ while(c) {
cnt++;
next = (CLIENT *)c->next;
- free( c );
+ Free_Client(&c);
c = next;
}
- if( cnt ) Log( LOG_INFO, "Freed %d client structure%s.", cnt, cnt == 1 ? "" : "s" );
+ if (cnt)
+ Log(LOG_INFO, "Freed %d client structure%s.",
+ cnt, cnt == 1 ? "" : "s");
} /* Client_Exit */
if (Type == CLIENT_SERVER)
Generate_MyToken(client);
- if (strchr(client->modes, 'a'))
- strlcpy(client->away, DEFAULT_AWAY_MSG, sizeof(client->away));
+ if (Client_HasMode(client, 'a'))
+ client->away = strndup(DEFAULT_AWAY_MSG, CLIENT_AWAY_LEN - 1);
client->next = (POINTER *)My_Clients;
My_Clients = client;
/* remove a client */
CLIENT *last, *c;
- char msg[LINE_LEN];
+ char msg[COMMAND_LEN];
const char *txt;
assert( Client != NULL );
}
}
- free( c );
+ Free_Client(&c);
break;
}
last = c;
} /* Client_SetHostname */
+/**
+ * Set IP address to display for a client.
+ *
+ * @param Client The client.
+ * @param IPAText Textual representation of the IP address or NULL to unset.
+ */
+GLOBAL void
+Client_SetIPAText(CLIENT *Client, const char *IPAText)
+{
+ assert(Client != NULL);
+
+ if (Client->ipa_text)
+ free(Client->ipa_text);
+
+ if (*IPAText)
+ Client->ipa_text = strndup(IPAText, CLIENT_HOST_LEN - 1);
+ else
+ Client->ipa_text = NULL;
+}
+
+
GLOBAL void
Client_SetID( CLIENT *Client, const char *ID )
{
} /* Client_SetFlags */
+GLOBAL void
+Client_SetAccountName(CLIENT *Client, const char *AccountName)
+{
+ assert(Client != NULL);
+
+ if (Client->account_name)
+ free(Client->account_name);
+
+ if (*AccountName)
+ Client->account_name = strndup(AccountName,
+ CLIENT_NICK_LEN - 1);
+ else
+ Client->account_name = NULL;
+}
+
+
GLOBAL void
Client_SetAway( CLIENT *Client, const char *Txt )
{
assert( Client != NULL );
assert( Txt != NULL );
- strlcpy( Client->away, Txt, sizeof( Client->away ));
+ if (Client->away)
+ free(Client->away);
+
+ Client->away = strndup(Txt, CLIENT_AWAY_LEN - 1);
+
LogDebug("%s \"%s\" is away: %s", Client_TypeText(Client),
Client_Mask(Client), Txt);
} /* Client_SetAway */
} /* Client_SetIntroducer */
-GLOBAL void
-Client_SetOperByMe( CLIENT *Client, bool OperByMe )
-{
- assert( Client != NULL );
- Client->oper_by_me = OperByMe;
-} /* Client_SetOperByMe */
-
-
GLOBAL bool
Client_ModeAdd( CLIENT *Client, char Mode )
{
/* Set Mode.
- * If Client already alread had Mode, return false.
+ * If Client already had Mode, return false.
* If the Mode was newly set, return true.
*/
assert( Client != NULL );
x[0] = Mode; x[1] = '\0';
- if (!strchr( Client->modes, x[0])) {
+ if (!Client_HasMode(Client, x[0])) {
strlcat( Client->modes, x, sizeof( Client->modes ));
return true;
}
} /* Client_ModeDel */
+/**
+ * Search CLIENT structure of a given nick name.
+ *
+ * @return Pointer to CLIENT structure or NULL if not found.
+ */
GLOBAL CLIENT *
Client_Search( const char *Nick )
{
- /* return Client-Structure that has the corresponding Nick.
- * If none is found, return NULL.
- */
-
char search_id[CLIENT_ID_LEN], *ptr;
CLIENT *c = NULL;
UINT32 search_hash;
c = (CLIENT *)c->next;
}
return NULL;
-} /* Client_Search */
+}
+
+
+/**
+ * Search first CLIENT structure matching a given mask of a server.
+ *
+ * The order of servers is arbitrary, but this function makes sure that the
+ * local server is always returned if the mask matches it.
+ *
+ * @return Pointer to CLIENT structure or NULL if no server could be found.
+ */
+GLOBAL CLIENT *
+Client_SearchServer(const char *Mask)
+{
+ CLIENT *c;
+
+ assert(Mask != NULL);
+
+ /* First check if mask matches the local server */
+ if (MatchCaseInsensitive(Mask, Client_ID(Client_ThisServer())))
+ return Client_ThisServer();
+
+ c = My_Clients;
+ while (c) {
+ if (Client_Type(c) == CLIENT_SERVER) {
+ /* This is a server: check if Mask matches */
+ if (MatchCaseInsensitive(Mask, c->id))
+ return c;
+ }
+ c = (CLIENT *)c->next;
+ }
+ return NULL;
+}
/**
* Get (potentially cloaked) hostname of a client to display it to other users.
*
* If the client has not enabled cloaking, the real hostname is used.
- * Please note that this function uses a global static buffer, so you can't
- * nest invocations without overwriting earlier results!
*
* @param Client Pointer to client structure
* @return Pointer to client hostname
return Client_Hostname(Client);
/* Use an already saved cloaked hostname, if there is one */
- if (Client->cloaked[0])
+ if (Client->cloaked)
return Client->cloaked;
Client_UpdateCloakedHostname(Client, NULL, NULL);
return Client->cloaked;
}
+GLOBAL const char *
+Client_IPAText(CLIENT *Client)
+{
+ assert(Client != NULL);
+
+ /* Not a local client? */
+ if (Client_Conn(Client) <= NONE)
+ return "0.0.0.0";
+
+ if (!Client->ipa_text)
+ return Conn_GetIPAInfo(Client_Conn(Client));
+ else
+ return Client->ipa_text;
+}
+
/**
* Update (and generate, if necessary) the cloaked hostname of a client.
*
Client_UpdateCloakedHostname(CLIENT *Client, CLIENT *Origin,
const char *Hostname)
{
- static char Cloak_Buffer[CLIENT_HOST_LEN];
+ char Cloak_Buffer[CLIENT_HOST_LEN];
assert(Client != NULL);
if (!Origin)
Origin = Client_ThisServer();
+ if (!Client->cloaked) {
+ Client->cloaked = malloc(CLIENT_HOST_LEN);
+ if (!Client->cloaked)
+ return;
+ }
+
if (!Hostname) {
/* Generate new cloaked hostname */
if (*Conf_CloakHostModeX) {
- strlcpy(Cloak_Buffer, Client->host, CLIENT_HOST_LEN);
+ strlcpy(Cloak_Buffer, Client->host,
+ sizeof(Cloak_Buffer));
strlcat(Cloak_Buffer, Conf_CloakHostSalt,
- CLIENT_HOST_LEN);
- snprintf(Client->cloaked, sizeof(Client->cloaked),
+ sizeof(Cloak_Buffer));
+ snprintf(Client->cloaked, CLIENT_HOST_LEN,
Conf_CloakHostModeX, Hash(Cloak_Buffer));
} else
strlcpy(Client->cloaked, Client_ID(Client->introducer),
- sizeof(Client->cloaked));
+ CLIENT_HOST_LEN);
} else
- strlcpy(Client->cloaked, Hostname, sizeof(Client->cloaked));
+ strlcpy(Client->cloaked, Hostname, CLIENT_HOST_LEN);
LogDebug("Cloaked hostname of \"%s\" updated to \"%s\"",
Client_ID(Client), Client->cloaked);
} /* Client_Flags */
-GLOBAL bool
-Client_OperByMe( CLIENT *Client )
-{
- assert( Client != NULL );
- return Client->oper_by_me;
-} /* Client_OperByMe */
-
-
GLOBAL int
Client_Hops( CLIENT *Client )
{
} /* Client_HasMode */
+GLOBAL bool
+Client_HasFlag( CLIENT *Client, char Flag )
+{
+ assert( Client != NULL );
+ return strchr( Client->flags, Flag ) != NULL;
+} /* Client_HasFlag */
+
+
GLOBAL char *
Client_Away( CLIENT *Client )
{
} /* Client_Away */
+GLOBAL char *
+Client_AccountName(CLIENT *Client)
+{
+ assert(Client != NULL);
+ return Client->account_name;
+}
+
+
/**
* Make sure that a given nickname is valid.
*
if (!Client_IsValidNick(Nick)) {
if (strlen(Nick ) >= Conf_MaxNickLength)
- IRC_WriteStrClient(Client, ERR_NICKNAMETOOLONG_MSG,
+ IRC_WriteErrClient(Client, ERR_NICKNAMETOOLONG_MSG,
Client_ID(Client), Nick,
Conf_MaxNickLength - 1);
else
- IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
+ IRC_WriteErrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
Client_ID(Client), Nick);
return false;
}
&& Client_Type(Client) != CLIENT_SERVICE) {
/* Make sure that this isn't a restricted/forbidden nickname */
if (Conf_NickIsBlocked(Nick)) {
- IRC_WriteStrClient(Client, ERR_FORBIDDENNICKNAME_MSG,
+ IRC_WriteErrClient(Client, ERR_FORBIDDENNICKNAME_MSG,
Client_ID(Client), Nick);
return false;
}
/* Nickname already registered? */
if (Client_Search(Nick)) {
- IRC_WriteStrClient(Client, ERR_NICKNAMEINUSE_MSG,
+ IRC_WriteErrClient(Client, ERR_NICKNAMEINUSE_MSG,
Client_ID(Client), Nick);
return false;
}
/* ID too long? */
if (strlen(ID) > CLIENT_ID_LEN) {
- IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG, Client_ID(Client), ID);
+ IRC_WriteErrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
+ Client_ID(Client), ID);
return false;
}
c = My_Clients;
while( c )
{
- if( c && ( c->type == CLIENT_USER ) && ( strchr( c->modes, 'o' ))) cnt++;
+ if (c && c->type == CLIENT_USER && Client_HasMode(c, 'o' ))
+ cnt++;
c = (CLIENT *)c->next;
}
return cnt;
} /* MyCount */
+/**
+ * Allocate and initialize new CLIENT strcuture.
+ *
+ * @return Pointer to CLIENT structure or NULL on error.
+ */
static CLIENT *
New_Client_Struct( void )
{
- /* Neue CLIENT-Struktur pre-initialisieren */
-
CLIENT *c;
c = (CLIENT *)malloc( sizeof( CLIENT ));
c->type = CLIENT_UNKNOWN;
c->conn_id = NONE;
- c->oper_by_me = false;
c->hops = -1;
c->token = -1;
c->mytoken = -1;
return c;
-} /* New_Client */
+}
+/**
+ * Free a CLIENT structure and its member variables.
+ */
+static void
+Free_Client(CLIENT **Client)
+{
+ assert(Client != NULL);
+ assert(*Client != NULL);
+
+ if ((*Client)->account_name)
+ free((*Client)->account_name);
+ if ((*Client)->away)
+ free((*Client)->away);
+ if ((*Client)->cloaked)
+ free((*Client)->cloaked);
+ if ((*Client)->ipa_text)
+ free((*Client)->ipa_text);
+
+ free(*Client);
+ *Client = NULL;
+}
static void
Generate_MyToken( CLIENT *Client )
{
if( c->mytoken == token )
{
- /* Das Token wurde bereits vergeben */
+ /* The token is already in use */
token++;
c = My_Clients;
continue;
/**
* Introduce a new user or service client to a remote server.
*
- * This function differentiates between RFC1459 and RFC2813 server links and
- * generates the appropriate commands to register the new user or service.
- *
* @param To The remote server to inform.
* @param Prefix Prefix for the generated commands.
* @param data CLIENT structure of the new client.
cb_introduceClient(CLIENT *To, CLIENT *Prefix, void *data)
{
CLIENT *c = (CLIENT *)data;
+
+ (void)Client_Announce(To, Prefix, c);
+
+} /* cb_introduceClient */
+
+
+/**
+ * Announce an user or service to a server.
+ *
+ * This function differentiates between RFC1459 and RFC2813 server links and
+ * generates the appropriate commands to register the user or service.
+ *
+ * @param Client Server
+ * @param Prefix Prefix for the generated commands
+ * @param User User to announce
+ */
+GLOBAL bool
+Client_Announce(CLIENT * Client, CLIENT * Prefix, CLIENT * User)
+{
CONN_ID conn;
char *modes, *user, *host;
- modes = Client_Modes(c);
- user = Client_User(c) ? Client_User(c) : "-";
- host = Client_Hostname(c) ? Client_Hostname(c) : "-";
+ modes = Client_Modes(User);
+ user = Client_User(User) ? Client_User(User) : "-";
+ host = Client_Hostname(User) ? Client_Hostname(User) : "-";
- conn = Client_Conn(To);
+ conn = Client_Conn(Client);
if (Conn_Options(conn) & CONN_RFC1459) {
/* RFC 1459 mode: separate NICK and USER commands */
- Conn_WriteStr(conn, "NICK %s :%d", Client_ID(c),
- Client_Hops(c) + 1);
- Conn_WriteStr(conn, ":%s USER %s %s %s :%s",
- Client_ID(c), user, host,
- Client_ID(Client_Introducer(c)), Client_Info(c));
- if (modes[0])
- Conn_WriteStr(conn, ":%s MODE %s +%s",
- Client_ID(c), Client_ID(c), modes);
+ if (! Conn_WriteStr(conn, "NICK %s :%d",
+ Client_ID(User), Client_Hops(User) + 1))
+ return DISCONNECTED;
+ if (! Conn_WriteStr(conn, ":%s USER %s %s %s :%s",
+ Client_ID(User), user, host,
+ Client_ID(Client_Introducer(User)),
+ Client_Info(User)))
+ return DISCONNECTED;
+ if (modes[0]) {
+ if (! Conn_WriteStr(conn, ":%s MODE %s +%s",
+ Client_ID(User), Client_ID(User),
+ modes))
+ return DISCONNECTED;
+ }
} else {
/* RFC 2813 mode: one combined NICK or SERVICE command */
- if (Client_Type(c) == CLIENT_SERVICE
- && strchr(Client_Flags(To), 'S'))
- IRC_WriteStrClientPrefix(To, Prefix,
- "SERVICE %s %d * +%s %d :%s",
- Client_Mask(c),
- Client_MyToken(Client_Introducer(c)),
- Client_Modes(c), Client_Hops(c) + 1,
- Client_Info(c));
- else
- IRC_WriteStrClientPrefix(To, Prefix,
- "NICK %s %d %s %s %d +%s :%s",
- Client_ID(c), Client_Hops(c) + 1,
- user, host,
- Client_MyToken(Client_Introducer(c)),
- modes, Client_Info(c));
+ if (Client_Type(User) == CLIENT_SERVICE
+ && Client_HasFlag(Client, 'S')) {
+ if (!IRC_WriteStrClientPrefix(Client, Prefix,
+ "SERVICE %s %d * +%s %d :%s",
+ Client_Mask(User),
+ Client_MyToken(Client_Introducer(User)),
+ modes, Client_Hops(User) + 1,
+ Client_Info(User)))
+ return DISCONNECTED;
+ } else {
+ if (!IRC_WriteStrClientPrefix(Client, Prefix,
+ "NICK %s %d %s %s %d +%s :%s",
+ Client_ID(User), Client_Hops(User) + 1,
+ user, host,
+ Client_MyToken(Client_Introducer(User)),
+ modes, Client_Info(User)))
+ return DISCONNECTED;
+ }
}
-} /* cb_introduceClient */
+
+ if (Client_HasFlag(Client, 'M')) {
+ /* Synchronize metadata */
+ if (Client_HostnameCloaked(User)) {
+ if (!IRC_WriteStrClientPrefix(Client, Prefix,
+ "METADATA %s cloakhost :%s",
+ Client_ID(User),
+ Client_HostnameCloaked(User)))
+ return DISCONNECTED;
+ }
+
+ if (Client_AccountName(User)) {
+ if (!IRC_WriteStrClientPrefix(Client, Prefix,
+ "METADATA %s accountname :%s",
+ Client_ID(User),
+ Client_AccountName(User)))
+ return DISCONNECTED;
+ }
+
+ if (Conn_GetCertFp(Client_Conn(User))) {
+ if (!IRC_WriteStrClientPrefix(Client, Prefix,
+ "METADATA %s certfp :%s",
+ Client_ID(User),
+ Conn_GetCertFp(Client_Conn(User))))
+ return DISCONNECTED;
+ }
+ }
+
+ return CONNECTED;
+} /* Client_Announce */
#ifdef DEBUG