From: Pekka Riikonen Date: Sun, 2 Dec 2001 19:05:27 +0000 (+0000) Subject: updates. X-Git-Tag: silc.toolkit.0.7~5 X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=commitdiff_plain;h=6b9bd8ef71943b580d26d0a686bc2ebdc55f2731 updates. --- diff --git a/CHANGES b/CHANGES index 88a533cf..bb563c34 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,36 @@ +Sun Dec 2 13:48:46 EET 2001 Pekka Riikonen + + * 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 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 * Memory leak fixes in: diff --git a/TODO b/TODO index 00e588d8..d080e0ca 100644 --- a/TODO +++ b/TODO @@ -13,11 +13,6 @@ TODO/bugs in Irssi SILC client 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. @@ -35,48 +30,14 @@ TODO/bugs in Irssi SILC client 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 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. diff --git a/apps/irssi/config b/apps/irssi/config index d611d4bd..f6276469 100644 --- a/apps/irssi/config +++ b/apps/irssi/config @@ -1,5 +1,8 @@ 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 = { diff --git a/apps/irssi/docs/help/in/join.in b/apps/irssi/docs/help/in/join.in index 424362af..bd951345 100644 --- a/apps/irssi/docs/help/in/join.in +++ b/apps/irssi/docs/help/in/join.in @@ -4,9 +4,21 @@ 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 . 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 . + +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 diff --git a/apps/irssi/src/silc/core/silc-channels.c b/apps/irssi/src/silc/core/silc-channels.c index 25d6b6a4..5e52a6bb 100644 --- a/apps/irssi/src/silc/core/silc-channels.c +++ b/apps/irssi/src/silc/core/silc-channels.c @@ -242,17 +242,19 @@ static void event_signoff(SILC_SERVER_REC *server, va_list va) 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); @@ -260,11 +262,23 @@ static void event_topic(SILC_SERVER_REC *server, va_list va) 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); + } } /* @@ -459,6 +473,8 @@ static void event_server_signoff(SILC_SERVER_REC *server, va_list va) 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; diff --git a/apps/irssi/src/silc/core/silc-servers.c b/apps/irssi/src/silc/core/silc-servers.c index f9f5854f..8e476fa1 100644 --- a/apps/irssi/src/silc/core/silc-servers.c +++ b/apps/irssi/src/silc/core/silc-servers.c @@ -312,6 +312,7 @@ char *silc_server_get_channels(SILC_SERVER_REC *server) /* SYNTAX: FILE RECEIVE [] */ /* SYNTAX: FILE CLOSE [] */ /* SYNTAX: FILE */ +/* SYNTAX: JOIN [] [-cipher ] [-hmac ] [-founder <-pubkey|passwd>] */ void silc_command_exec(SILC_SERVER_REC *server, const char *command, const char *args) @@ -331,7 +332,7 @@ void silc_command_exec(SILC_SERVER_REC *server, 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, diff --git a/apps/silcd/command.c b/apps/silcd/command.c index 5316ee99..d523172c 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -825,11 +825,18 @@ silc_server_command_whois_process(SilcServerCommandContext cmd) 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; @@ -860,20 +867,11 @@ silc_server_command_whois_process(SilcServerCommandContext cmd) 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 */ @@ -1270,7 +1268,8 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd, uint32 *servers_count, SilcChannelEntry **channels, uint32 *channels_count, - uint32 *count) + uint32 *count, + bool *names) { SilcServer server = cmd->server; unsigned char *tmp; @@ -1291,6 +1290,7 @@ silc_server_command_identify_parse(SilcServerCommandContext cmd, 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); @@ -1824,12 +1824,21 @@ silc_server_command_identify_process(SilcServerCommandContext cmd) 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; @@ -1860,17 +1869,6 @@ silc_server_command_identify_process(SilcServerCommandContext cmd) 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, @@ -2287,13 +2285,14 @@ SILC_SERVER_CMD_FUNC(topic) 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)); @@ -2931,7 +2930,9 @@ static void silc_server_command_join_channel(SilcServer server, 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; @@ -2942,6 +2943,7 @@ static void silc_server_command_join_channel(SilcServer server, 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")); @@ -2971,64 +2973,104 @@ static void silc_server_command_join_channel(SilcServer server, } /* - * 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))) { @@ -3038,16 +3080,6 @@ static void silc_server_command_join_channel(SilcServer server, } } - /* 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. */ @@ -3143,7 +3175,7 @@ static void silc_server_command_join_channel(SilcServer server, 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); @@ -3159,6 +3191,24 @@ static void silc_server_command_join_channel(SilcServer server, /* 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); @@ -3179,14 +3229,15 @@ SILC_SERVER_CMD_FUNC(join) { 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); @@ -3220,9 +3271,10 @@ SILC_SERVER_CMD_FUNC(join) 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, @@ -3364,7 +3416,8 @@ SILC_SERVER_CMD_FUNC(join) /* 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); @@ -3672,7 +3725,7 @@ SILC_SERVER_CMD_FUNC(cmode) 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); @@ -3796,7 +3849,7 @@ SILC_SERVER_CMD_FUNC(cmode) } /* Save the passphrase */ - channel->passphrase = strdup(tmp); + passphrase = channel->passphrase = strdup(tmp); } } else { if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) { @@ -4007,14 +4060,16 @@ SILC_SERVER_CMD_FUNC(cmode) /* 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) @@ -4022,7 +4077,7 @@ SILC_SERVER_CMD_FUNC(cmode) 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, @@ -4255,7 +4310,7 @@ SILC_SERVER_CMD_FUNC(cumode) /* 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, diff --git a/apps/silcd/packet_receive.c b/apps/silcd/packet_receive.c index f5cd5d20..248442d7 100644 --- a/apps/silcd/packet_receive.c +++ b/apps/silcd/packet_receive.c @@ -367,10 +367,8 @@ void silc_server_notify(SilcServer server, 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, @@ -520,6 +518,13 @@ void silc_server_notify(SilcServer server, 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: @@ -800,6 +805,15 @@ void silc_server_notify(SilcServer server, 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); @@ -2235,7 +2249,8 @@ void silc_server_new_channel(SilcServer server, 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 diff --git a/apps/silcd/packet_send.c b/apps/silcd/packet_send.c index 3c71557c..0278ff1c 100644 --- a/apps/silcd/packet_send.c +++ b/apps/silcd/packet_send.c @@ -525,7 +525,8 @@ silc_server_packet_send_to_channel_real(SilcServer server, 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, @@ -574,7 +575,7 @@ void silc_server_packet_send_to_channel(SilcServer server, 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, @@ -1204,7 +1205,8 @@ void silc_server_send_notify_cmode(SilcServer server, 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]; @@ -1214,15 +1216,17 @@ void silc_server_send_notify_cmode(SilcServer server, 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, @@ -1271,7 +1275,7 @@ void silc_server_send_notify_signoff(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. */ @@ -1280,17 +1284,18 @@ void silc_server_send_notify_topic_set(SilcServer server, 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); } @@ -1441,7 +1446,7 @@ void silc_server_send_notify_dest(SilcServer server, void silc_server_send_notify_to_channel(SilcServer server, SilcSocketConnection sender, SilcChannelEntry channel, - unsigned char route_notify, + bool route_notify, SilcNotifyType type, uint32 argc, ...) { @@ -1714,7 +1719,8 @@ void silc_server_send_channel_key(SilcServer server, 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); } diff --git a/apps/silcd/packet_send.h b/apps/silcd/packet_send.h index 893c820f..412799b1 100644 --- a/apps/silcd/packet_send.h +++ b/apps/silcd/packet_send.h @@ -140,7 +140,8 @@ void silc_server_send_notify_cmode(SilcServer server, 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, @@ -157,7 +158,7 @@ void silc_server_send_notify_topic_set(SilcServer server, 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, @@ -196,7 +197,7 @@ void silc_server_send_notify_dest(SilcServer server, 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, diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 22af66f5..8f58188e 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -3145,6 +3145,19 @@ static void silc_server_announce_get_servers(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' @@ -3188,12 +3201,15 @@ void silc_server_announce_servers(SilcServer server, bool global, 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)) { @@ -3216,6 +3232,20 @@ static void silc_server_announce_get_clients(SilcServer server, 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)) @@ -3237,17 +3267,18 @@ void silc_server_announce_clients(SilcServer server, 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); @@ -3260,19 +3291,36 @@ void silc_server_announce_clients(SilcServer server, 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'. */ @@ -3348,6 +3396,7 @@ void silc_server_announce_get_channels(SilcServer server, SilcBuffer *channel_users, SilcBuffer **channel_users_modes, uint32 *channel_users_modes_c, + SilcBuffer **channel_topics, SilcChannelID ***channel_ids, unsigned long creation_time) { @@ -3397,6 +3446,7 @@ void silc_server_announce_get_channels(SilcServer server, silc_buffer_pull(*channels, len); } + /* Channel user modes */ *channel_users_modes = silc_realloc(*channel_users_modes, sizeof(**channel_users_modes) * (i + 1)); @@ -3408,6 +3458,13 @@ void silc_server_announce_get_channels(SilcServer server, 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)) @@ -3434,6 +3491,7 @@ void silc_server_announce_channels(SilcServer server, { 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; @@ -3444,6 +3502,7 @@ void silc_server_announce_channels(SilcServer server, &channels, &channel_users, &channel_users_modes, &channel_users_modes_c, + &channel_topics, &channel_ids, creation_time); /* Get channels and channel users in global list */ @@ -3452,6 +3511,7 @@ void silc_server_announce_channels(SilcServer server, &channels, &channel_users, &channel_users_modes, &channel_users_modes_c, + &channel_topics, &channel_ids, creation_time); if (channels) { @@ -3499,8 +3559,32 @@ void silc_server_announce_channels(SilcServer server, 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 diff --git a/apps/silcd/server.h b/apps/silcd/server.h index 8ea3293b..6301b58e 100644 --- a/apps/silcd/server.h +++ b/apps/silcd/server.h @@ -178,6 +178,9 @@ SilcChannelEntry silc_server_save_channel_key(SilcServer server, 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, @@ -188,6 +191,7 @@ void silc_server_announce_get_channels(SilcServer server, 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, diff --git a/doc/draft-riikonen-silc-commands-03.nroff b/doc/draft-riikonen-silc-commands-03.nroff index 203c5308..64e3d44f 100644 --- a/doc/draft-riikonen-silc-commands-03.nroff +++ b/doc/draft-riikonen-silc-commands-03.nroff @@ -846,10 +846,7 @@ List of all defined commands in SILC follows. 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 @@ -857,9 +854,7 @@ List of all defined commands in SILC follows. are: o The user MUST be invited to the channel if the channel - is invite-only channel, or MUST provide correct - 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. @@ -869,6 +864,11 @@ List of all defined commands in SILC follows. o The user count limit, if set, MUST NOT be reached. + If the client provided correct payload it can + override these conditions, except the condition for the passphrase. + The correct passphrase MUST be provided even if + payload is provided. + Reply messages to the command: Max Arguments: 14 diff --git a/doc/draft-riikonen-silc-pp-05.nroff b/doc/draft-riikonen-silc-pp-05.nroff index eedbc567..23669b93 100644 --- a/doc/draft-riikonen-silc-pp-05.nroff +++ b/doc/draft-riikonen-silc-pp-05.nroff @@ -1281,9 +1281,10 @@ Also, all ID's sent in arguments are sent inside ID Payload. topic was set or changed. Max Arguments: 2 - Arguments: (1) (2) + Arguments: (1) (2) - The is the client which set or changed the . + The 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 @@ -1308,9 +1309,10 @@ Also, all ID's sent in arguments are sent inside ID Payload. to the clients which is joined on the channel which mode was changed. - Max Arguments: 4 - Arguments: (1) (2) - (3) [] (4) <[hmac>] + Max Arguments: 5 + Arguments: (1) (2) + (3) [] (4) <[hmac>] + (5) [] The is the ID (usually Client ID but it can be Server ID as well when the router is enforcing channel mode @@ -1319,7 +1321,8 @@ Also, all ID's sent in arguments are sent inside ID Payload. ignore the argument since the SILC_PACKET_CHANNEL_KEY packet will force the new channel key change anyway. The 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 is + the passphrase of the channel, if it was now set. 8 SILC_NOTIFY_TYPE_CUMODE_CHANGE diff --git a/lib/silcclient/client.c b/lib/silcclient/client.c index 0440478d..58bcad2b 100644 --- a/lib/silcclient/client.c +++ b/lib/silcclient/client.c @@ -371,7 +371,7 @@ bool silc_client_start_key_exchange(SilcClient client, 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; diff --git a/lib/silcclient/client_notify.c b/lib/silcclient/client_notify.c index ddc70e7b..d819b972 100644 --- a/lib/silcclient/client_notify.c +++ b/lib/silcclient/client_notify.c @@ -346,15 +346,58 @@ void silc_client_notify_by_server(SilcClient client, 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); @@ -375,7 +418,10 @@ void silc_client_notify_by_server(SilcClient client, /* 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: @@ -659,6 +705,9 @@ void silc_client_notify_by_server(SilcClient client, 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); @@ -673,8 +722,7 @@ void silc_client_notify_by_server(SilcClient client, 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); diff --git a/lib/silcclient/command.c b/lib/silcclient/command.c index 5257bf45..dd329441 100644 --- a/lib/silcclient/command.c +++ b/lib/silcclient/command.c @@ -42,7 +42,7 @@ SilcClientCommand silc_command_list[] = 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), @@ -364,11 +364,11 @@ SILC_CLIENT_CMD_FUNC(nick_change) 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; @@ -993,7 +993,9 @@ SILC_CLIENT_CMD_FUNC(join) 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); @@ -1001,6 +1003,11 @@ SILC_CLIENT_CMD_FUNC(join) 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)) { @@ -1014,31 +1021,50 @@ SILC_CLIENT_CMD_FUNC(join) 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; @@ -1520,11 +1546,11 @@ SILC_CLIENT_CMD_FUNC(cumode) 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]);