+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcChannelID *channel_id = NULL;
+ unsigned char *tmp;
+ uint32 tmp_len;
+ SilcChannelEntry *lchannels = NULL, *gchannels = NULL;
+ uint32 lch_count = 0, gch_count = 0;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_LIST, cmd, 0, 1);
+
+ /* If we are normal server, send the command to router, since we
+ want to know all channels in the network. */
+ if (!cmd->pending && server->server_type == SILC_SERVER &&
+ !server->standalone) {
+ SilcBuffer tmpbuf;
+ uint16 old_ident;
+
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+ silc_server_packet_send(server, server->router->connection,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, tmpbuf->len, TRUE);
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_LIST,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_list,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
+ }
+
+ /* Get Channel ID */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ if (tmp) {
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_LIST,
+ SILC_STATUS_ERR_NO_CHANNEL_ID);
+ goto out;
+ }
+ }
+
+ /* Get the channels from local list */
+ lchannels = silc_idlist_get_channels(server->local_list, channel_id,
+ &lch_count);
+
+ /* Get the channels from global list */
+ gchannels = silc_idlist_get_channels(server->global_list, channel_id,
+ &gch_count);
+
+ /* Send the reply */
+ silc_server_command_list_send_reply(cmd, lchannels, lch_count,
+ gchannels, gch_count);
+
+ silc_free(lchannels);
+ silc_free(gchannels);
+
+ out:
+ silc_server_command_free(cmd);
+}
+
+/* Server side of TOPIC command. Sets topic for channel and/or returns
+ current topic to client. */
+
+SILC_SERVER_CMD_FUNC(topic)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+ SilcChannelID *channel_id;
+ SilcChannelEntry channel;
+ SilcChannelClientEntry chl;
+ SilcBuffer packet, idp;
+ unsigned char *tmp;
+ uint32 argc, tmp_len;
+ uint16 ident = silc_command_get_ident(cmd->payload);
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_TOPIC, cmd, 1, 2);
+
+ argc = silc_argument_get_arg_num(cmd->args);
+
+ /* Get Channel ID */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+ SILC_STATUS_ERR_NO_CHANNEL_ID);
+ goto out;
+ }
+ channel_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!channel_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+ SILC_STATUS_ERR_NO_CHANNEL_ID);
+ goto out;
+ }
+
+ /* Check whether the channel exists */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+ goto out;
+ }
+ }
+
+ if (argc > 1) {
+ /* Get the topic */
+ tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ if (strlen(tmp) > 256) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ /* See whether the client is on channel and has rights to change topic */
+ if (!silc_hash_table_find(channel->user_list, client, NULL,
+ (void *)&chl)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+ SILC_STATUS_ERR_NOT_ON_CHANNEL);
+ goto out;
+ }
+
+ if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+ if (channel->mode & SILC_CHANNEL_MODE_TOPIC) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_TOPIC,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+ goto out;
+ }
+ }
+
+ /* Set the topic for channel */
+ silc_free(channel->topic);
+ channel->topic = strdup(tmp);
+
+ /* Send TOPIC_SET notify type to the network */
+ if (!server->standalone)
+ silc_server_send_notify_topic_set(server, server->router->connection,
+ server->server_type == SILC_ROUTER ?
+ 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, FALSE,
+ SILC_NOTIFY_TYPE_TOPIC_SET, 2,
+ idp->data, idp->len,
+ channel->topic, strlen(channel->topic));
+ silc_buffer_free(idp);
+ }
+
+ /* Send the topic to client as reply packet */
+ idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_TOPIC,
+ SILC_STATUS_OK, ident, 2,
+ 2, idp->data, idp->len,
+ 3, channel->topic,
+ channel->topic ?
+ strlen(channel->topic) : 0);
+ silc_server_packet_send(cmd->server, cmd->sock, SILC_PACKET_COMMAND_REPLY,
+ 0, packet->data, packet->len, FALSE);
+
+ silc_buffer_free(packet);
+ silc_buffer_free(idp);
+ silc_free(channel_id);
+
+ out:
+ silc_server_command_free(cmd);
+}
+
+/* Server side of INVITE command. Invites some client to join some channel.
+ This command is also used to manage the invite list of the channel. */
+
+SILC_SERVER_CMD_FUNC(invite)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcSocketConnection sock = cmd->sock, dest_sock;
+ SilcChannelClientEntry chl;
+ SilcClientEntry sender, dest;
+ SilcClientID *dest_id = NULL;
+ SilcChannelEntry channel;
+ SilcChannelID *channel_id = NULL;
+ SilcIDListData idata;
+ SilcBuffer idp, idp2, packet;
+ unsigned char *tmp, *add, *del;
+ uint32 len;
+ uint16 ident = silc_command_get_ident(cmd->payload);
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INVITE, cmd, 1, 4);
+
+ /* Get Channel ID */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NO_CHANNEL_ID);
+ goto out;
+ }
+ channel_id = silc_id_payload_parse_id(tmp, len);
+ if (!channel_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NO_CHANNEL_ID);
+ goto out;
+ }
+
+ /* Get the channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ if (!channel) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NO_SUCH_CHANNEL);
+ goto out;
+ }
+ }
+
+ /* Check whether the sender of this command is on the channel. */
+ sender = (SilcClientEntry)sock->user_data;
+ if (!silc_server_client_on_channel(sender, channel)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NOT_ON_CHANNEL);
+ goto out;
+ }
+
+ /* Check whether the channel is invite-only channel. If yes then the
+ sender of this command must be at least channel operator. */
+ if (channel->mode & SILC_CHANNEL_MODE_INVITE) {
+ silc_hash_table_find(channel->user_list, sender, NULL, (void *)&chl);
+ if (chl->mode == SILC_CHANNEL_UMODE_NONE) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+ goto out;
+ }
+ }
+
+ /* Get destination client ID */
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
+ if (tmp) {
+ char invite[512];
+ bool resolve;
+
+ dest_id = silc_id_payload_parse_id(tmp, len);
+ if (!dest_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NO_CLIENT_ID);
+ goto out;
+ }
+
+ /* Get the client entry */
+ dest = silc_server_get_client_resolve(server, dest_id, &resolve);
+ if (!dest) {
+ if (server->server_type != SILC_SERVER || !resolve) {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+ goto out;
+ }
+
+ /* The client info is being resolved. Reprocess this packet after
+ receiving the reply to the query. */
+ silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ server->cmd_ident,
+ silc_server_command_invite,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_free(channel_id);
+ silc_free(dest_id);
+ goto out;
+ }
+
+ /* Check whether the requested client is already on the channel. */
+ if (silc_server_client_on_channel(dest, channel)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_USER_ON_CHANNEL);
+ goto out;
+ }
+
+ /* Get route to the client */
+ dest_sock = silc_server_get_client_route(server, NULL, 0, dest_id, &idata);
+ if (!dest_sock) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INVITE,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+ goto out;
+ }
+
+ memset(invite, 0, sizeof(invite));
+ strncat(invite, dest->nickname, strlen(dest->nickname));
+ strncat(invite, "!", 1);
+ strncat(invite, dest->username, strlen(dest->username));
+ if (!strchr(dest->username, '@')) {
+ strncat(invite, "@", 1);
+ strncat(invite, cmd->sock->hostname, strlen(cmd->sock->hostname));
+ }
+
+ len = strlen(invite);
+ if (!channel->invite_list)
+ channel->invite_list = silc_calloc(len + 2,
+ sizeof(*channel->invite_list));
+ else
+ channel->invite_list = silc_realloc(channel->invite_list,
+ sizeof(*channel->invite_list) *
+ (len +
+ strlen(channel->invite_list) + 2));
+ strncat(channel->invite_list, invite, len);
+ strncat(channel->invite_list, ",", 1);
+
+ /* Send notify to the client that is invited to the channel */
+ idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
+ idp2 = silc_id_payload_encode(sender->id, SILC_ID_CLIENT);
+ silc_server_send_notify_dest(server, dest_sock, FALSE, dest_id,
+ SILC_ID_CLIENT,
+ SILC_NOTIFY_TYPE_INVITE, 3,
+ idp->data, idp->len,
+ channel->channel_name,
+ strlen(channel->channel_name),
+ idp2->data, idp2->len);
+ silc_buffer_free(idp);
+ silc_buffer_free(idp2);
+ }
+
+ /* Add the client to the invite list of the channel */
+ add = silc_argument_get_arg_type(cmd->args, 3, &len);
+ if (add) {
+ if (!channel->invite_list)
+ channel->invite_list = silc_calloc(len + 2,
+ sizeof(*channel->invite_list));
+ else
+ channel->invite_list = silc_realloc(channel->invite_list,
+ sizeof(*channel->invite_list) *
+ (len +
+ strlen(channel->invite_list) + 2));
+ if (add[len - 1] == ',')
+ add[len - 1] = '\0';
+
+ strncat(channel->invite_list, add, len);
+ strncat(channel->invite_list, ",", 1);
+ }
+
+ /* Get the invite to be removed and remove it from the list */
+ del = silc_argument_get_arg_type(cmd->args, 4, &len);
+ if (del && channel->invite_list) {
+ char *start, *end, *n;
+
+ if (!strncmp(channel->invite_list, del,
+ strlen(channel->invite_list) - 1)) {
+ silc_free(channel->invite_list);
+ channel->invite_list = NULL;
+ } else {
+ start = strstr(channel->invite_list, del);
+ if (start && strlen(start) >= len) {
+ end = start + len;
+ n = silc_calloc(strlen(channel->invite_list) - len, sizeof(*n));
+ strncat(n, channel->invite_list, start - channel->invite_list);
+ strncat(n, end + 1, ((channel->invite_list +
+ strlen(channel->invite_list)) - end) - 1);
+ silc_free(channel->invite_list);
+ channel->invite_list = n;
+ }
+ }
+ }
+
+ /* Send notify to the primary router */
+ if (!server->standalone)
+ silc_server_send_notify_invite(server, server->router->connection,
+ server->server_type == SILC_ROUTER ?
+ TRUE : FALSE, channel,
+ sender->id, add, del);
+
+ /* Send command reply */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+
+ if (add || del)
+ packet =
+ silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
+ SILC_STATUS_OK, ident, 2,
+ 2, tmp, len,
+ 3, channel->invite_list,
+ channel->invite_list ?
+ strlen(channel->invite_list) : 0);
+ else
+ packet =
+ silc_command_reply_payload_encode_va(SILC_COMMAND_INVITE,
+ SILC_STATUS_OK, ident, 1,
+ 2, tmp, len);
+ silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
+ packet->data, packet->len, FALSE);
+ silc_buffer_free(packet);
+
+ out:
+ silc_free(dest_id);
+ silc_free(channel_id);
+ silc_server_command_free(cmd);
+}
+
+typedef struct {
+ SilcServer server;
+ SilcSocketConnection sock;
+ char *signoff;
+} *QuitInternal;
+
+/* Quits connection to client. This gets called if client won't
+ close the connection even when it has issued QUIT command. */
+
+SILC_TASK_CALLBACK(silc_server_command_quit_cb)
+{
+ QuitInternal q = (QuitInternal)context;
+
+ /* Free all client specific data, such as client entry and entires
+ on channels this client may be on. */
+ silc_server_free_client_data(q->server, q->sock, q->sock->user_data,
+ TRUE, q->signoff);
+ q->sock->user_data = NULL;
+
+ /* Close the connection on our side */
+ silc_server_close_connection(q->server, q->sock);
+
+ silc_free(q->signoff);
+ silc_free(q);
+}
+
+/* Quits SILC session. This is the normal way to disconnect client. */
+
+SILC_SERVER_CMD_FUNC(quit)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcSocketConnection sock = cmd->sock;
+ QuitInternal q;
+ unsigned char *tmp = NULL;
+ uint32 len = 0;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_QUIT, cmd, 0, 1);
+
+ if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+ goto out;
+
+ /* Get destination ID */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+ if (len > 128)
+ tmp = NULL;
+
+ q = silc_calloc(1, sizeof(*q));
+ q->server = server;
+ q->sock = sock;
+ q->signoff = tmp ? strdup(tmp) : NULL;
+
+ /* We quit the connection with little timeout */
+ silc_schedule_task_add(server->schedule, sock->sock,
+ silc_server_command_quit_cb, (void *)q,
+ 0, 200000, SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
+
+ out:
+ silc_server_command_free(cmd);
+}
+
+/* Server side of command KILL. This command is used by router operator
+ to remove an client from the SILC Network temporarily. */
+
+SILC_SERVER_CMD_FUNC(kill)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+ SilcClientEntry remote_client;
+ SilcClientID *client_id;
+ unsigned char *tmp, *comment;
+ uint32 tmp_len, tmp_len2;
+ bool local;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_KILL, cmd, 1, 2);
+
+ if (!client || cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+ goto out;
+
+ /* KILL command works only on router */
+ if (server->server_type != SILC_ROUTER) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_ERR_NO_ROUTER_PRIV);
+ goto out;
+ }
+
+ /* Check whether client has the permissions. */
+ if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_ERR_NO_ROUTER_PRIV);
+ goto out;
+ }
+
+ /* Get the client ID */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+ goto out;
+ }
+
+ /* Get the client entry */
+ remote_client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, NULL);
+ local = TRUE;
+ if (!remote_client) {
+ remote_client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, TRUE, NULL);
+ local = FALSE;
+ if (!remote_client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_ERR_NO_SUCH_CLIENT_ID);
+ goto out;
+ }
+ }
+
+ /* Get comment */
+ comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2);
+ if (tmp_len2 > 128)
+ comment = NULL;
+
+ /* Send reply to the sender */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_KILL,
+ SILC_STATUS_OK);
+
+ /* Send the KILL notify packets. First send it to the channel, then
+ to our primary router and then directly to the client who is being
+ killed right now. */
+
+ /* Send KILLED notify to the channels. It is not sent to the client
+ as it will be sent differently destined directly to the client and not
+ to the channel. */
+ silc_server_send_notify_on_channels(server, remote_client,
+ remote_client, SILC_NOTIFY_TYPE_KILLED,
+ comment ? 2 : 1,
+ tmp, tmp_len,
+ comment, comment ? tmp_len2 : 0);
+
+ /* Send KILLED notify to primary route */
+ if (!server->standalone)
+ silc_server_send_notify_killed(server, server->router->connection, TRUE,
+ remote_client->id, comment);
+
+ /* Send KILLED notify to the client directly */
+ silc_server_send_notify_killed(server, remote_client->connection ?
+ remote_client->connection :
+ remote_client->router->connection, FALSE,
+ remote_client->id, comment);
+
+ /* Remove the client from all channels. This generates new keys to the
+ channels as well. */
+ silc_server_remove_from_channels(server, NULL, remote_client, FALSE,
+ NULL, TRUE);
+
+ /* Remove the client entry, If it is locally connected then we will also
+ disconnect the client here */
+ if (remote_client->connection) {
+ /* Remove locally conneted client */
+ SilcSocketConnection sock = remote_client->connection;
+ silc_server_free_client_data(server, sock, remote_client, FALSE, NULL);
+ silc_server_close_connection(server, sock);
+ } else {
+ /* Update statistics */
+ if (remote_client->connection)
+ server->stat.my_clients--;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.cell_clients--;
+ SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR);
+ SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR);
+
+ /* Remove remote client */
+ silc_idlist_del_client(local ? server->local_list :
+ server->global_list, remote_client);
+ }
+
+ out:
+ silc_server_command_free(cmd);
+}
+
+/* Server side of command INFO. This sends information about us to
+ the client. If client requested specific server we will send the
+ command to that server. */
+
+SILC_SERVER_CMD_FUNC(info)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcBuffer packet, idp;
+ unsigned char *tmp;
+ uint32 tmp_len;
+ char *dest_server, *server_info = NULL, *server_name;
+ uint16 ident = silc_command_get_ident(cmd->payload);
+ SilcServerEntry entry = NULL;
+ SilcServerID *server_id = NULL;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 0, 2);
+
+ /* Get server name */
+ dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
+
+ /* Get Server ID */
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ if (tmp) {
+ server_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!server_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
+ SILC_STATUS_ERR_NO_SERVER_ID);
+ goto out;
+ }
+ }
+
+ if (server_id) {
+ /* Check whether we have this server cached */
+ entry = silc_idlist_find_server_by_id(server->local_list,
+ server_id, TRUE, NULL);
+ if (!entry) {
+ entry = silc_idlist_find_server_by_id(server->global_list,
+ server_id, TRUE, NULL);
+ if (!entry && server->server_type != SILC_SERVER) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
+ SILC_STATUS_ERR_NO_SUCH_SERVER);
+ goto out;
+ }
+ }
+ }
+
+ /* Some buggy servers has sent request to router about themselves. */
+ if (server->server_type != SILC_SERVER && cmd->sock->user_data == entry)
+ goto out;
+
+ if ((!dest_server && !server_id && !entry) || (entry &&
+ entry == server->id_entry) ||
+ (dest_server && !cmd->pending &&
+ !strncasecmp(dest_server, server->server_name, strlen(dest_server)))) {
+ /* Send our reply */
+ char info_string[256];
+
+ memset(info_string, 0, sizeof(info_string));
+ snprintf(info_string, sizeof(info_string),
+ "location: %s server: %s admin: %s <%s>",
+ server->config->server_info->location,
+ server->config->server_info->server_type,
+ server->config->server_info->admin,
+ server->config->server_info->email);
+
+ server_info = info_string;
+ entry = server->id_entry;
+ } else {
+ /* Check whether we have this server cached */
+ if (!entry && dest_server) {
+ entry = silc_idlist_find_server_by_name(server->global_list,
+ dest_server, TRUE, NULL);
+ if (!entry) {
+ entry = silc_idlist_find_server_by_name(server->local_list,
+ dest_server, TRUE, NULL);
+ }
+ }
+
+ if (!cmd->pending &&
+ server->server_type != SILC_SERVER && entry && !entry->server_info) {
+ /* Send to the server */
+ SilcBuffer tmpbuf;
+ uint16 old_ident;
+
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ silc_server_packet_send(server, entry->connection,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, tmpbuf->len, TRUE);
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_INFO,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_info,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
+ }
+
+ if (!entry && !cmd->pending && !server->standalone) {
+ /* Send to the primary router */
+ SilcBuffer tmpbuf;
+ uint16 old_ident;
+
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ silc_server_packet_send(server, server->router->connection,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, tmpbuf->len, TRUE);
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_INFO,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_info,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
+ }
+ }
+
+ silc_free(server_id);
+
+ if (!entry) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
+ SILC_STATUS_ERR_NO_SUCH_SERVER);
+ goto out;
+ }
+
+ idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
+ if (!server_info)
+ server_info = entry->server_info;
+ server_name = entry->server_name;
+
+ /* Send the reply */
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_INFO,
+ SILC_STATUS_OK, ident, 3,
+ 2, idp->data, idp->len,
+ 3, server_name,
+ strlen(server_name),
+ 4, server_info,
+ server_info ?
+ strlen(server_info) : 0);
+ silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
+ packet->data, packet->len, FALSE);
+
+ silc_buffer_free(packet);
+ silc_buffer_free(idp);
+
+ out:
+ silc_server_command_free(cmd);
+}
+
+/* Server side of command PING. This just replies to the ping. */
+
+SILC_SERVER_CMD_FUNC(ping)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcServerID *id;
+ uint32 len;
+ unsigned char *tmp;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_INFO, cmd, 1, 2);
+
+ /* Get Server ID */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &len);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+ SILC_STATUS_ERR_NO_SERVER_ID);
+ goto out;
+ }
+ id = silc_id_str2id(tmp, len, SILC_ID_SERVER);
+ if (!id)
+ goto out;
+
+ if (SILC_ID_SERVER_COMPARE(id, server->id)) {
+ /* Send our reply */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+ SILC_STATUS_OK);
+ } else {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PING,
+ SILC_STATUS_ERR_NO_SUCH_SERVER);
+ goto out;
+ }
+
+ silc_free(id);
+
+ out:
+ silc_server_command_free(cmd);
+}
+
+/* Internal routine to join channel. The channel sent to this function
+ has been either created or resolved from ID lists. This joins the sent
+ client to the channel. */
+
+static void silc_server_command_join_channel(SilcServer server,
+ SilcServerCommandContext cmd,
+ SilcChannelEntry channel,
+ SilcClientID *client_id,
+ bool created,
+ bool create_key,
+ uint32 umode,
+ const unsigned char *auth,
+ uint32 auth_len)
+{
+ SilcSocketConnection sock = cmd->sock;
+ unsigned char *tmp;
+ uint32 tmp_len, user_count;
+ unsigned char *passphrase = NULL, mode[4], tmp2[4], tmp3[4];
+ SilcClientEntry client;
+ SilcChannelClientEntry chl;
+ 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;
+ bool resolve;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (!channel)
+ return;
+
+ /* Get the client entry */
+ if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+ client = (SilcClientEntry)sock->user_data;
+ } else {
+ client = silc_server_get_client_resolve(server, client_id, &resolve);
+ if (!client) {
+ if (cmd->pending)
+ goto out;
+
+ if (!resolve) {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ /* The client info is being resolved. Reprocess this packet after
+ receiving the reply to the query. */
+ silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ server->cmd_ident,
+ silc_server_command_join,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ goto out;
+ }
+
+ cmd->pending = FALSE;
+ }
+
+ /*
+ * 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.
+ */
+ 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;
+ }
+ }
+ }
+
+ /*
+ * Check channel modes
+ */
+
+ 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));
+ }
+
+ 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;
+ }
+ }
+
+ /* 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 (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(passphrase, channel->passphrase, strlen(channel->passphrase))) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_BAD_PASSWORD);
+ goto out;
+ }
+ }
+
+ /*
+ * Client is allowed to join to the channel. Make it happen.
+ */
+
+ /* Check whether the client already is on the channel */
+ if (silc_server_client_on_channel(client, channel)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_USER_ON_CHANNEL);
+ goto out;
+ }
+
+ /* Generate new channel key as protocol dictates */
+ if (create_key) {
+ if (!silc_server_create_channel_key(server, channel, 0))
+ goto out;
+
+ /* Send the channel key. This is broadcasted to the channel but is not
+ sent to the client who is joining to the channel. */
+ if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
+ silc_server_send_channel_key(server, NULL, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ }
+
+ /* Join the client to the channel by adding it to channel's user list.
+ Add also the channel to client entry's channels list for fast cross-
+ referencing. */
+ chl = silc_calloc(1, sizeof(*chl));
+ chl->mode = umode;
+ chl->client = client;
+ chl->channel = channel;
+ silc_hash_table_add(channel->user_list, client, chl);
+ silc_hash_table_add(client->channels, channel, chl);
+
+ /* Get users on the channel */
+ silc_server_get_users_on_channel(server, channel, &user_list, &mode_list,
+ &user_count);
+
+ /* Encode Client ID Payload of the original client who wants to join */
+ clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+
+ /* Encode command reply packet */
+ chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+ SILC_PUT32_MSB(channel->mode, mode);
+ SILC_PUT32_MSB(created, tmp2);
+ SILC_PUT32_MSB(user_count, tmp3);
+
+ if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
+ tmp = silc_id_id2str(channel->id, SILC_ID_CHANNEL);
+ keyp = silc_channel_key_payload_encode(SILC_ID_CHANNEL_LEN, tmp,
+ strlen(channel->channel_key->
+ cipher->name),
+ channel->channel_key->cipher->name,
+ channel->key_len / 8, channel->key);
+ silc_free(tmp);
+ }
+
+ reply =
+ silc_command_reply_payload_encode_va(SILC_COMMAND_JOIN,
+ SILC_STATUS_OK, ident, 13,
+ 2, channel->channel_name,
+ strlen(channel->channel_name),
+ 3, chidp->data, chidp->len,
+ 4, clidp->data, clidp->len,
+ 5, mode, 4,
+ 6, tmp2, 4,
+ 7, keyp ? keyp->data : NULL,
+ keyp ? keyp->len : 0,
+ 8, channel->ban_list,
+ channel->ban_list ?
+ strlen(channel->ban_list) : 0,
+ 9, channel->invite_list,
+ channel->invite_list ?
+ strlen(channel->invite_list) : 0,
+ 10, channel->topic,
+ channel->topic ?
+ strlen(channel->topic) : 0,
+ 11, silc_hmac_get_name(channel->hmac),
+ strlen(silc_hmac_get_name(channel->
+ hmac)),
+ 12, tmp3, 4,
+ 13, user_list->data, user_list->len,
+ 14, mode_list->data,
+ mode_list->len);
+
+ /* Send command reply */
+ silc_server_packet_send(server, sock, SILC_PACKET_COMMAND_REPLY, 0,
+ reply->data, reply->len, FALSE);
+
+ /* Send JOIN notify to locally connected clients on the channel. If
+ we are normal server then router will send or have sent JOIN notify
+ already. However since we've added the client already to our channel
+ 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_NOTIFY_TYPE_JOIN, 2,
+ clidp->data, clidp->len,
+ chidp->data, chidp->len);
+
+ if (!cmd->pending) {
+ /* Send JOIN notify packet to our primary router */
+ if (!server->standalone)
+ silc_server_send_notify_join(server, server->router->connection,
+ server->server_type == SILC_ROUTER ?
+ TRUE : FALSE, channel, client->id);
+
+ if (keyp)
+ /* 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);
+ silc_buffer_free(clidp);
+ silc_buffer_free(chidp);
+ silc_buffer_free(keyp);
+ silc_buffer_free(user_list);
+ silc_buffer_free(mode_list);
+
+ out:
+ silc_free(passphrase);
+}
+
+/* Server side of command JOIN. Joins client into requested channel. If
+ the channel does not exist it will be created. */
+
+SILC_SERVER_CMD_FUNC(join)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ 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, 2, 6);
+
+ /* Get channel name */
+ tmp = silc_argument_get_arg_type(cmd->args, 1, &tmp_len);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+ channel_name = tmp;
+
+ if (tmp_len > 256)
+ channel_name[255] = '\0';
+
+ if (silc_server_name_bad_chars(channel_name, tmp_len) == TRUE) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_BAD_CHANNEL);
+ goto out;
+ }
+
+ /* Get Client ID of the client who is joining to the channel */
+ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+ client_id = silc_id_payload_parse_id(tmp, tmp_len);
+ if (!client_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ /* 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,
+ channel_name, NULL);
+
+ if (cmd->sock->type == SILC_SOCKET_TYPE_CLIENT) {
+ SilcClientEntry entry = (SilcClientEntry)cmd->sock->user_data;
+ client_id = silc_id_dup(entry->id, SILC_ID_CLIENT);
+
+ if (!channel || channel->disabled) {
+ /* Channel not found */
+
+ /* If we are standalone server we don't have a router, we just create
+ the channel by ourselves. */
+ if (server->standalone) {
+ channel = silc_server_create_new_channel(server, server->id, cipher,
+ hmac, channel_name, TRUE);
+ if (!channel) {
+ silc_server_command_send_status_reply(
+ cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+ goto out;
+ }
+
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ created = TRUE;
+ create_key = FALSE;
+
+ } else {
+
+ /* The channel does not exist on our server. If we are normal server
+ we will send JOIN command to our router which will handle the
+ joining procedure (either creates the channel if it doesn't exist
+ or joins the client to it). */
+ if (server->server_type != SILC_ROUTER) {
+ SilcBuffer tmpbuf;
+ uint16 old_ident;
+
+ /* If this is pending command callback then we've resolved
+ it and it didn't work, return since we've notified the
+ client already in the command reply callback. */
+ if (cmd->pending)
+ goto out;
+
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ /* Send JOIN command to our router */
+ silc_server_packet_send(server, (SilcSocketConnection)
+ server->router->connection,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, tmpbuf->len, TRUE);
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_JOIN,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_join,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
+ }
+
+ /* We are router and the channel does not seem exist so we will check
+ our global list as well for the channel. */
+ channel = silc_idlist_find_channel_by_name(server->global_list,
+ channel_name, NULL);
+ if (!channel) {
+ /* Channel really does not exist, create it */
+ channel = silc_server_create_new_channel(server, server->id, cipher,
+ hmac, channel_name, TRUE);
+ if (!channel) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+ goto out;
+ }
+
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ created = TRUE;
+ create_key = FALSE;
+ }
+ }
+ }
+ } else {
+ if (!channel) {
+ /* Channel not found */
+
+ /* If the command came from router and we are normal server then
+ something went wrong with the joining as the channel was not found.
+ We can't do anything else but ignore this. */
+ if (cmd->sock->type == SILC_SOCKET_TYPE_ROUTER ||
+ server->server_type != SILC_ROUTER)
+ goto out;
+
+ /* We are router and the channel does not seem exist so we will check
+ our global list as well for the channel. */
+ channel = silc_idlist_find_channel_by_name(server->global_list,
+ channel_name, NULL);
+ if (!channel) {
+ /* Channel really does not exist, create it */
+ channel = silc_server_create_new_channel(server, server->id, cipher,
+ hmac, channel_name, TRUE);
+ if (!channel) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+ goto out;
+ }
+
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+ created = TRUE;
+ create_key = FALSE;
+ }
+ }
+ }
+
+ /* Check whether the channel was created by our router */
+ if (cmd->pending && context2) {
+ SilcServerCommandReplyContext reply =
+ (SilcServerCommandReplyContext)context2;
+
+ if (silc_command_get(reply->payload) == SILC_COMMAND_JOIN) {
+ tmp = silc_argument_get_arg_type(reply->args, 6, NULL);
+ SILC_GET32_MSB(created, tmp);
+ if (silc_argument_get_arg_type(reply->args, 7, NULL))
+ create_key = FALSE; /* Router returned the key already */
+ }
+
+ if (silc_command_get(reply->payload) == SILC_COMMAND_WHOIS &&
+ !silc_hash_table_count(channel->user_list))
+ created = TRUE;
+ }
+
+ /* If the channel does not have global users and is also empty the client
+ will be the channel founder and operator. */
+ if (!channel->global_users && !silc_hash_table_count(channel->user_list))
+ umode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+
+ /* Join to the channel */
+ silc_server_command_join_channel(server, cmd, channel, client_id,
+ created, create_key, umode,
+ auth, auth_len);
+
+ silc_free(client_id);
+
+ out:
+ silc_server_command_free(cmd);
+}
+
+/* Server side of command MOTD. Sends server's current "message of the
+ day" to the client. */
+
+SILC_SERVER_CMD_FUNC(motd)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcBuffer packet, idp;
+ char *motd, *dest_server;
+ uint32 motd_len;
+ uint16 ident = silc_command_get_ident(cmd->payload);
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_MOTD, cmd, 1, 1);
+
+ /* Get server name */
+ dest_server = silc_argument_get_arg_type(cmd->args, 1, NULL);
+ if (!dest_server) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_MOTD,
+ SILC_STATUS_ERR_NO_SUCH_SERVER);
+ goto out;
+ }
+
+ if (!strncasecmp(dest_server, server->server_name, strlen(dest_server))) {
+ /* Send our MOTD */
+
+ idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
+
+ if (server->config && server->config->server_info &&
+ server->config->server_info->motd_file) {
+ /* Send motd */
+ motd = silc_file_readfile(server->config->server_info->motd_file, &motd_len);
+ if (!motd)
+ goto out;
+
+ motd[motd_len] = 0;
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
+ SILC_STATUS_OK, ident, 2,
+ 2, idp, idp->len,
+ 3, motd, motd_len);
+ } else {
+ /* No motd */
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
+ SILC_STATUS_OK, ident, 1,
+ 2, idp, idp->len);
+ }
+
+ silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
+ packet->data, packet->len, FALSE);
+ silc_buffer_free(packet);
+ silc_buffer_free(idp);
+ } else {
+ SilcServerEntry entry;
+
+ /* Check whether we have this server cached */
+ entry = silc_idlist_find_server_by_name(server->global_list,
+ dest_server, TRUE, NULL);
+ if (!entry) {
+ entry = silc_idlist_find_server_by_name(server->local_list,
+ dest_server, TRUE, NULL);
+ }
+
+ if (server->server_type != SILC_SERVER && !cmd->pending &&
+ entry && !entry->motd) {
+ /* Send to the server */
+ SilcBuffer tmpbuf;
+ uint16 old_ident;
+
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ silc_server_packet_send(server, entry->connection,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, tmpbuf->len, TRUE);
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_MOTD,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_motd,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
+ }
+
+ if (!entry && !cmd->pending && !server->standalone) {
+ /* Send to the primary router */
+ SilcBuffer tmpbuf;
+ uint16 old_ident;
+
+ old_ident = silc_command_get_ident(cmd->payload);
+ silc_command_set_ident(cmd->payload, ++server->cmd_ident);
+ tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+
+ silc_server_packet_send(server, server->router->connection,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ tmpbuf->data, tmpbuf->len, TRUE);
+
+ /* Reprocess this packet after received reply from router */
+ silc_server_command_pending(server, SILC_COMMAND_MOTD,
+ silc_command_get_ident(cmd->payload),
+ silc_server_command_motd,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+ silc_command_set_ident(cmd->payload, old_ident);
+ silc_buffer_free(tmpbuf);
+ goto out;
+ }
+
+ if (!entry) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_INFO,
+ SILC_STATUS_ERR_NO_SUCH_SERVER);
+ goto out;
+ }
+
+ idp = silc_id_payload_encode(server->id_entry->id, SILC_ID_SERVER);
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_MOTD,
+ SILC_STATUS_OK, ident, 2,
+ 2, idp, idp->len,
+ 3, entry->motd,
+ entry->motd ?
+ strlen(entry->motd) : 0);
+ silc_server_packet_send(server, cmd->sock, SILC_PACKET_COMMAND_REPLY, 0,
+ packet->data, packet->len, FALSE);
+ silc_buffer_free(packet);
+ silc_buffer_free(idp);
+ }
+
+ out:
+ silc_server_command_free(cmd);
+}
+
+/* Server side of command UMODE. Client can use this command to set/unset
+ user mode. Client actually cannot set itself to be as server/router
+ operator so this can be used only to unset the modes. */
+
+SILC_SERVER_CMD_FUNC(umode)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+ SilcBuffer packet;
+ unsigned char *tmp_mask;
+ uint32 mask;
+ uint16 ident = silc_command_get_ident(cmd->payload);
+
+ if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT)
+ goto out;
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_UMODE, cmd, 2, 2);
+
+ /* Get the client's 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_UMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+ SILC_GET32_MSB(mask, tmp_mask);
+
+ /*
+ * Change the mode
+ */
+
+ if (mask & SILC_UMODE_SERVER_OPERATOR) {
+ if (!(client->mode & SILC_UMODE_SERVER_OPERATOR)) {
+ /* Cannot operator mode */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+ SILC_STATUS_ERR_PERM_DENIED);
+ goto out;
+ }
+ } else {
+ /* Remove the server operator rights */
+ if (client->mode & SILC_UMODE_SERVER_OPERATOR) {
+ client->mode &= ~SILC_UMODE_SERVER_OPERATOR;
+ if (client->connection)
+ server->stat.my_server_ops--;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.server_ops--;
+ }
+ }
+
+ if (mask & SILC_UMODE_ROUTER_OPERATOR) {
+ if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+ /* Cannot operator mode */
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_UMODE,
+ SILC_STATUS_ERR_PERM_DENIED);
+ goto out;
+ }
+ } else {
+ /* Remove the router operator rights */
+ if (client->mode & SILC_UMODE_ROUTER_OPERATOR) {
+ client->mode &= ~SILC_UMODE_ROUTER_OPERATOR;
+ if (client->connection)
+ server->stat.my_router_ops--;
+ if (server->server_type == SILC_ROUTER)
+ server->stat.router_ops--;
+ }
+ }
+
+ if (mask & SILC_UMODE_GONE) {
+ client->mode |= SILC_UMODE_GONE;
+ } else {
+ if (client->mode & SILC_UMODE_GONE)
+ /* Remove the gone status */
+ client->mode &= ~SILC_UMODE_GONE;
+ }
+
+ /* Send UMODE change to primary router */
+ if (!server->standalone)
+ silc_server_send_notify_umode(server, server->router->connection, TRUE,
+ client->id, client->mode);
+
+ /* Send command reply to sender */
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_UMODE,
+ SILC_STATUS_OK, ident, 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);
+
+ out:
+ silc_server_command_free(cmd);
+}
+
+/* Checks that client has rights to add or remove channel modes. If any
+ of the checks fails FALSE is returned. */
+
+int silc_server_check_cmode_rights(SilcChannelEntry channel,
+ SilcChannelClientEntry client,
+ uint32 mode)
+{
+ int is_op = client->mode & SILC_CHANNEL_UMODE_CHANOP;
+ int is_fo = client->mode & SILC_CHANNEL_UMODE_CHANFO;
+
+ /* Check whether has rights to change anything */
+ if (!is_op && !is_fo)
+ return FALSE;
+
+ /* Check whether has rights to change everything */
+ if (is_op && is_fo)
+ return TRUE;
+
+ /* We know that client is channel operator, check that they are not
+ changing anything that requires channel founder rights. Rest of the
+ modes are available automatically for channel operator. */
+
+ if (mode & SILC_CHANNEL_MODE_PRIVKEY) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
+ if (is_op && !is_fo)
+ return FALSE;
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+ if (is_op && !is_fo)
+ return FALSE;
+ }
+ }
+
+ if (mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE))
+ if (is_op && !is_fo)
+ return FALSE;
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+ if (is_op && !is_fo)
+ return FALSE;
+ }
+ }
+
+ if (mode & SILC_CHANNEL_MODE_CIPHER) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER))
+ if (is_op && !is_fo)
+ return FALSE;
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
+ if (is_op && !is_fo)
+ return FALSE;
+ }
+ }
+
+ if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH))
+ if (is_op && !is_fo)
+ return FALSE;
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+ if (is_op && !is_fo)
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Server side command of CMODE. Changes channel mode */
+
+SILC_SERVER_CMD_FUNC(cmode)
+{
+ SilcServerCommandContext cmd = (SilcServerCommandContext)context;
+ SilcServer server = cmd->server;
+ SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+ SilcIDListData idata = (SilcIDListData)client;
+ SilcChannelID *channel_id;
+ SilcChannelEntry channel;
+ SilcChannelClientEntry chl;
+ SilcBuffer packet, cidp;
+ unsigned char *tmp, *tmp_id, *tmp_mask;
+ char *cipher = NULL, *hmac = NULL, *passphrase = NULL;
+ uint32 mode_mask, tmp_len, tmp_len2;
+ uint16 ident = silc_command_get_ident(cmd->payload);
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CMODE, cmd, 2, 7);
+
+ /* 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);
+ if (!channel_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_ID);
+ goto out;
+ }
+
+ /* 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, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ 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_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
+
+ /* 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 */
+ } 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. */
+
+ /* Re-generate channel key */
+ if (!silc_server_create_channel_key(server, channel, 0))
+ goto out;
+
+ /* Send the channel key. This sends it to our local clients and if
+ we are normal server to our router as well. */
+ silc_server_send_channel_key(server, NULL, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+
+ cipher = channel->channel_key->cipher->name;
+ hmac = (char *)silc_hmac_get_name(channel->hmac);
+ }
+ }
+
+ if (mode_mask & SILC_CHANNEL_MODE_ULIMIT) {
+ /* User limit is set on channel */
+ uint32 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->user_limit = user_limit;
+ }
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+ /* User limit mode is unset. Remove user limit */
+ channel->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 */
+ passphrase = channel->passphrase = strdup(tmp);
+ }
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) {
+ /* Passphrase mode is unset. remove the passphrase */
+ if (channel->passphrase) {
+ silc_free(channel->passphrase);
+ channel->passphrase = NULL;
+ }
+ }
+ }
+
+ if (mode_mask & SILC_CHANNEL_MODE_CIPHER) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) {
+ /* Cipher to use protect the traffic */
+ SilcCipher newkey, oldkey;
+
+ /* Get cipher */
+ cipher = silc_argument_get_arg_type(cmd->args, 5, NULL);
+ if (!cipher) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ /* Delete old cipher and allocate the new one */
+ if (!silc_cipher_alloc(cipher, &newkey)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+ goto out;
+ }
+
+ oldkey = channel->channel_key;
+ channel->channel_key = newkey;
+
+ /* Re-generate channel key */
+ if (!silc_server_create_channel_key(server, channel, 0)) {
+ /* We don't have new key, revert to old one */
+ channel->channel_key = oldkey;
+ goto out;
+ }
+
+ /* Remove old channel key for good */
+ silc_cipher_free(oldkey);
+
+ /* Send the channel key. This sends it to our local clients and if
+ we are normal server to our router as well. */
+ silc_server_send_channel_key(server, NULL, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ }
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_CIPHER) {
+ /* Cipher mode is unset. Remove the cipher and revert back to
+ default cipher */
+ SilcCipher newkey, oldkey;
+ cipher = channel->cipher;
+
+ /* Delete old cipher and allocate default one */
+ if (!silc_cipher_alloc(cipher ? cipher : SILC_DEFAULT_CIPHER, &newkey)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+ goto out;
+ }
+
+ oldkey = channel->channel_key;
+ channel->channel_key = newkey;
+
+ /* Re-generate channel key */
+ if (!silc_server_create_channel_key(server, channel, 0)) {
+ /* We don't have new key, revert to old one */
+ channel->channel_key = oldkey;
+ goto out;
+ }
+
+ /* Remove old channel key for good */
+ silc_cipher_free(oldkey);
+
+ /* Send the channel key. This sends it to our local clients and if
+ we are normal server to our router as well. */
+ silc_server_send_channel_key(server, NULL, channel,
+ server->server_type == SILC_ROUTER ?
+ FALSE : !server->standalone);
+ }
+ }
+
+ if (mode_mask & SILC_CHANNEL_MODE_HMAC) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_HMAC)) {
+ /* HMAC to use protect the traffic */
+ unsigned char hash[32];
+ SilcHmac newhmac;
+
+ /* Get hmac */
+ hmac = silc_argument_get_arg_type(cmd->args, 6, NULL);
+ if (!hmac) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ /* Delete old hmac and allocate the new one */
+ if (!silc_hmac_alloc(hmac, NULL, &newhmac)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+ goto out;
+ }
+
+ silc_hmac_free(channel->hmac);
+ channel->hmac = newhmac;
+
+ /* Set the HMAC key out of current channel key. The client must do
+ this locally. */
+ silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key,
+ channel->key_len / 8, hash);
+ silc_hmac_set_key(channel->hmac, hash,
+ silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+ memset(hash, 0, sizeof(hash));
+ }
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_HMAC) {
+ /* Hmac mode is unset. Remove the hmac and revert back to
+ default hmac */
+ SilcHmac newhmac;
+ unsigned char hash[32];
+ hmac = channel->hmac_name;
+
+ /* Delete old hmac and allocate default one */
+ silc_hmac_free(channel->hmac);
+ if (!silc_hmac_alloc(hmac ? hmac : SILC_DEFAULT_HMAC, NULL, &newhmac)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
+ goto out;
+ }
+
+ silc_hmac_free(channel->hmac);
+ channel->hmac = newhmac;
+
+ /* Set the HMAC key out of current channel key. The client must do
+ this locally. */
+ silc_hash_make(silc_hmac_get_hash(channel->hmac), channel->key,
+ channel->key_len / 8,
+ hash);
+ silc_hmac_set_key(channel->hmac, hash,
+ silc_hash_len(silc_hmac_get_hash(channel->hmac)));
+ memset(hash, 0, sizeof(hash));
+ }
+ }
+
+ if (mode_mask & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) {
+ /* Set the founder authentication */
+ SilcAuthPayload auth;
+
+ tmp = silc_argument_get_arg_type(cmd->args, 7, &tmp_len);
+ if (!tmp) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ auth = silc_auth_payload_parse(tmp, tmp_len);
+ if (!auth) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ /* Save the public key */
+ tmp = silc_pkcs_public_key_encode(idata->public_key, &tmp_len);
+ silc_pkcs_public_key_decode(tmp, tmp_len, &channel->founder_key);
+ silc_free(tmp);
+
+ channel->founder_method = silc_auth_get_method(auth);
+
+ if (channel->founder_method == SILC_AUTH_PASSWORD) {
+ tmp = silc_auth_get_data(auth, &tmp_len);
+ channel->founder_passwd =
+ silc_calloc(tmp_len + 1, sizeof(*channel->founder_passwd));
+ memcpy(channel->founder_passwd, tmp, tmp_len);
+ channel->founder_passwd_len = tmp_len;
+ } else {
+ /* Verify the payload before setting the mode */
+ if (!silc_auth_verify(auth, channel->founder_method,
+ channel->founder_key, 0, idata->hash,
+ client->id, SILC_ID_CLIENT)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CMODE,
+ SILC_STATUS_ERR_AUTH_FAILED);
+ goto out;
+ }
+ }
+
+ silc_auth_payload_free(auth);
+ }
+ }
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANFO) {
+ if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) {
+ if (channel->founder_key)
+ silc_pkcs_public_key_free(channel->founder_key);
+ if (channel->founder_passwd) {
+ silc_free(channel->founder_passwd);
+ channel->founder_passwd = NULL;
+ }
+ }
+ }
+ }
+
+ /* 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, NULL, channel, FALSE,
+ SILC_NOTIFY_TYPE_CMODE_CHANGE, 5,
+ cidp->data, cidp->len,
+ tmp_mask, 4,
+ cipher, cipher ? strlen(cipher) : 0,
+ hmac, hmac ? strlen(hmac) : 0,
+ passphrase, passphrase ?
+ strlen(passphrase) : 0);
+
+ /* Set CMODE notify type to network */
+ if (!server->standalone)
+ silc_server_send_notify_cmode(server, server->router->connection,
+ server->server_type == SILC_ROUTER ?
+ TRUE : FALSE, channel,
+ mode_mask, client->id, SILC_ID_CLIENT,
+ cipher, hmac, passphrase);
+
+ /* Send command reply to sender */
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CMODE,
+ SILC_STATUS_OK, ident, 2,
+ 2, tmp_id, tmp_len2,
+ 3, 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);
+ silc_buffer_free(cidp);
+
+ 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;
+ SilcClientEntry client = (SilcClientEntry)cmd->sock->user_data;
+ SilcIDListData idata = (SilcIDListData)client;
+ SilcChannelID *channel_id;
+ SilcClientID *client_id;
+ SilcChannelEntry channel;
+ SilcClientEntry target_client;
+ SilcChannelClientEntry chl;
+ SilcBuffer packet, idp;
+ unsigned char *tmp_id, *tmp_ch_id, *tmp_mask;
+ uint32 target_mask, sender_mask = 0, tmp_len, tmp_ch_len;
+ int notify = FALSE;
+ uint16 ident = silc_command_get_ident(cmd->payload);
+
+ SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_CUMODE, cmd, 3, 4);
+
+ /* Get Channel ID */
+ tmp_ch_id = silc_argument_get_arg_type(cmd->args, 1, &tmp_ch_len);
+ if (!tmp_ch_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_ch_id, tmp_ch_len);
+ if (!channel_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_ID);
+ goto out;
+ }
+
+ /* Get channel entry */
+ channel = silc_idlist_find_channel_by_id(server->local_list,
+ channel_id, NULL);
+ if (!channel) {
+ channel = silc_idlist_find_channel_by_id(server->global_list,
+ channel_id, NULL);
+ 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_hash_table_find(channel->user_list, client, NULL, (void *)&chl);
+ sender_mask = chl->mode;
+
+ /* 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_CLIENT_ID);
+ goto out;
+ }
+ client_id = silc_id_payload_parse_id(tmp_id, tmp_len);
+ if (!client_id) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CLIENT_ID);
+ goto out;
+ }
+
+ /* Get target client's entry */
+ target_client = silc_idlist_find_client_by_id(server->local_list,
+ client_id, TRUE, NULL);
+ if (!target_client) {
+ target_client = silc_idlist_find_client_by_id(server->global_list,
+ client_id, TRUE, NULL);
+ }
+
+ if (target_client != client &&
+ !(sender_mask & SILC_CHANNEL_UMODE_CHANFO) &&
+ !(sender_mask & SILC_CHANNEL_UMODE_CHANOP)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+ goto out;
+ }
+
+ /* Check whether target client is on the channel */
+ if (target_client != client) {
+ 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_hash_table_find(channel->user_list, target_client, NULL,
+ (void *)&chl);
+ }
+
+ /*
+ * 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 && client != target_client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+ goto out;
+ }
+
+ if (target_mask & SILC_CHANNEL_UMODE_CHANFO) {
+ if (!(chl->mode & SILC_CHANNEL_UMODE_CHANFO)) {
+ /* The client tries to claim the founder rights. */
+ unsigned char *tmp_auth;
+ uint32 tmp_auth_len, auth_len;
+ void *auth;
+
+ if (target_client != client) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_YOU);
+ goto out;
+ }
+
+ if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) ||
+ !channel->founder_key || !idata->public_key ||
+ !silc_pkcs_public_key_compare(channel->founder_key,
+ idata->public_key)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_YOU);
+ goto out;
+ }
+
+ tmp_auth = silc_argument_get_arg_type(cmd->args, 4, &tmp_auth_len);
+ if (!tmp_auth) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ goto out;
+ }
+
+ auth = (channel->founder_method == SILC_AUTH_PASSWORD ?
+ (void *)channel->founder_passwd : (void *)channel->founder_key);
+ auth_len = (channel->founder_method == SILC_AUTH_PASSWORD ?
+ channel->founder_passwd_len : 0);
+
+ if (!silc_auth_verify_data(tmp_auth, tmp_auth_len,
+ channel->founder_method, auth, auth_len,
+ idata->hash, client->id, SILC_ID_CLIENT)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_AUTH_FAILED);
+ goto out;
+ }
+
+ sender_mask = chl->mode |= SILC_CHANNEL_UMODE_CHANFO;
+ notify = TRUE;
+ }
+ } 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)) {
+ if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
+ !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+ goto out;
+ }
+
+ chl->mode |= SILC_CHANNEL_UMODE_CHANOP;
+ notify = TRUE;
+ }
+ } else {
+ if (chl->mode & SILC_CHANNEL_UMODE_CHANOP) {
+ if (!(sender_mask & SILC_CHANNEL_UMODE_CHANOP) &&
+ !(sender_mask & SILC_CHANNEL_UMODE_CHANFO)) {
+ silc_server_command_send_status_reply(cmd, SILC_COMMAND_CUMODE,
+ SILC_STATUS_ERR_NO_CHANNEL_PRIV);
+ goto out;
+ }
+
+ /* Demote to normal user */
+ chl->mode &= ~SILC_CHANNEL_UMODE_CHANOP;
+ notify = TRUE;
+ }
+ }
+
+ idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT);
+ tmp_id = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
+
+ /* Send notify to channel, notify only if mode was actually changed. */
+ if (notify) {
+ silc_server_send_notify_to_channel(server, NULL, channel, FALSE,
+ SILC_NOTIFY_TYPE_CUMODE_CHANGE, 3,
+ idp->data, idp->len,
+ tmp_mask, 4,
+ tmp_id, tmp_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,
+ target_mask, client->id,
+ SILC_ID_CLIENT,
+ target_client->id);
+ }
+
+ /* Send command reply to sender */
+ packet = silc_command_reply_payload_encode_va(SILC_COMMAND_CUMODE,
+ SILC_STATUS_OK, ident, 3,
+ 2, tmp_mask, 4,
+ 3, tmp_ch_id, tmp_ch_len,
+ 4, 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);
+ silc_buffer_free(idp);
+
+ out:
+ silc_server_command_free(cmd);