+Sun Dec 2 13:48:46 EET 2001 Pekka Riikonen <priikone@silcnet.org>
+
+ * Distribute to the channel passphrase in CMODE_CHANGE notify.
+ Updated specs and implemented it. Affected file silcd/command.c,
+ silcd/packet_send.c and silcd/packet_receive.c.
+
+ * Implemented the <founder auth> payload handling in the JOIN
+ command. If provided all conditions for channel joining
+ except requirement to provide correct passphrase can be
+ overrided by the channel founder. Updated the protocol specs.
+ Affected file silcd/command.c.
+
+ Added support for founder auth in JOIN command in client
+ library. Fixed the parsing of the JOIN command now to support
+ all options as they should be. The affected file is
+ lib/silcclient/command.c.
+
+ * Optimized the WHOIS and IDENTIFY commands to send the request
+ to router only if it includes nicknames or other names. If
+ they include only IDs then check the local cache first before
+ routing. Affected file is silcd/command.c.
+
+ * Added channels topic announcements. Affected file is
+ silcd/packet_receive.c and silcd/server.c.
+
+ * Fixed the silc_server_send_notify_topic_set to really destine
+ the packet to channel. Affected file silcd/packet_send.c.
+
+ * Fixed a crash in CHANNEL_CHANGE notify handling in the client
+ library. Affected file lib/silcclient/client_notify.c.
+
+ * Added UMODE announcements. Affected file silcd/server.c.
+
Sat Dec 1 12:52:39 EET 2001 Pekka Riikonen <priikone@silcnet.org>
* Memory leak fixes in:
that the user has. And a local command to dump the contents of the
public key to the screen. Something like LISTKEYS, SHOWKEY...
- o The JOIN command's HELP is generated from Irssi IRCs JOIN help and
- the syntax is not same in SILC. This must be fixed. Most likely
- we must forget the Irssi's JOIN command and mimic it to get our
- required syntax for it too.
-
o We should get rid of the clientconfig.[ch] in Irssi SILC and move the
cipher, hash, hmac and pkcs configuration to the Irssi SILC's config
file.
TODO/bugs In SILC Client Library
================================
- o JOIN command's argument handling is buggy. See the XXX in the code.
+ o N/A
TODO/bugs In SILC Server
========================
- o XXXXXXXXX cannot join +a channel from other servers
-
- o Implement the <founder auth> and founder privileges gaining to
- the JOIN command. This will bypass invite-only mode as well for
- the client who got the founder mode during JOIN.
-
- o Optimize the WHOIS and IDENTIFY commands to check if the request
- includes an ID or multiple IDs, then they are checked from local cache
- first, and not sent directly to router. This is because if they
- are found in the local cache there's no need to send them to the
- router. Only if some ID is not found, or an found entry is
- incomplete it can be queried from the router. This way these
- commands become faster, and for example JOIN command becomes a lot
- faster since the server ends up resolving the same information only
- once, as opposed to resolve it everytime JOIN command is issued, like
- it does now.
-
- The same thing can be done with WHOWAS command as well.
-
- It is important to send these requests to router only if they can be
- expanded to many results (as when doing WHOIS for nickname). If
- they are explicit (like requesting by ID) the local cache MUST be
- searched before sending it to router.
-
- o Announcements are incomplete: channel topics are not announced,
- user modes (UMODE) are not announced.
-
- o Change the server to connect to another server from low ports (706)
- and not from high ports. Currently we cannot do incoming connection
- checking by remote port because the port is not fixed.
-
o Backup router related issues
- o After backup resume protocol the TOPIC_SET was not handled
- correctly by all (unknown Channel ID).
-
o Channel user mode changes are notified unnecessarely when
switching to backup router on router crash.
servers = (
{ address = "silc.silcnet.org"; chatnet = SILCNet; port = 706; }
+ { address = "silc.ytti.fi"; chatnet = SILCNet; port = 706; }
+ { address = "silc.peelo.com"; chatnet = SILCNet; port = 706; }
+ { address = "silc.silcnet.org"; chatnet = SILCNet; port = 707; }
);
chatnets = {
Joins a specified channel. Channel names usually begin with #-sign,
but note that the #-sign is not mandatory in channel names.
+If -cipher is provided and the channel does not exist the cipher to
+secure the channel messages on the channel will be set to <cipher>. If
+the -hmac is provided and the channel does not exist the hmac to
+secure the channel messages on the channel will be set to <hmac>.
+
+If -founder is provided, and the channel's mode includes founder mode
+it is possible to gain channel founder privileges at the same time
+joining the channel. If the channel has user limit, active bans,
+or is invite-only channel the founder can override these conditions
+and join the channel. Only the client who set the founder mode on the
+channel is able to use -founder option.
+
JOIN is aliased to J by default.
Description
-See also: LEAVE, WINDOW CLOSE
+See also: LEAVE, WINDOW CLOSE, CMODE, CUMODE
static void event_topic(SILC_SERVER_REC *server, va_list va)
{
SILC_CHANNEL_REC *chanrec;
+ void *entry;
SilcClientEntry client;
+ SilcServerEntry server_entry;
SilcChannelEntry channel;
char *topic;
char userhost[256];
+ SilcIdType idtype;
- client = va_arg(va, SilcClientEntry);
+ idtype = va_arg(va, int);
+ entry = va_arg(va, void *);
topic = va_arg(va, char *);
channel = va_arg(va, SilcChannelEntry);
- silc_server_free_ftp(server, client);
-
chanrec = silc_channel_find_entry(server, channel);
if (chanrec != NULL) {
g_free_not_null(chanrec->topic);
signal_emit("channel topic changed", 1, chanrec);
}
- memset(userhost, 0, sizeof(userhost));
- snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
- client->username, client->hostname);
- signal_emit("message topic", 5, server, channel->channel_name,
- topic, client->nickname, userhost);
+ if (idtype == SILC_ID_CLIENT) {
+ client = (SilcClientEntry)entry;
+ memset(userhost, 0, sizeof(userhost));
+ snprintf(userhost, sizeof(userhost) - 1, "%s@%s",
+ client->username, client->hostname);
+ signal_emit("message topic", 5, server, channel->channel_name,
+ topic, client->nickname, userhost);
+ } else if (idtype == SILC_ID_SERVER) {
+ server_entry = (SilcServerEntry)entry;
+ signal_emit("message topic", 5, server, channel->channel_name,
+ topic, server_entry->server_name,
+ server_entry->server_name);
+ } else {
+ channel = (SilcChannelEntry)entry;
+ signal_emit("message topic", 5, server, channel->channel_name,
+ topic, channel->channel_name, channel->channel_name);
+ }
}
/*
clients[i]->username ? userhost : "",
"server signoff");
+ silc_server_free_ftp(server, clients[i]);
+
nicks = nicklist_get_same_unique(SERVER(server), clients[i]);
for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
CHANNEL_REC *channel = tmp->data;
/* SYNTAX: FILE RECEIVE [<nickname>] */
/* SYNTAX: FILE CLOSE [<nickname>] */
/* SYNTAX: FILE */
+/* SYNTAX: JOIN <channel> [<passphrase>] [-cipher <cipher>] [-hmac <hmac>] [-founder <-pubkey|passwd>] */
void silc_command_exec(SILC_SERVER_REC *server,
const char *command, const char *args)
g_free(tmpcmd);
if (cmd == NULL)
return;
-
+
/* Now parse all arguments */
data = g_strconcat(command, " ", args, NULL);
silc_parse_command_line(data, &argv, &argv_lens,
int i, ret = 0;
bool check_global = FALSE;
- /* Protocol dictates that we must always send the received WHOIS request
- to our router if we are normal server, so let's do it now unless we
- are standalone. We will not send any replies to the client until we
- have received reply from the router. */
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+ /* Parse the whois request */
+ if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
+ &nick, &server_name, &count,
+ SILC_COMMAND_WHOIS))
+ return 0;
+
+ /* Send the WHOIS request to the router only if it included nickname.
+ Since nicknames can be expanded into many clients we need to send it
+ to router. If the WHOIS included only client ID's we will check them
+ first locally since we just might have them. */
+ if (nick && !client_id_count &&
+ cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
server->server_type == SILC_SERVER && !cmd->pending &&
!server->standalone) {
SilcBuffer tmpbuf;
goto out;
}
- /* We are ready to process the command request. Let's search for the
- requested client and send reply to the requesting client. */
-
if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT)
check_global = TRUE;
else if (server->server_type != SILC_SERVER)
check_global = TRUE;
- /* Parse the whois request */
- if (!silc_server_command_whois_parse(cmd, &client_id, &client_id_count,
- &nick, &server_name, &count,
- SILC_COMMAND_WHOIS))
- return 0;
-
/* Get all clients matching that ID or nickname from local list */
if (client_id_count) {
/* Check all Client ID's received in the command packet */
uint32 *servers_count,
SilcChannelEntry **channels,
uint32 *channels_count,
- uint32 *count)
+ uint32 *count,
+ bool *names)
{
SilcServer server = cmd->server;
unsigned char *tmp;
tmp = silc_argument_get_arg_type(cmd->args, 5, &len);
if (!tmp) {
/* No ID, get the names. */
+ *names = TRUE;
/* Try to get nickname@server. */
tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
SilcServerEntry *servers = NULL;
SilcChannelEntry *channels = NULL;
uint32 clients_count = 0, servers_count = 0, channels_count = 0;
+ bool names;
- /* Protocol dictates that we must always send the received IDENTIFY request
- to our router if we are normal server, so let's do it now unless we
- are standalone. We will not send any replies to the client until we
- have received reply from the router. */
- if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
+ /* Parse the IDENTIFY request */
+ if (!silc_server_command_identify_parse(cmd,
+ &clients, &clients_count,
+ &servers, &servers_count,
+ &channels, &channels_count,
+ &count, &names))
+ return 0;
+
+ /* Send the IDENTIFY request to the router only if it included nickname.
+ Since nicknames can be expanded into many clients we need to send it
+ to router. If the IDENTIFY included only client ID's we will check them
+ first locally since we just might have them. */
+ if (names && cmd->sock->type == SILC_SOCKET_TYPE_CLIENT &&
server->server_type == SILC_SERVER && !cmd->pending &&
!server->standalone) {
SilcBuffer tmpbuf;
goto out;
}
- /* We are ready to process the command request. Let's search for the
- requested client and send reply to the requesting client. */
-
- /* Parse the IDENTIFY request */
- if (!silc_server_command_identify_parse(cmd,
- &clients, &clients_count,
- &servers, &servers_count,
- &channels, &channels_count,
- &count))
- return 0;
-
/* Check that all mandatory fields are present and request those data
from the server who owns the client if necessary. */
if (clients && !silc_server_command_identify_check_client(cmd, clients,
if (!server->standalone)
silc_server_send_notify_topic_set(server, server->router->connection,
server->server_type == SILC_ROUTER ?
- TRUE : FALSE, channel, client->id,
+ TRUE : FALSE, channel,
+ client->id, SILC_ID_CLIENT,
channel->topic);
idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
/* Send notify about topic change to all clients on the channel */
- silc_server_send_notify_to_channel(server, NULL, channel, TRUE,
+ silc_server_send_notify_to_channel(server, NULL, channel, TRUE,
SILC_NOTIFY_TYPE_TOPIC_SET, 2,
idp->data, idp->len,
channel->topic, strlen(channel->topic));
SilcClientID *client_id,
bool created,
bool create_key,
- uint32 umode)
+ uint32 umode,
+ const unsigned char *auth,
+ uint32 auth_len)
{
SilcSocketConnection sock = cmd->sock;
unsigned char *tmp;
SilcBuffer reply, chidp, clidp, keyp = NULL, user_list, mode_list;
uint16 ident = silc_command_get_ident(cmd->payload);
char check[512], check2[512];
+ bool founder = FALSE;
SILC_LOG_DEBUG(("Start"));
}
/*
- * Check channel modes
+ * Check founder auth payload if provided. If client can gain founder
+ * privileges it can override various conditions on joining the channel,
+ * and can have directly the founder mode set on the channel.
*/
-
- memset(check, 0, sizeof(check));
- memset(check2, 0, sizeof(check2));
- strncat(check, client->nickname, strlen(client->nickname));
- strncat(check, "!", 1);
- strncat(check, client->username, strlen(client->username));
- if (!strchr(client->username, '@')) {
- strncat(check, "@", 1);
- strncat(check, cmd->sock->hostname, strlen(cmd->sock->hostname));
+ if (auth && auth_len && channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+ SilcIDListData idata = (SilcIDListData)client;
+
+ if (channel->founder_key && idata->public_key &&
+ silc_pkcs_public_key_compare(channel->founder_key,
+ idata->public_key)) {
+ void *auth_data = (channel->founder_method == SILC_AUTH_PASSWORD ?
+ (void *)channel->founder_passwd :
+ (void *)channel->founder_key);
+ uint32 auth_data_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
+ channel->founder_passwd_len : 0);
+
+ /* Check whether the client is to become founder */
+ if (silc_auth_verify_data(auth, auth_len, channel->founder_method,
+ auth_data, auth_data_len,
+ idata->hash, client->id, SILC_ID_CLIENT)) {
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ founder = TRUE;
+ }
+ }
}
- strncat(check2, client->nickname, strlen(client->nickname));
- if (!strchr(client->nickname, '@')) {
- strncat(check2, "@", 1);
- strncat(check2, server->server_name, strlen(server->server_name));
- }
- strncat(check2, "!", 1);
- strncat(check2, client->username, strlen(client->username));
- if (!strchr(client->username, '@')) {
- strncat(check2, "@", 1);
- strncat(check2, cmd->sock->hostname, strlen(cmd->sock->hostname));
- }
+ /*
+ * Check channel modes
+ */
- /* Check invite list if channel is invite-only channel */
- if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
- if (!channel->invite_list ||
- (!silc_string_match(channel->invite_list, check) &&
- !silc_string_match(channel->invite_list, check2))) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
- SILC_STATUS_ERR_NOT_INVITED);
- goto out;
+ if (!umode) {
+ memset(check, 0, sizeof(check));
+ memset(check2, 0, sizeof(check2));
+ strncat(check, client->nickname, strlen(client->nickname));
+ strncat(check, "!", 1);
+ strncat(check, client->username, strlen(client->username));
+ if (!strchr(client->username, '@')) {
+ strncat(check, "@", 1);
+ strncat(check, cmd->sock->hostname, strlen(cmd->sock->hostname));
}
- }
- /* Check ban list if it exists. If the client's nickname, server,
- username and/or hostname is in the ban list the access to the
- channel is denied. */
- if (channel->ban_list) {
- if (!channel->ban_list ||
- silc_string_match(channel->ban_list, check) ||
- silc_string_match(channel->ban_list, check2)) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
- SILC_STATUS_ERR_BANNED_FROM_CHANNEL);
- goto out;
+ strncat(check2, client->nickname, strlen(client->nickname));
+ if (!strchr(client->nickname, '@')) {
+ strncat(check2, "@", 1);
+ strncat(check2, server->server_name, strlen(server->server_name));
+ }
+ strncat(check2, "!", 1);
+ strncat(check2, client->username, strlen(client->username));
+ if (!strchr(client->username, '@')) {
+ strncat(check2, "@", 1);
+ strncat(check2, cmd->sock->hostname, strlen(cmd->sock->hostname));
+ }
+
+ /* Check invite list if channel is invite-only channel */
+ if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
+ if (!channel->invite_list ||
+ (!silc_string_match(channel->invite_list, check) &&
+ !silc_string_match(channel->invite_list, check2))) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NOT_INVITED);
+ goto out;
+ }
}
- }
- /* Get passphrase */
- tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
- if (tmp) {
- passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
- memcpy(passphrase, tmp, tmp_len);
+ /* Check ban list if it exists. If the client's nickname, server,
+ username and/or hostname is in the ban list the access to the
+ channel is denied. */
+ if (channel->ban_list) {
+ if (!channel->ban_list ||
+ silc_string_match(channel->ban_list, check) ||
+ silc_string_match(channel->ban_list, check2)) {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_BANNED_FROM_CHANNEL);
+ goto out;
+ }
+ }
+
+ /* Check user count limit if set. */
+ if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
+ if (silc_hash_table_count(channel->user_list) + 1 >
+ channel->user_limit) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_CHANNEL_IS_FULL);
+ goto out;
+ }
+ }
}
-
+
/* Check the channel passphrase if set. */
if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+ /* Get passphrase */
+ tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+ if (tmp) {
+ passphrase = silc_calloc(tmp_len, sizeof(*passphrase));
+ memcpy(passphrase, tmp, tmp_len);
+ }
+
if (!passphrase || !channel->passphrase ||
memcmp(channel->passphrase, passphrase,
strlen(channel->passphrase))) {
}
}
- /* Check user count limit if set. */
- if (channel->mode & SILC_CHANNEL_MODE_ULIMIT) {
- if (silc_hash_table_count(channel->user_list) + 1 >
- channel->user_limit) {
- silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
- SILC_STATUS_ERR_CHANNEL_IS_FULL);
- goto out;
- }
- }
-
/*
* Client is allowed to join to the channel. Make it happen.
*/
we'll ignore it (in packet_receive.c) so we must send it here. If
we are router then this will send it to local clients and local
servers. */
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
SILC_NOTIFY_TYPE_JOIN, 2,
clidp->data, clidp->len,
chidp->data, chidp->len);
/* Distribute the channel key to all backup routers. */
silc_server_backup_send(server, NULL, SILC_PACKET_CHANNEL_KEY, 0,
keyp->data, keyp->len, FALSE, TRUE);
+
+ /* If client became founder by providing correct founder auth data
+ notify the mode change to the channel. */
+ if (founder) {
+ SILC_PUT32_MSB(chl->mode, mode);
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+ SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
+ clidp->data, clidp->len,
+ mode, 4, clidp->data, clidp->len);
+
+ /* Set CUMODE notify type to network */
+ if (!server->standalone)
+ silc_server_send_notify_cumode(server, server->router->connection,
+ server->server_type == SILC_ROUTER ?
+ TRUE : FALSE, channel,
+ chl->mode, client->id, SILC_ID_CLIENT,
+ client->id);
+ }
}
silc_buffer_free(reply);
{
SilcServerCommandContext cmd = (SilcServerCommandContext)context;
SilcServer server = cmd->server;
- uint32 tmp_len;
+ unsigned char *auth;
+ uint32 tmp_len, auth_len;
char *tmp, *channel_name = NULL, *cipher, *hmac;
SilcChannelEntry channel;
uint32 umode = 0;
bool created = FALSE, create_key = TRUE;
SilcClientID *client_id;
- SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_JOIN, cmd, 1, 4);
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_JOIN, cmd, 2, 6);
/* Get channel name */
tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
goto out;
}
- /* Get cipher and hmac name */
+ /* Get cipher, hmac name and auth payload */
cipher = silc_argument_get_arg_type(cmd->args, 4, NULL);
hmac = silc_argument_get_arg_type(cmd->args, 5, NULL);
+ auth = silc_argument_get_arg_type(cmd->args, 6, &auth_len);
/* See if the channel exists */
channel = silc_idlist_find_channel_by_name(server->local_list,
/* Join to the channel */
silc_server_command_join_channel(server, cmd, channel, client_id,
- created, create_key, umode);
+ created, create_key, umode,
+ auth, auth_len);
silc_free(client_id);
SilcChannelClientEntry chl;
SilcBuffer packet, cidp;
unsigned char *tmp, *tmp_id, *tmp_mask;
- char *cipher = NULL, *hmac = NULL;
+ char *cipher = NULL, *hmac = NULL, *passphrase = NULL;
uint32 mode_mask, tmp_len, tmp_len2;
uint16 ident = silc_command_get_ident(cmd->payload);
}
/* Save the passphrase */
- channel->passphrase = strdup(tmp);
+ passphrase = channel->passphrase = strdup(tmp);
}
} else {
if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
/* Finally, set the mode */
channel->mode = mode_mask;
- /* Send CMODE_CHANGE notify */
+ /* Send CMODE_CHANGE notify. */
cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
- SILC_NOTIFY_TYPE_CMODE_CHANGE, 4,
+ SILC_NOTIFY_TYPE_CMODE_CHANGE, 5,
cidp->data, cidp->len,
tmp_mask, 4,
cipher, cipher ? strlen(cipher) : 0,
- hmac, hmac ? strlen(hmac) : 0);
+ hmac, hmac ? strlen(hmac) : 0,
+ passphrase, passphrase ?
+ strlen(passphrase) : 0);
/* Set CMODE notify type to network */
if (!server->standalone)
server->server_type == SILC_ROUTER ?
TRUE : FALSE, channel,
mode_mask, client->id, SILC_ID_CLIENT,
- cipher, hmac);
+ cipher, hmac, passphrase);
/* Send command reply to sender */
packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
/* Send notify to channel, notify only if mode was actually changed. */
if (notify) {
- silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
idp->data, idp->len,
tmp_mask, 4,
goto out;
}
- if (channel->topic)
- silc_free(channel->topic);
- channel->topic = silc_calloc(tmp_len + 1, sizeof(*channel->topic));
- memcpy(channel->topic, tmp, tmp_len);
+ silc_free(channel->topic);
+ channel->topic = strdup(tmp);
/* Send the same notify to the channel */
silc_server_packet_send_to_channel(server, sock, channel, packet->type,
memset(hash, 0, sizeof(hash));
}
+ /* Get the passphrase */
+ tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
+ if (tmp) {
+ silc_free(channel->passphrase);
+ channel->passphrase = strdup(tmp);
+ }
+
break;
case SILC_NOTIFY_TYPE_CUMODE_CHANGE:
users_modes->len, FALSE);
silc_buffer_free(users_modes);
}
+
+ /* Re-announce channel's topic */
+ if (channel->topic) {
+ silc_server_send_notify_topic_set(server, sock,
+ server->server_type == SILC_ROUTER ?
+ TRUE : FALSE, channel,
+ channel->id, SILC_ID_CHANNEL,
+ channel->topic);
+ }
}
silc_free(channel_id);
silc_server_send_notify_cmode(server, sock, FALSE, channel,
channel->mode, server->id,
SILC_ID_SERVER,
- channel->cipher, channel->hmac_name);
+ channel->cipher, channel->hmac_name,
+ channel->passphrase);
}
/* Create new key for the channel and send it to the server and
If `route' is FALSE then the packet is sent only locally and will not
be routed anywhere (for router locally means cell wide). If `sender'
is provided then the packet is not sent to that connection since it
- originally came from it. */
+ originally came from it. If `send_to_clients' is FALSE then the
+ packet is not sent clients, only servers. */
void silc_server_packet_send_to_channel(SilcServer server,
SilcSocketConnection sender,
idata = (SilcIDListData)router;
if (sock != sender) {
- SILC_LOG_DEBUG(("Sending channel message to router for routing"));
+ SILC_LOG_DEBUG(("Sending packet to router for routing"));
silc_server_packet_send_to_channel_real(server, sock, &packetdata,
idata->send_key,
SilcChannelEntry channel,
uint32 mode_mask,
void *id, SilcIdType id_type,
- char *cipher, char *hmac)
+ char *cipher, char *hmac,
+ char *passphrase)
{
SilcBuffer idp;
unsigned char mode[4];
silc_server_send_notify_dest(server, sock, broadcast, (void *)channel->id,
SILC_ID_CHANNEL, SILC_NOTIFY_TYPE_CMODE_CHANGE,
- 4, idp->data, idp->len,
+ 5, idp->data, idp->len,
mode, 4,
cipher, cipher ? strlen(cipher) : 0,
- hmac, hmac ? strlen(hmac) : 0);
+ hmac, hmac ? strlen(hmac) : 0,
+ passphrase, passphrase ?
+ strlen(passphrase) : 0);
silc_buffer_free(idp);
}
/* Sends CUMODE_CHANGE notify type. This tells that `client_id' changed the
- `target' client's mode on `channel'. The Notify packet is always
+ `target' client's mode on `channel'. The notify packet is always
destined to the channel. */
void silc_server_send_notify_cumode(SilcServer server,
silc_buffer_free(idp);
}
-/* Sends TOPIC_SET notify type. This tells that `client_id' changed
+/* Sends TOPIC_SET notify type. This tells that `id' changed
the `channel's topic to `topic'. The Notify packet is always destined
to the channel. This function is used to send the topic set notifies
between routers. */
SilcSocketConnection sock,
bool broadcast,
SilcChannelEntry channel,
- SilcClientID *client_id,
+ void *id, SilcIdType id_type,
char *topic)
{
SilcBuffer idp;
- idp = silc_id_payload_encode((void *)client_id, SILC_ID_CLIENT);
- silc_server_send_notify(server, sock, broadcast,
- SILC_NOTIFY_TYPE_TOPIC_SET,
- topic ? 2 : 1,
- idp->data, idp->len,
- topic, topic ? strlen(topic) : 0);
+ idp = silc_id_payload_encode(id, id_type);
+ silc_server_send_notify_dest(server, sock, broadcast,
+ (void *)channel->id, SILC_ID_CHANNEL,
+ SILC_NOTIFY_TYPE_TOPIC_SET,
+ topic ? 2 : 1,
+ idp->data, idp->len,
+ topic, topic ? strlen(topic) : 0);
silc_buffer_free(idp);
}
void silc_server_send_notify_to_channel(SilcServer server,
SilcSocketConnection sender,
SilcChannelEntry channel,
- unsigned char route_notify,
+ bool route_notify,
SilcNotifyType type,
uint32 argc, ...)
{
channel->key_len / 8, channel->key);
silc_server_packet_send_to_channel(server, sender, channel,
SILC_PACKET_CHANNEL_KEY,
- route, packet->data, packet->len, FALSE);
+ route, packet->data, packet->len,
+ FALSE);
silc_buffer_free(packet);
silc_free(chid);
}
SilcChannelEntry channel,
uint32 mode_mask,
void *id, SilcIdType id_type,
- char *cipher, char *hmac);
+ char *cipher, char *hmac,
+ char *passphrase);
void silc_server_send_notify_cumode(SilcServer server,
SilcSocketConnection sock,
bool broadcast,
SilcSocketConnection sock,
bool broadcast,
SilcChannelEntry channel,
- SilcClientID *client_id,
+ void *id, SilcIdType id_type,
char *topic);
void silc_server_send_notify_kicked(SilcServer server,
SilcSocketConnection sock,
void silc_server_send_notify_to_channel(SilcServer server,
SilcSocketConnection sender,
SilcChannelEntry channel,
- unsigned char route_notify,
+ bool route_notify,
SilcNotifyType type,
uint32 argc, ...);
void silc_server_send_notify_on_channels(SilcServer server,
}
}
+static SilcBuffer
+silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
+{
+ va_list ap;
+ SilcBuffer p;
+
+ va_start(ap, argc);
+ p = silc_notify_payload_encode(notify, argc, ap);
+ va_end(ap);
+
+ return p;
+}
+
/* This function is used by router to announce existing servers to our
primary router when we've connected to it. If `creation_time' is non-zero
then only the servers that has been created after the `creation_time'
static void silc_server_announce_get_clients(SilcServer server,
SilcIDList id_list,
SilcBuffer *clients,
+ SilcBuffer *umodes,
unsigned long creation_time)
{
SilcIDCacheList list;
SilcIDCacheEntry id_cache;
SilcClientEntry client;
SilcBuffer idp;
+ SilcBuffer tmp;
+ unsigned char mode[4];
/* Go through all clients in the list */
if (silc_idcache_get_all(id_list->clients, &list)) {
silc_buffer_pull_tail(*clients, ((*clients)->end - (*clients)->data));
silc_buffer_put(*clients, idp->data, idp->len);
silc_buffer_pull(*clients, idp->len);
+
+ SILC_PUT32_MSB(client->mode, mode);
+ tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_UMODE_CHANGE,
+ 2, idp->data, idp->len,
+ mode, 4);
+ *umodes = silc_buffer_realloc(*umodes,
+ (*umodes ?
+ (*umodes)->truelen + tmp->len :
+ tmp->len));
+ silc_buffer_pull_tail(*umodes, ((*umodes)->end - (*umodes)->data));
+ silc_buffer_put(*umodes, tmp->data, tmp->len);
+ silc_buffer_pull(*umodes, tmp->len);
+ silc_buffer_free(tmp);
+
silc_buffer_free(idp);
if (!silc_idcache_list_next(list, &id_cache))
SilcSocketConnection remote)
{
SilcBuffer clients = NULL;
+ SilcBuffer umodes = NULL;
SILC_LOG_DEBUG(("Announcing clients"));
/* Get clients in local list */
silc_server_announce_get_clients(server, server->local_list,
- &clients, creation_time);
+ &clients, &umodes, creation_time);
/* As router we announce our global list as well */
if (server->server_type == SILC_ROUTER)
silc_server_announce_get_clients(server, server->global_list,
- &clients, creation_time);
+ &clients, &umodes, creation_time);
if (clients) {
silc_buffer_push(clients, clients->data - clients->head);
silc_buffer_free(clients);
}
+
+ if (umodes) {
+ silc_buffer_push(umodes, umodes->data - umodes->head);
+ SILC_LOG_HEXDUMP(("umodes"), umodes->data, umodes->len);
+
+ /* Send the packet */
+ silc_server_packet_send(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ umodes->data, umodes->len, TRUE);
+
+ silc_buffer_free(umodes);
+ }
}
-static SilcBuffer
-silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
+/* Returns channel's topic for announcing it */
+
+void silc_server_announce_get_channel_topic(SilcServer server,
+ SilcChannelEntry channel,
+ SilcBuffer *topic)
{
- va_list ap;
- SilcBuffer p;
+ SilcBuffer chidp;
- va_start(ap, argc);
- p = silc_notify_payload_encode(notify, argc, ap);
- va_end(ap);
-
- return p;
+ if (channel->topic) {
+ chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ *topic = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_TOPIC_SET, 2,
+ chidp->data, chidp->len,
+ channel->topic,
+ strlen(channel->topic));
+ silc_buffer_free(chidp);
+ }
}
/* Returns assembled packets for channel users of the `channel'. */
SilcBuffer *channel_users,
SilcBuffer **channel_users_modes,
uint32 *channel_users_modes_c,
+ SilcBuffer **channel_topics,
SilcChannelID ***channel_ids,
unsigned long creation_time)
{
silc_buffer_pull(*channels, len);
}
+ /* Channel user modes */
*channel_users_modes = silc_realloc(*channel_users_modes,
sizeof(**channel_users_modes) *
(i + 1));
channel_users,
&(*channel_users_modes)[i]);
(*channel_ids)[i] = channel->id;
+
+ /* Channel's topic */
+ *channel_topics = silc_realloc(*channel_topics,
+ sizeof(**channel_topics) * (i + 1));
+ (*channel_topics)[i] = NULL;
+ silc_server_announce_get_channel_topic(server, channel,
+ &(*channel_topics)[i]);
i++;
if (!silc_idcache_list_next(list, &id_cache))
{
SilcBuffer channels = NULL, channel_users = NULL;
SilcBuffer *channel_users_modes = NULL;
+ SilcBuffer *channel_topics = NULL;
uint32 channel_users_modes_c = 0;
SilcChannelID **channel_ids = NULL;
&channels, &channel_users,
&channel_users_modes,
&channel_users_modes_c,
+ &channel_topics,
&channel_ids, creation_time);
/* Get channels and channel users in global list */
&channels, &channel_users,
&channel_users_modes,
&channel_users_modes_c,
+ &channel_topics,
&channel_ids, creation_time);
if (channels) {
silc_buffer_free(channel_users_modes[i]);
}
silc_free(channel_users_modes);
- silc_free(channel_ids);
}
+
+ if (channel_topics) {
+ int i;
+
+ for (i = 0; i < channel_users_modes_c; i++) {
+ if (!channel_topics[i])
+ continue;
+
+ silc_buffer_push(channel_topics[i],
+ channel_topics[i]->data -
+ channel_topics[i]->head);
+ SILC_LOG_HEXDUMP(("channel topic"), channel_topics[i]->data,
+ channel_topics[i]->len);
+ silc_server_packet_send_dest(server, remote,
+ SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+ channel_ids[i], SILC_ID_CHANNEL,
+ channel_topics[i]->data,
+ channel_topics[i]->len,
+ FALSE);
+ silc_buffer_free(channel_topics[i]);
+ }
+ silc_free(channel_topics);
+ }
+
+ silc_free(channel_ids);
}
/* Failure timeout callback. If this is called then we will immediately
SilcChannelEntry channel);
void silc_server_perform_heartbeat(SilcSocketConnection sock,
void *hb_context);
+void silc_server_announce_get_channel_topic(SilcServer server,
+ SilcChannelEntry channel,
+ SilcBuffer *topic);
void silc_server_announce_get_channel_users(SilcServer server,
SilcChannelEntry channel,
SilcBuffer *channel_users,
SilcBuffer *channel_users,
SilcBuffer **channel_users_modes,
uint32 *channel_users_modes_c,
+ SilcBuffer **channel_topics,
SilcChannelID ***channel_ids,
unsigned long creation_time);
void silc_server_announce_servers(SilcServer server, bool global,
privileges the same way as the client had given the
SILC_COMMAND_CUMODE command to gain founder privileges. The
client is still able to join the channel even if the founder
- privileges could not be gained. If the channel is invite only
- channel, the client is able to join the channel only if it
- was invited to the channel or was able to gain the founder
- privileges.
+ privileges could not be gained.
The server MUST check whether the user is allowed to join to
the requested channel. Various modes set to the channel affect
are:
o The user MUST be invited to the channel if the channel
- is invite-only channel, or MUST provide correct
- <founder auth> payload and gain founder privileges, and
- bypass the invite-only mode.
+ is invite-only channel.
o The Client ID/nickname/username/host name MUST NOT match
any active bans.
o The user count limit, if set, MUST NOT be reached.
+ If the client provided correct <founder auth> payload it can
+ override these conditions, except the condition for the passphrase.
+ The correct passphrase MUST be provided even if <founder auth>
+ payload is provided.
+
Reply messages to the command:
Max Arguments: 14
topic was set or changed.
Max Arguments: 2
- Arguments: (1) <Client ID> (2) <topic>
+ Arguments: (1) <ID Payload> (2) <topic>
- The <Client ID> is the client which set or changed the <topic>.
+ The <ID Payload> is the ID of the entity who set the topic. It
+ usually is Client ID but it can be Server ID and Channel ID as well.
6 SILC_NOTIFY_TYPE_NICK_CHANGE
to the clients which is joined on the channel which mode was
changed.
- Max Arguments: 4
- Arguments: (1) <ID Payload> (2) <mode mask>
- (3) [<cipher>] (4) <[hmac>]
+ Max Arguments: 5
+ Arguments: (1) <ID Payload> (2) <mode mask>
+ (3) [<cipher>] (4) <[hmac>]
+ (5) [<passphrase>]
The <ID Payload> is the ID (usually Client ID but it can be
Server ID as well when the router is enforcing channel mode
ignore the <cipher> argument since the SILC_PACKET_CHANNEL_KEY
packet will force the new channel key change anyway. The <hmac>
argument is important since the client is responsible of setting
- the new HMAC and the hmac key into use.
+ the new HMAC and the hmac key into use. The <passphrase> is
+ the passphrase of the channel, if it was now set.
8 SILC_NOTIFY_TYPE_CUMODE_CHANGE
silc_schedule_task_del_by_fd(client->schedule, fd);
conn->nickname = strdup(client->username);
- conn->sock->hostname = conn->remote_host;
+ conn->sock->hostname = strdup(conn->remote_host);
conn->sock->ip = strdup(conn->remote_host);
conn->sock->port = conn->remote_port;
if (!tmp)
goto out;
- client_id = silc_id_payload_parse_id(tmp, tmp_len);
- if (!client_id)
+ idp = silc_id_payload_parse(tmp, tmp_len);
+ if (!idp)
goto out;
/* Find Client entry */
- client_entry =
- silc_client_get_client_by_id(client, conn, client_id);
- if (!client_entry)
- goto out;
+ if (silc_id_payload_get_type(idp) == SILC_ID_CLIENT) {
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id) {
+ silc_id_payload_free(idp);
+ goto out;
+ }
+
+ /* Find Client entry */
+ client_entry =
+ silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry)
+ goto out;
+ } else if (silc_id_payload_get_type(idp) == SILC_ID_SERVER) {
+ server_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!server_id) {
+ silc_id_payload_free(idp);
+ goto out;
+ }
+
+ server = silc_client_get_server_by_id(client, conn, server_id);
+ if (!server) {
+ silc_id_payload_free(idp);
+ silc_free(server_id);
+ goto out;
+ }
+
+ /* Save the pointer to the client_entry pointer */
+ client_entry = (SilcClientEntry)server;
+ silc_free(server_id);
+ } else {
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel_id) {
+ silc_id_payload_free(idp);
+ goto out;
+ }
+
+ channel = silc_client_get_channel_by_id(client, conn, channel_id);
+ if (!channel) {
+ silc_id_payload_free(idp);
+ silc_free(channel_id);
+ goto out;
+ }
+
+ /* Save the pointer to the client_entry pointer */
+ client_entry = (SilcClientEntry)channel;
+ silc_free(channel_id);
+ }
/* Get topic */
tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
/* Notify application. The channel entry is sent last as this notify
is for channel but application don't know it from the arguments
sent by server. */
- client->ops->notify(client, conn, type, client_entry, tmp, channel);
+ client->ops->notify(client, conn, type, silc_id_payload_get_type(idp),
+ client_entry, tmp, channel);
+
+ silc_id_payload_free(idp);
break;
case SILC_NOTIFY_TYPE_NICK_CHANGE:
SILC_LOG_DEBUG(("Old Channel ID id(%s)",
silc_id_render(channel->id, SILC_ID_CHANNEL)));
+ /* Remove the old channel entry */
+ silc_idcache_del_by_context(conn->channel_cache, channel);
+
/* Free the old ID */
silc_free(channel->id);
SILC_LOG_DEBUG(("New Channel ID id(%s)",
silc_id_render(channel->id, SILC_ID_CHANNEL)));
- /* Remove the old cache entry and create a new one */
- silc_idcache_del_by_context(conn->channel_cache, channel);
+ /* Add the channel entry again to ID cache */
silc_idcache_add(conn->channel_cache, channel->channel_name,
channel->id, channel, FALSE);
SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
SILC_CLIENT_CMD(oper, OPER, "OPER",
SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
- SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 5),
+ SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 9),
SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
SILC_GET16_MSB(status, silc_argument_get_arg_type(reply->args, 1, NULL));
if (status == SILC_STATUS_OK) {
/* Set the nickname */
+ silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
if (conn->nickname)
silc_free(conn->nickname);
conn->nickname = strdup(cmd->argv[1]);
conn->local_entry->nickname = conn->nickname;
- silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
silc_idcache_add(conn->client_cache, strdup(cmd->argv[1]),
conn->local_entry->id, conn->local_entry, FALSE);
COMMAND;
SilcClientCommandContext cmd = (SilcClientCommandContext)context;
SilcClientConnection conn = cmd->conn;
SilcIDCacheEntry id_cache = NULL;
- SilcBuffer buffer, idp;
+ SilcBuffer buffer, idp, auth = NULL;
+ char *name, *passphrase = NULL, *cipher = NULL, *hmac = NULL;
+ int i;
if (!cmd->conn) {
SILC_NOT_CONNECTED(cmd->client, cmd->conn);
goto out;
}
+ if (cmd->argc < 2) {
+ COMMAND_ERROR;
+ goto out;
+ }
+
/* See if we have joined to the requested channel already */
if (silc_idcache_find_by_name_one(conn->channel_cache, cmd->argv[1],
&id_cache)) {
if (cmd->argv_lens[1] > 256)
cmd->argv_lens[1] = 256;
- /* Send JOIN command to the server */
- if (cmd->argc == 2)
- buffer =
- silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
- 1, cmd->argv[1], cmd->argv_lens[1],
- 2, idp->data, idp->len);
- else if (cmd->argc == 3)
- /* XXX Buggy */
- buffer =
- silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
- 1, cmd->argv[1], cmd->argv_lens[1],
- 2, idp->data, idp->len,
- 3, cmd->argv[2], cmd->argv_lens[2]);
- else
- buffer =
- silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
- 1, cmd->argv[1], cmd->argv_lens[1],
- 2, idp->data, idp->len,
- 3, cmd->argv[2], cmd->argv_lens[2],
- 4, cmd->argv[3], cmd->argv_lens[3]);
+ name = cmd->argv[1];
+
+ for (i = 2; i < cmd->argc; i++) {
+ if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc >= i + 1) {
+ cipher = cmd->argv[i + 1];
+ i++;
+ } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc >= i + 1) {
+ hmac = cmd->argv[i + 1];
+ i++;
+ } else if (!strcasecmp(cmd->argv[i], "-founder") && cmd->argc >= i + 1) {
+ if (!strcasecmp(cmd->argv[i + 1], "-pubkey")) {
+ auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+ cmd->client->private_key,
+ conn->hash,
+ conn->local_id,
+ SILC_ID_CLIENT);
+ } else {
+ auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
+ cmd->argv[i + 1],
+ cmd->argv_lens[i + 1]);
+ }
+ i++;
+ } else {
+ passphrase = cmd->argv[i];
+ }
+ }
+ /* Send JOIN command to the server */
+ buffer =
+ silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 6,
+ 1, name, strlen(name),
+ 2, idp->data, idp->len,
+ 3, passphrase,
+ passphrase ? strlen(passphrase) : 0,
+ 4, cipher, cipher ? strlen(cipher) : 0,
+ 5, hmac, hmac ? strlen(hmac) : 0,
+ 6, auth ? auth->data : NULL,
+ auth ? auth->len : 0);
silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
0, NULL, NULL, buffer->data, buffer->len, TRUE);
silc_buffer_free(buffer);
silc_buffer_free(idp);
+ if (auth)
+ silc_buffer_free(auth);
/* Notify application */
COMMAND;
if (add) {
if (cmd->argc == 5) {
if (!strcasecmp(cmd->argv[4], "-pubkey")) {
- auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
- cmd->client->private_key,
- conn->hash,
- conn->local_id,
- SILC_ID_CLIENT);
+ auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
+ cmd->client->private_key,
+ conn->hash,
+ conn->local_id,
+ SILC_ID_CLIENT);
} else {
auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
cmd->argv[4], cmd->argv_lens[4]);