+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcSocketConnection sock = cmd->sock;
+ SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+ SilcChannelID *channel_id;
+ SilcClientID *client_id;
+ SilcChannelEntry channel;
+ SilcChannelClientEntry chl;
+ SilcBuffer packet, cidp;
+ unsigned char *tmp, *tmp_id, *tmp_mask;
+ unsigned int argc, mode_mask, tmp_len, tmp_len2;
+ int i;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ argc = silc_argument_get_arg_num(cmd->args);
+ if (argc < 2) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+ if (argc > 8) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_TOO_MANY_PARAMS);
+ goto out;
+ }
+
+ /* Get Channel ID */
+ tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len2);
+ if (!tmp_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_ID);
+ goto out;
+ }
+ channel_id = silc_id_payload_parse_id(tmp_id, tmp_len2);
+
+ /* Get the channel mode mask */
+ tmp_mask = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ if (!tmp_mask) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+ SILC_GET32_MSB(mode_mask, tmp_mask);
+
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
+ if (!channel) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+ goto out;
+ }
+
+ /* Check whether this client is on the channel */
+ if (!silc_server_client_on_channel(client, channel)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ON_CHANNEL);
+ goto out;
+ }
+
+ /* Get entry to the channel user list */
+ silc_list_start(channel->user_list);
+ while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
+ if (chl->client == client)
+ break;
+
+ /* Check that client has rights to change any requested channel modes */
+ if (!silc_server_check_cmode_rights(channel, chl, mode_mask)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+ goto out;
+ }
+
+ /*
+ * Check the modes. Modes that requires nothing special operation are
+ * not checked here.
+ */
+
+ if (mode_mask & SILC_CHANNEL_MODE_PRIVKEY) {
+ /* Channel uses private keys to protect traffic. Client(s) has set the
+ key locally they want to use, server does not know that key. */
+ /* Nothing interesting to do here now */
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+ /* The mode is removed and we need to generate and distribute
+ new channel key. Clients are not using private channel keys
+ anymore after this. */
+ unsigned int key_len;
+ unsigned char channel_key[32];
+
+ /* XXX Duplicated code, make own function for this!! LEAVE uses this
+ as well */
+
+ /* Re-generate channel key */
+ key_len = channel->key_len / 8;
+ for (i = 0; i < key_len; i++)
+ channel_key[i] = silc_rng_get_byte(server->rng);
+ channel->channel_key->cipher->set_key(channel->channel_key->context,
+ channel_key, key_len);
+ memset(channel->key, 0, key_len);
+ silc_free(channel->key);
+ channel->key = silc_calloc(key_len, sizeof(*channel->key));
+ memcpy(channel->key, channel_key, key_len);
+ memset(channel_key, 0, sizeof(channel_key));
+
+ /* Encode channel key payload to be distributed on the channel */
+ packet =
+ silc_channel_key_payload_encode(tmp_len2, tmp_id,
+ strlen(channel->channel_key->
+ cipher->name),
+ channel->channel_key->cipher->name,
+ key_len, channel->key);
+
+ /* If we are normal server then we will send it to our router. If we
+ are router we will send it to all local servers that has clients on
+ the channel */
+ if (server->server_type == SILC_SERVER) {
+ if (!server->standalone)
+ silc_server_packet_send(server,
+ cmd->server->id_entry->router->connection,
+ SILC_PACKET_CHANNEL_KEY, 0, packet->data,
+ packet->len, TRUE);
+ } else {
+
+ }
+
+ /* Send to locally connected clients on the channel */
+ silc_server_packet_send_local_channel(server, channel,
+ SILC_PACKET_CHANNEL_KEY, 0,
+ packet->data, packet->len, FALSE);
+ silc_buffer_free(packet);
+ }
+ }
+
+ if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) {
+ /* User limit is set on channel */
+ unsigned int user_limit;
+
+ /* Get user limit */
+ tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
+ if (!tmp) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+ } else {
+ SILC_GET32_MSB(user_limit, tmp);
+ channel->mode_data.user_limit = user_limit;
+ }
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+ /* User limit mode is unset. Remove user limit */
+ channel->mode_data.user_limit = 0;
+ }
+
+ if (mode_mask & SILC_CHANNEL_MODE_PASSPHRASE) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE)) {
+ /* Passphrase has been set to channel */
+
+ /* Get the passphrase */
+ tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ /* Save the passphrase */
+ channel->mode_data.passphrase = strdup(tmp);
+ }
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+ /* Passphrase mode is unset. remove the passphrase */
+ if (channel->mode_data.passphrase) {
+ silc_free(channel->mode_data.passphrase);
+ channel->mode_data.passphrase = NULL;
+ }
+ }
+ }
+
+ if (mode_mask & SILC_CHANNEL_MODE_BAN) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_BAN)) {
+ /* Ban list is specified for channel */
+
+ /* Get ban list */
+ tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ /* XXX check that channel founder is not banned */
+
+ /* Save the ban list */
+ channel->mode_data.ban_list = strdup(tmp);
+ }
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_BAN) {
+ /* Ban mode is unset. Remove the entire ban list */
+ if (channel->mode_data.ban_list) {
+ silc_free(channel->mode_data.ban_list);
+ channel->mode_data.ban_list = NULL;
+ }
+ }
+ }
+
+ if (mode_mask & SILC_CHANNEL_MODE_INVITE_LIST) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_INVITE_LIST)) {
+ /* Invite list is specified for channel */
+
+ /* Get invite list */
+ tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ /* Save the invite linst */
+ channel->mode_data.invite_list = strdup(tmp);
+ }
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_INVITE_LIST) {
+ /* Invite list mode is unset. Remove the entire invite list */
+ if (channel->mode_data.invite_list) {
+ silc_free(channel->mode_data.invite_list);
+ channel->mode_data.invite_list = NULL;
+ }
+ }
+ }
+
+ if (mode_mask & SILC_CHANNEL_MODE_CIPHER) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
+ /* Cipher to use protect the traffic */
+ unsigned int key_len = 128;
+ unsigned char channel_key[32];
+ char *cp;
+
+ /* Get cipher */
+ tmp = silc_argument_get_arg_type(cmd->args, 8, NULL);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ cp = strchr(tmp, ':');
+ if (cp) {
+ key_len = atoi(cp);
+ *cp = '\0';
+ }
+
+ /* XXX Duplicated code, make own function for this!! */
+
+ /* Delete old cipher and allocate the new one */
+ silc_cipher_free(channel->channel_key);
+ silc_cipher_alloc(tmp, &channel->channel_key);
+
+ /* Re-generate channel key */
+ key_len /= 8;
+ if (key_len > sizeof(channel_key))
+ key_len = sizeof(channel_key);
+
+ for (i = 0; i < key_len; i++)
+ channel_key[i] = silc_rng_get_byte(server->rng);
+ channel->channel_key->cipher->set_key(channel->channel_key->context,
+ channel_key, key_len);
+ memset(channel->key, 0, key_len);
+ silc_free(channel->key);
+ channel->key = silc_calloc(key_len, sizeof(*channel->key));
+ memcpy(channel->key, channel_key, key_len);
+ memset(channel_key, 0, sizeof(channel_key));
+
+ /* Encode channel key payload to be distributed on the channel */
+ packet =
+ silc_channel_key_payload_encode(tmp_len2, tmp_id,
+ strlen(channel->channel_key->
+ cipher->name),
+ channel->channel_key->cipher->name,
+ key_len, channel->key);
+
+ /* If we are normal server then we will send it to our router. If we
+ are router we will send it to all local servers that has clients on
+ the channel */
+ if (server->server_type == SILC_SERVER) {
+ if (!server->standalone)
+ silc_server_packet_send(server,
+ cmd->server->id_entry->router->connection,
+ SILC_PACKET_CHANNEL_KEY, 0, packet->data,
+ packet->len, TRUE);
+ } else {
+
+ }
+
+ /* Send to locally connected clients on the channel */
+ silc_server_packet_send_local_channel(server, channel,
+ SILC_PACKET_CHANNEL_KEY, 0,
+ packet->data, packet->len, FALSE);
+ silc_buffer_free(packet);
+ }
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
+ /* Cipher mode is unset. Remove the cipher and revert back to
+ default cipher */
+ unsigned int key_len;
+ unsigned char channel_key[32];
+
+ if (channel->mode_data.cipher) {
+ silc_free(channel->mode_data.cipher);
+ channel->mode_data.cipher = NULL;
+ channel->mode_data.key_len = 0;
+ }
+
+ /* Generate new cipher and key for the channel */
+
+ /* XXX Duplicated code, make own function for this!! */
+
+ /* Delete old cipher and allocate default one */
+ silc_cipher_free(channel->channel_key);
+ if (!channel->cipher)
+ silc_cipher_alloc("twofish", &channel->channel_key);
+ else
+ silc_cipher_alloc(channel->cipher, &channel->channel_key);
+
+ /* Re-generate channel key */
+ key_len = channel->key_len / 8;
+ for (i = 0; i < key_len; i++)
+ channel_key[i] = silc_rng_get_byte(server->rng);
+ channel->channel_key->cipher->set_key(channel->channel_key->context,
+ channel_key, key_len);
+ memset(channel->key, 0, key_len);
+ silc_free(channel->key);
+ channel->key = silc_calloc(key_len, sizeof(*channel->key));
+ memcpy(channel->key, channel_key, key_len);
+ memset(channel_key, 0, sizeof(channel_key));
+
+ /* Encode channel key payload to be distributed on the channel */
+ packet =
+ silc_channel_key_payload_encode(tmp_len2, tmp_id,
+ strlen(channel->channel_key->
+ cipher->name),
+ channel->channel_key->cipher->name,
+ key_len, channel->key);
+
+ /* If we are normal server then we will send it to our router. If we
+ are router we will send it to all local servers that has clients on
+ the channel */
+ if (server->server_type == SILC_SERVER) {
+ if (!server->standalone)
+ silc_server_packet_send(server,
+ cmd->server->id_entry->router->connection,
+ SILC_PACKET_CHANNEL_KEY, 0, packet->data,
+ packet->len, TRUE);
+ } else {
+
+ }
+
+ /* Send to locally connected clients on the channel */
+ silc_server_packet_send_local_channel(server, channel,
+ SILC_PACKET_CHANNEL_KEY, 0,
+ packet->data, packet->len, FALSE);
+ silc_buffer_free(packet);
+ }
+ }
+
+ /* Finally, set the mode */
+ channel->mode = mode_mask;
+
+ /* Send CMODE_CHANGE notify */
+ cidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ silc_server_send_notify_to_channel(server, channel,
+ SILC_NOTIFY_TYPE_CMODE_CHANGE, 2,
+ cidp->data, cidp->len,
+ tmp_mask, tmp_len);
+ silc_free(cidp);
+
+ /* Send command reply to sender */
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
+ SILC_STATUS_OK, 0, 1,
+ 2, tmp_mask, 4);
+ silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
+ packet->data, packet->len, FALSE);
+
+ silc_buffer_free(packet);
+ silc_free(channel_id);
+
+ out:
+ silc_server_command_free(cmd);
+}
+
+/* Server side of CUMODE command. Changes client's mode on a channel. */
+
+SILC_SERVER_CMD_FUNC(cumode)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcSocketConnection sock = cmd->sock;
+ SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+ SilcChannelID *channel_id;
+ SilcClientID *client_id;
+ SilcChannelEntry channel;
+ SilcClientEntry target_client;
+ SilcChannelClientEntry chl;
+ SilcBuffer packet, idp;
+ unsigned char *tmp, *tmp_id, *tmp_mask;
+ unsigned int argc, target_mask, sender_mask, tmp_len;
+ int i, notify = FALSE;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ argc = silc_argument_get_arg_num(cmd->args);
+ if (argc < 3) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+ if (argc > 3) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_TOO_MANY_PARAMS);
+ goto out;
+ }
+
+ /* Get Channel ID */
+ tmp_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ if (!tmp_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_ID);
+ goto out;
+ }
+ channel_id = silc_id_payload_parse_id(tmp_id, tmp_len);
+
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list, channel_id);
+ if (!channel) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+ goto out;
+ }
+
+ /* Check whether sender is on the channel */
+ if (!silc_server_client_on_channel(client, channel)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_ON_CHANNEL);
+ goto out;
+ }
+
+ /* Check that client has rights to change other's rights */
+ silc_list_start(channel->user_list);
+ while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END) {
+ if (chl->client == client) {
+ if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO) &&
+ !(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+ goto out;
+ }
+
+ sender_mask = chl->mode;
+ break;
+ }
+ }
+
+ /* Get the target client's channel mode mask */
+ tmp_mask = silc_argument_get_arg_type(cmd->args, 2, NULL);
+ if (!tmp_mask) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+ SILC_GET32_MSB(target_mask, tmp_mask);
+
+ /* Get target Client ID */
+ tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+ if (!tmp_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_ID);
+ goto out;
+ }
+ client_id = silc_id_payload_parse_id(tmp_id, tmp_len);
+
+ /* Get target client's entry */
+ target_client = silc_idlist_find_client_by_id(server->local_list, client_id);
+ if (!client) {
+ /* XXX If target client is not one of mine send to primary route */
+ }
+
+ /* Check whether target client is on the channel */
+ if (!silc_server_client_on_channel(target_client, channel)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_USER_NOT_ON_CHANNEL);
+ goto out;
+ }
+
+ /* Get entry to the channel user list */
+ silc_list_start(channel->user_list);
+ while ((chl = silc_list_get(channel->user_list)) != SILC_LIST_END)
+ if (chl->client == target_client)
+ break;
+
+ /*
+ * Change the mode
+ */
+
+ /* If the target client is founder, no one else can change their mode
+ but themselves. */
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && chl->client != target_client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_YOU);
+ goto out;
+ }
+
+ if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
+ /* Cannot promote anyone to channel founder */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_YOU);
+ goto out;
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ if (target_client == client) {
+ /* Remove channel founder rights from itself */
+ chl->mode &= ~SILC_CHANNEL_UMODE_CHANFO;
+ notify = TRUE;
+ } else {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_YOU);
+ goto out;
+ }
+ }
+ }
+
+ if (target_mask & SILC_CHANNEL_UMODE_CHANOP) {
+ /* Promote to operator */
+ if (!(chl->mode & SILC_CHANNEL_UMODE_CHANOP)) {
+ chl->mode |= SILC_CHANNEL_UMODE_CHANOP;
+ notify = TRUE;
+ }
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANOP) {
+ /* Demote to normal user */
+ chl->mode &= ~SILC_CHANNEL_UMODE_CHANOP;
+ notify = TRUE;
+ }
+ }
+
+ /* Send notify to channel, notify only if mode was actually changed. */
+ if (notify) {
+ idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ silc_server_send_notify_to_channel(server, channel,
+ SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
+ idp->data, idp->len,
+ tmp_mask, 4, tmp_id, tmp_len);
+ silc_buffer_free(idp);
+ }
+
+ /* Send command reply to sender */
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CUMODE,
+ SILC_STATUS_OK, 0, 2,
+ 2, tmp_mask, 4,
+ 3, tmp_id, tmp_len);
+ silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
+ packet->data, packet->len, FALSE);
+
+ silc_buffer_free(packet);
+ silc_free(channel_id);
+ silc_free(client_id);
+
+ out:
+ silc_server_command_free(cmd);