X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=apps%2Fsilcd%2Fcommand_reply.c;h=a72e6c206a8ccdef7d0f727f6fe7ec6fbbd94fba;hp=8e99cb38158de0f0cc355a7e2459643fdf20cb72;hb=382d15d447b7a95390decfa783836ae4fe255b3d;hpb=692b5f0e37647edafb7317ac1d3b164a6285194a diff --git a/apps/silcd/command_reply.c b/apps/silcd/command_reply.c index 8e99cb38..a72e6c20 100644 --- a/apps/silcd/command_reply.c +++ b/apps/silcd/command_reply.c @@ -2,9 +2,9 @@ command_reply.c - Author: Pekka Riikonen + Author: Pekka Riikonen - Copyright (C) 1997 - 2001 Pekka Riikonen + Copyright (C) 1997 - 2002 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,37 +23,36 @@ #include "server_internal.h" #include "command_reply.h" -#define COMMAND_CHECK_STATUS \ -do { \ - SILC_LOG_DEBUG(("Start")); \ - SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \ - if (status != SILC_STATUS_OK) { \ - silc_server_command_reply_free(cmd); \ - return; \ - } \ -} while(0) - -#define COMMAND_CHECK_STATUS_LIST \ -do { \ - SILC_LOG_DEBUG(("Start")); \ - SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \ - if (status != SILC_STATUS_OK && \ - status != SILC_STATUS_LIST_START && \ - status != SILC_STATUS_LIST_ITEM && \ - status != SILC_STATUS_LIST_END) { \ - silc_server_command_reply_free(cmd); \ - return; \ - } \ +/* All functions that call the COMMAND_CHECK_STATUS macros must have + out: and err: goto labels. */ + +#define COMMAND_CHECK_STATUS \ +do { \ + SILC_LOG_DEBUG(("Start")); \ + if (!silc_command_get_status(cmd->payload, &status, &error)) { \ + if (SILC_STATUS_IS_ERROR(status)) \ + goto out; \ + if (status == SILC_STATUS_LIST_END) \ + goto out; \ + goto err; \ + } \ } while(0) /* Server command reply list. Not all commands have reply function as they are never sent by server. More maybe added later if need appears. */ SilcServerCommandReply silc_command_reply_list[] = { - SILC_SERVER_CMD_REPLY(join, JOIN), SILC_SERVER_CMD_REPLY(whois, WHOIS), + SILC_SERVER_CMD_REPLY(whowas, WHOWAS), SILC_SERVER_CMD_REPLY(identify, IDENTIFY), + SILC_SERVER_CMD_REPLY(info, INFO), + SILC_SERVER_CMD_REPLY(motd, MOTD), + SILC_SERVER_CMD_REPLY(join, JOIN), + SILC_SERVER_CMD_REPLY(stats, STATS), SILC_SERVER_CMD_REPLY(users, USERS), + SILC_SERVER_CMD_REPLY(getkey, GETKEY), + SILC_SERVER_CMD_REPLY(list, LIST), + SILC_SERVER_CMD_REPLY(watch, WATCH), { NULL, 0 }, }; @@ -68,12 +67,11 @@ void silc_server_command_reply_process(SilcServer server, SilcServerCommandReplyContext ctx; SilcCommandPayload payload; SilcCommand command; - unsigned short ident; SILC_LOG_DEBUG(("Start")); /* Get command reply payload from packet */ - payload = silc_command_payload_parse(buffer); + payload = silc_command_payload_parse(buffer->data, buffer->len); if (!payload) { /* Silently ignore bad reply packet */ SILC_LOG_DEBUG(("Bad command reply packet")); @@ -84,27 +82,35 @@ void silc_server_command_reply_process(SilcServer server, command reply routine receiving it. */ ctx = silc_calloc(1, sizeof(*ctx)); ctx->server = server; - ctx->sock = sock; + ctx->sock = silc_socket_dup(sock); ctx->payload = payload; ctx->args = silc_command_get_args(ctx->payload); - ident = silc_command_get_ident(ctx->payload); + ctx->ident = silc_command_get_ident(ctx->payload); + command = silc_command_get(ctx->payload); + + /* Client is not allowed to send reply to all commands */ + if (sock->type == SILC_SOCKET_TYPE_CLIENT && + command != SILC_COMMAND_WHOIS) { + silc_server_command_reply_free(ctx); + return; + } /* Check for pending commands and mark to be exeucted */ - silc_server_command_pending_check(server, ctx, - silc_command_get(ctx->payload), ident); + ctx->callbacks = + silc_server_command_pending_check(server, command, + ctx->ident, &ctx->callbacks_count); /* Execute command reply */ - command = silc_command_get(ctx->payload); for (cmd = silc_command_reply_list; cmd->cb; cmd++) if (cmd->cmd == command) break; if (cmd == NULL || !cmd->cb) { - silc_free(ctx); + silc_server_command_reply_free(ctx); return; } - cmd->cb(ctx); + cmd->cb(ctx, NULL); } /* Free command reply context and its internals. */ @@ -112,105 +118,206 @@ void silc_server_command_reply_process(SilcServer server, void silc_server_command_reply_free(SilcServerCommandReplyContext cmd) { if (cmd) { - silc_command_free_payload(cmd->payload); + silc_command_payload_free(cmd->payload); + if (cmd->sock) + silc_socket_free(cmd->sock); /* Decrease the reference counter */ + silc_free(cmd->callbacks); silc_free(cmd); } } -/* Caches the received WHOIS information. If we are normal server currently - we cache global information only for short period of time. */ -/* XXX cache expirying not implemented yet! */ +static void +silc_server_command_process_error(SilcServerCommandReplyContext cmd, + SilcStatus error) +{ + SilcServer server = cmd->server; + + /* If we received notify for invalid ID we'll remove the ID if we + have it cached. */ + if (error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID && + cmd->sock->type == SILC_SOCKET_TYPE_ROUTER) { + SilcClientEntry client; + SilcUInt32 tmp_len; + unsigned char *tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); + if (tmp) { + SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); + if (client_id) { + SILC_LOG_DEBUG(("Received invalid client ID notification, deleting " + "the entry from cache")); + client = silc_idlist_find_client_by_id(server->global_list, + client_id, FALSE, NULL); + if (client) { + silc_server_remove_from_channels(server, NULL, client, TRUE, + NULL, TRUE, FALSE); + silc_idlist_del_data(client); + silc_idlist_del_client(server->global_list, client); + } + silc_free(client_id); + } + } + } +} + +/* Caches the received WHOIS information. */ static char silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) { SilcServer server = cmd->server; - int len, id_len; - unsigned char *id_data; - char *nickname, *username, *realname; + unsigned char *tmp, *id_data, *umodes; + char *nickname, *username, *realname, *servername = NULL; + unsigned char *fingerprint; SilcClientID *client_id; SilcClientEntry client; SilcIDCacheEntry cache = NULL; char global = FALSE; char *nick; + SilcUInt32 mode = 0, len, len2, id_len, flen; id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len); nickname = silc_argument_get_arg_type(cmd->args, 3, &len); username = silc_argument_get_arg_type(cmd->args, 4, &len); realname = silc_argument_get_arg_type(cmd->args, 5, &len); - if (!id_data || !nickname || !username || !realname) + if (!id_data || !nickname || !username || !realname) return FALSE; - client_id = silc_id_payload_parse_id(id_data, id_len); + tmp = silc_argument_get_arg_type(cmd->args, 7, &len); + if (tmp) + SILC_GET32_MSB(mode, tmp); + + client_id = silc_id_payload_parse_id(id_data, id_len, NULL); if (!client_id) return FALSE; + fingerprint = silc_argument_get_arg_type(cmd->args, 9, &flen); + /* Check if we have this client cached already. */ - client = silc_idlist_find_client_by_id(server->local_list, client_id, - &cache); + client = silc_idlist_find_client_by_id(server->local_list, client_id, + FALSE, NULL); if (!client) { - client = silc_idlist_find_client_by_id(server->global_list, - client_id, &cache); + client = silc_idlist_find_client_by_id(server->global_list, client_id, + FALSE, NULL); global = TRUE; } if (!client) { /* If router did not find such Client ID in its lists then this must be bogus client or some router in the net is buggy. */ - if (server->server_type == SILC_ROUTER) + if (server->server_type != SILC_SERVER) return FALSE; /* Take hostname out of nick string if it includes it. */ - if (strchr(nickname, '@')) { - int len = strcspn(nickname, "@"); - nick = silc_calloc(len + 1, sizeof(char)); - memcpy(nick, nickname, len); - } else { - nick = strdup(nickname); - } + silc_parse_userfqdn(nickname, &nick, &servername); /* We don't have that client anywhere, add it. The client is added to global list since server didn't have it in the lists so it must be global. */ - silc_idlist_add_client(server->global_list, nick, - strdup(username), - strdup(realname), client_id, - cmd->sock->user_data, NULL); + client = silc_idlist_add_client(server->global_list, nick, + strdup(username), + strdup(realname), client_id, + cmd->sock->user_data, NULL, 0); + if (!client) { + SILC_LOG_ERROR(("Could not add new client to the ID Cache")); + return FALSE; + } + + client->data.status |= + (SILC_IDLIST_STATUS_REGISTERED | SILC_IDLIST_STATUS_RESOLVED); + client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; + client->mode = mode; + client->servername = servername; } else { /* We have the client already, update the data */ SILC_LOG_DEBUG(("Updating client data")); /* Take hostname out of nick string if it includes it. */ - if (strchr(nickname, '@')) { - int len = strcspn(nickname, "@"); - nick = silc_calloc(len + 1, sizeof(char)); - memcpy(nick, nickname, len); - } else { - nick = strdup(nickname); - } + silc_parse_userfqdn(nickname, &nick, &servername); + + /* Remove the old cache entry */ + silc_idcache_del_by_context(global ? server->global_list->clients : + server->local_list->clients, client); - if (client->nickname) - silc_free(client->nickname); - if (client->username) - silc_free(client->username); - if (client->userinfo) - silc_free(client->userinfo); + silc_free(client->nickname); + silc_free(client->username); + silc_free(client->userinfo); + silc_free(client->servername); client->nickname = nick; client->username = strdup(username); client->userinfo = strdup(realname); + client->servername = servername; + client->mode = mode; + client->data.status |= SILC_IDLIST_STATUS_RESOLVED; + client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; + + /* Create new cache entry */ + silc_idcache_add(global ? server->global_list->clients : + server->local_list->clients, nick, client->id, + client, 0, NULL); + silc_free(client_id); + } - if (cache) { - cache->data = nick; - silc_idcache_sort_by_data(global ? server->global_list->clients : - server->local_list->clients); + /* Save channel list if it was sent to us */ + if (server->server_type == SILC_SERVER) { + tmp = silc_argument_get_arg_type(cmd->args, 6, &len); + umodes = silc_argument_get_arg_type(cmd->args, 10, &len2); + if (tmp && umodes) { + SilcBufferStruct channels_buf, umodes_buf; + silc_buffer_set(&channels_buf, tmp, len); + silc_buffer_set(&umodes_buf, umodes, len2); + silc_server_save_user_channels(server, cmd->sock, client, &channels_buf, + &umodes_buf); + } else { + silc_server_save_user_channels(server, cmd->sock, client, NULL, NULL); } - silc_free(client_id); + /* If client is global and is not on any channel then add that we'll + expire the entry after a while. */ + if (global) { + silc_idlist_find_client_by_id(server->global_list, client->id, + FALSE, &cache); + if (!silc_hash_table_count(client->channels)) + cache->expire = time(NULL) + 300; + else + cache->expire = 0; + } } + if (fingerprint && flen == sizeof(client->data.fingerprint)) + memcpy(client->data.fingerprint, fingerprint, flen); + + /* Take Requested Attributes if set. */ + tmp = silc_argument_get_arg_type(cmd->args, 11, &len); + if (tmp) { + silc_free(client->attrs); + client->attrs = silc_memdup(tmp, len); + client->attrs_len = len; + } + + return TRUE; +} + +/* Handle requested attributes reply in WHOIS from client */ + +static char +silc_server_command_reply_whois_save_client(SilcServerCommandReplyContext cmd) +{ + unsigned char *tmp; + SilcUInt32 len; + SilcClientEntry client = cmd->sock->user_data; + + /* Take Requested Attributes if set. */ + tmp = silc_argument_get_arg_type(cmd->args, 11, &len); + if (tmp && client) { + silc_free(client->attrs); + client->attrs = silc_memdup(tmp, len); + client->attrs_len = len; + } + + client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; + return TRUE; } @@ -223,116 +330,372 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) SILC_SERVER_CMD_REPLY_FUNC(whois) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; - SilcCommandStatus status; + SilcStatus status, error; - COMMAND_CHECK_STATUS_LIST; + COMMAND_CHECK_STATUS; - if (!silc_server_command_reply_whois_save(cmd)) - goto out; + if (cmd->sock->type != SILC_SOCKET_TYPE_CLIENT) { + if (!silc_server_command_reply_whois_save(cmd)) + goto out; + } else { + if (!silc_server_command_reply_whois_save_client(cmd)) + goto out; + } - /* Execute any pending commands */ - SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS); + /* Pending callbacks are not executed if this was an list entry */ + if (status != SILC_STATUS_OK && + status != SILC_STATUS_LIST_END) { + silc_server_command_reply_free(cmd); + return; + } out: - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS); + silc_server_command_process_error(cmd, error); + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS); + silc_server_command_reply_free(cmd); + return; + + err: + silc_server_command_process_error(cmd, error); silc_server_command_reply_free(cmd); } -/* Caches the received IDENTIFY information. */ +/* Caches the received WHOWAS information for a short period of time. */ static char -silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) +silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd) { SilcServer server = cmd->server; - int len, id_len; + SilcUInt32 len, id_len; unsigned char *id_data; - char *nickname, *username; + char *nickname, *username, *realname, *servername = NULL; SilcClientID *client_id; SilcClientEntry client; SilcIDCacheEntry cache = NULL; - char global = FALSE; - char *nick = NULL; + char *nick; + int global = FALSE; id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len); nickname = silc_argument_get_arg_type(cmd->args, 3, &len); username = silc_argument_get_arg_type(cmd->args, 4, &len); - if (!id_data) + if (!id_data || !nickname || !username) return FALSE; - client_id = silc_id_payload_parse_id(id_data, id_len); + realname = silc_argument_get_arg_type(cmd->args, 5, &len); + + client_id = silc_id_payload_parse_id(id_data, id_len, NULL); if (!client_id) return FALSE; /* Check if we have this client cached already. */ client = silc_idlist_find_client_by_id(server->local_list, client_id, - &cache); + FALSE, &cache); if (!client) { client = silc_idlist_find_client_by_id(server->global_list, - client_id, &cache); + client_id, FALSE, &cache); global = TRUE; } if (!client) { /* If router did not find such Client ID in its lists then this must be bogus client or some router in the net is buggy. */ - if (server->server_type == SILC_ROUTER) + if (server->server_type != SILC_SERVER) return FALSE; /* Take hostname out of nick string if it includes it. */ - if (nickname) { - if (strchr(nickname, '@')) { - int len = strcspn(nickname, "@"); - nick = silc_calloc(len + 1, sizeof(char)); - memcpy(nick, nickname, len); - } else { - nick = strdup(nickname); - } - } + silc_parse_userfqdn(nickname, &nick, &servername); /* We don't have that client anywhere, add it. The client is added to global list since server didn't have it in the lists so it must be global. */ - silc_idlist_add_client(server->global_list, nick, - username ? strdup(username) : NULL, NULL, - client_id, cmd->sock->user_data, NULL); + client = silc_idlist_add_client(server->global_list, nick, + strdup(username), strdup(realname), + silc_id_dup(client_id, SILC_ID_CLIENT), + cmd->sock->user_data, NULL, + SILC_ID_CACHE_EXPIRE_DEF); + if (!client) { + SILC_LOG_ERROR(("Could not add new client to the ID Cache")); + return FALSE; + } + + client->data.status |= SILC_IDLIST_STATUS_RESOLVED; + client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; + client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; + client->servername = servername; } else { /* We have the client already, update the data */ - SILC_LOG_DEBUG(("Updating client data")); - /* Take hostname out of nick string if it includes it. */ - if (nickname) { - if (strchr(nickname, '@')) { - int len = strcspn(nickname, "@"); - nick = silc_calloc(len + 1, sizeof(char)); - memcpy(nick, nickname, len); - } else { - nick = strdup(nickname); + silc_parse_userfqdn(nickname, &nick, &servername); + + silc_free(client->nickname); + silc_free(client->username); + silc_free(client->servername); + + client->nickname = nick; + client->username = strdup(username); + client->servername = servername; + client->data.status |= SILC_IDLIST_STATUS_RESOLVED; + client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; + + /* Remove the old cache entry and create a new one */ + silc_idcache_del_by_context(global ? server->global_list->clients : + server->local_list->clients, client); + silc_idcache_add(global ? server->global_list->clients : + server->local_list->clients, nick, client->id, + client, 0, NULL); + } + + /* If client is global and is not on any channel then add that we'll + expire the entry after a while. */ + if (global) { + silc_idlist_find_client_by_id(server->global_list, client->id, + FALSE, &cache); + if (!silc_hash_table_count(client->channels)) + cache->expire = SILC_ID_CACHE_EXPIRE_DEF; + else + cache->expire = 0; + } + + silc_free(client_id); + + return TRUE; +} + +/* Received reply for WHOWAS command. Cache the client information only for + a short period of time. */ + +SILC_SERVER_CMD_REPLY_FUNC(whowas) +{ + SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; + SilcStatus status, error; + + COMMAND_CHECK_STATUS; + + if (!silc_server_command_reply_whowas_save(cmd)) + goto out; + + /* Pending callbacks are not executed if this was an list entry */ + if (status != SILC_STATUS_OK && + status != SILC_STATUS_LIST_END) { + silc_server_command_reply_free(cmd); + return; + } + + out: + silc_server_command_process_error(cmd, error); + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS); + silc_server_command_reply_free(cmd); + return; + + err: + silc_server_command_process_error(cmd, error); + silc_server_command_reply_free(cmd); +} + +/* Caches the received IDENTIFY information. */ + +static char +silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) +{ + SilcServer server = cmd->server; + SilcUInt32 len, id_len; + unsigned char *id_data; + char *name, *info; + SilcClientID *client_id = NULL; + SilcServerID *server_id = NULL; + SilcChannelID *channel_id = NULL; + SilcClientEntry client; + SilcServerEntry server_entry; + SilcChannelEntry channel; + char global = FALSE; + char *nick = NULL; + SilcIDPayload idp = NULL; + SilcIdType id_type; + int expire = 0; + + id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len); + if (!id_data) + return FALSE; + idp = silc_id_payload_parse(id_data, id_len); + if (!idp) + return FALSE; + + name = silc_argument_get_arg_type(cmd->args, 3, &len); + info = silc_argument_get_arg_type(cmd->args, 4, &len); + + id_type = silc_id_payload_get_type(idp); + + switch (id_type) { + case SILC_ID_CLIENT: + client_id = silc_id_payload_get_id(idp); + if (!client_id) + goto error; + + SILC_LOG_DEBUG(("Received client information")); + + client = silc_idlist_find_client_by_id(server->local_list, + client_id, FALSE, NULL); + if (!client) { + client = silc_idlist_find_client_by_id(server->global_list, client_id, + FALSE, NULL); + global = TRUE; + } + if (!client) { + /* If router did not find such Client ID in its lists then this must + be bogus client or some router in the net is buggy. */ + if (server->server_type != SILC_SERVER) + goto error; + + /* Take nickname */ + if (name) + silc_parse_userfqdn(name, &nick, NULL); + + /* We don't have that client anywhere, add it. The client is added + to global list since server didn't have it in the lists so it must be + global. */ + client = silc_idlist_add_client(server->global_list, nick, + info ? strdup(info) : NULL, NULL, + client_id, cmd->sock->user_data, + NULL, time(NULL) + 300); + if (!client) { + SILC_LOG_ERROR(("Could not add new client to the ID Cache")); + goto error; + } + client->data.status |= SILC_IDLIST_STATUS_REGISTERED; + client->data.status |= SILC_IDLIST_STATUS_RESOLVED; + client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; + } else { + /* We have the client already, update the data */ + + SILC_LOG_DEBUG(("Updating client data")); + + /* Take nickname */ + if (name) { + silc_parse_userfqdn(name, &nick, NULL); + + /* Remove the old cache entry */ + silc_idcache_del_by_context(global ? server->global_list->clients : + server->local_list->clients, client); + + silc_free(client->nickname); + client->nickname = nick; + } + + if (info) { + silc_free(client->username); + client->username = strdup(info); + } + + client->data.status |= SILC_IDLIST_STATUS_RESOLVED; + client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; + + if (name) { + /* Add new cache entry */ + silc_idcache_add(global ? server->global_list->clients : + server->local_list->clients, nick, client->id, + client, expire, NULL); + } + + /* If client is global and is not on any channel then add that we'll + expire the entry after a while. */ + if (global && server->server_type == SILC_SERVER) { + SilcIDCacheEntry cache = NULL; + silc_idlist_find_client_by_id(server->global_list, client->id, + FALSE, &cache); + if (!silc_hash_table_count(client->channels)) + cache->expire = time(NULL) + 300; + else + cache->expire = 0; } + + silc_free(client_id); } - if (nickname && client->nickname) - silc_free(client->nickname); + break; + + case SILC_ID_SERVER: + if (!name) + goto error; - if (nickname) - client->nickname = nick; + server_id = silc_id_payload_get_id(idp); + if (!server_id) + goto error; - if (username && client->username) { - silc_free(client->username); - client->username = strdup(username); + SILC_LOG_DEBUG(("Received server information")); + + server_entry = silc_idlist_find_server_by_id(server->local_list, + server_id, FALSE, NULL); + if (!server_entry) + server_entry = silc_idlist_find_server_by_id(server->global_list, + server_id, FALSE, NULL); + if (!server_entry) { + /* If router did not find such Server ID in its lists then this must + be bogus server or some router in the net is buggy. */ + if (server->server_type != SILC_SERVER) + goto error; + + /* We don't have that server anywhere, add it. */ + server_entry = silc_idlist_add_server(server->global_list, + strdup(name), 0, + server_id, server->router, + SILC_PRIMARY_ROUTE(server)); + if (!server_entry) { + silc_free(server_id); + goto error; + } + server_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED; + server_entry->data.status |= SILC_IDLIST_STATUS_RESOLVED; + server_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; + server_id = NULL; } - if (nickname && cache) { - cache->data = nick; - silc_idcache_sort_by_data(global ? server->global_list->clients : - server->local_list->clients); + silc_free(server_id); + break; + + case SILC_ID_CHANNEL: + if (!name) + goto error; + + channel_id = silc_id_payload_get_id(idp); + if (!channel_id) + goto error; + + SILC_LOG_DEBUG(("Received channel information")); + + channel = silc_idlist_find_channel_by_name(server->local_list, + name, NULL); + if (!channel) + channel = silc_idlist_find_channel_by_name(server->global_list, + name, NULL); + if (!channel) { + /* If router did not find such Channel ID in its lists then this must + be bogus channel or some router in the net is buggy. */ + if (server->server_type != SILC_SERVER) + goto error; + + /* We don't have that channel anywhere, add it. */ + channel = silc_idlist_add_channel(server->global_list, strdup(name), + SILC_CHANNEL_MODE_NONE, channel_id, + server->router, NULL, NULL, 0); + if (!channel) { + silc_free(channel_id); + goto error; + } + channel_id = NULL; } - silc_free(client_id); + silc_free(channel_id); + break; } + silc_id_payload_free(idp); return TRUE; + + error: + silc_id_payload_free(idp); + return FALSE; } /* Received reply for forwarded IDENTIFY command. We have received the @@ -343,19 +706,135 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) SILC_SERVER_CMD_REPLY_FUNC(identify) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; - SilcCommandStatus status; + SilcStatus status, error; - COMMAND_CHECK_STATUS_LIST; + COMMAND_CHECK_STATUS; if (!silc_server_command_reply_identify_save(cmd)) goto out; - /* Execute any pending commands */ + /* Pending callbacks are not executed if this was an list entry */ + if (status != SILC_STATUS_OK && + status != SILC_STATUS_LIST_END) { + silc_server_command_reply_free(cmd); + return; + } + + out: + silc_server_command_process_error(cmd, error); SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY); + silc_server_command_reply_free(cmd); + return; + + err: + silc_server_command_process_error(cmd, error); + silc_server_command_reply_free(cmd); +} + +/* Received reply fro INFO command. Cache the server and its information */ + +SILC_SERVER_CMD_REPLY_FUNC(info) +{ + SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; + SilcServer server = cmd->server; + SilcStatus status, error; + SilcServerEntry entry; + SilcServerID *server_id; + SilcUInt32 tmp_len; + unsigned char *tmp, *name; + + COMMAND_CHECK_STATUS; + + /* Get Server ID */ + tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); + if (!tmp) + goto out; + server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); + if (!server_id) + goto out; + + /* Get the name */ + name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); + if (tmp_len > 256) + goto out; + + entry = silc_idlist_find_server_by_id(server->local_list, server_id, + FALSE, NULL); + if (!entry) { + entry = silc_idlist_find_server_by_id(server->global_list, server_id, + FALSE, NULL); + if (!entry) { + /* Add the server to global list */ + server_id = silc_id_dup(server_id, SILC_ID_SERVER); + entry = silc_idlist_add_server(server->global_list, name, 0, + server_id, cmd->sock->user_data, + cmd->sock); + if (!entry) { + silc_free(server_id); + goto out; + } + entry->data.status |= SILC_IDLIST_STATUS_REGISTERED; + } + } + + /* Get the info string */ + tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len); + if (tmp_len > 256) + tmp = NULL; + + entry->server_info = tmp ? strdup(tmp) : NULL; + + out: + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO); + err: + silc_server_command_reply_free(cmd); +} + +/* Received reply fro MOTD command. */ + +SILC_SERVER_CMD_REPLY_FUNC(motd) +{ + SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; + SilcServer server = cmd->server; + SilcStatus status, error; + SilcServerEntry entry = NULL; + SilcServerID *server_id; + SilcUInt32 tmp_len; + unsigned char *tmp; + + COMMAND_CHECK_STATUS; + + /* Get Server ID */ + tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); + if (!tmp) + goto out; + server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); + if (!server_id) + goto out; + + 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) + goto out; + } + + /* Get the motd */ + tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); + if (tmp_len > 256) + tmp = NULL; + + entry->motd = tmp; out: - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY); + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_MOTD); + err: silc_server_command_reply_free(cmd); + + if (entry) + entry->motd = NULL; } /* Received reply for forwarded JOIN command. Router has created or joined @@ -366,14 +845,18 @@ SILC_SERVER_CMD_REPLY_FUNC(join) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; SilcServer server = cmd->server; - SilcCommandStatus status; + SilcIDCacheEntry cache = NULL; + SilcStatus status, error; SilcChannelID *id; + SilcClientID *client_id = NULL; SilcChannelEntry entry; - unsigned int id_len, len; + SilcHmac hmac = NULL; + SilcUInt32 id_len, len, list_count; unsigned char *id_string; char *channel_name, *tmp; - unsigned int mode, created; - SilcBuffer keyp; + SilcUInt32 mode, created; + SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL; + SilcPublicKey founder_key = NULL; COMMAND_CHECK_STATUS; @@ -387,14 +870,22 @@ SILC_SERVER_CMD_REPLY_FUNC(join) if (!id_string) goto out; + /* Get client ID */ + tmp = silc_argument_get_arg_type(cmd->args, 4, &len); + if (!tmp) + goto out; + client_id = silc_id_payload_parse_id(tmp, len, NULL); + if (!client_id) + goto out; + /* Get mode mask */ - tmp = silc_argument_get_arg_type(cmd->args, 4, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 5, NULL); if (!tmp) goto out; SILC_GET32_MSB(mode, tmp); /* Get created boolean value */ - tmp = silc_argument_get_arg_type(cmd->args, 5, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 6, NULL); if (!tmp) goto out; SILC_GET32_MSB(created, tmp); @@ -402,19 +893,57 @@ SILC_SERVER_CMD_REPLY_FUNC(join) goto out; /* Get channel key */ - tmp = silc_argument_get_arg_type(cmd->args, 6, &len); + tmp = silc_argument_get_arg_type(cmd->args, 7, &len); + if (tmp) { + keyp = silc_buffer_alloc(len); + silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp)); + silc_buffer_put(keyp, tmp, len); + } + + /* Parse the Channel ID */ + id = silc_id_payload_parse_id(id_string, id_len, NULL); + if (!id) + goto out; + + /* Get hmac */ + tmp = silc_argument_get_arg_type(cmd->args, 11, NULL); + if (tmp) { + if (!silc_hmac_alloc(tmp, NULL, &hmac)) + goto out; + } + + /* Get the list count */ + tmp = silc_argument_get_arg_type(cmd->args, 12, &len); if (!tmp) goto out; - keyp = silc_buffer_alloc(len); - silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp)); - silc_buffer_put(keyp, tmp, len); + SILC_GET32_MSB(list_count, tmp); - id = silc_id_payload_parse_id(id_string, id_len); - if (!id) + /* Get Client ID list */ + tmp = silc_argument_get_arg_type(cmd->args, 13, &len); + if (!tmp) + goto out; + + client_id_list = silc_buffer_alloc(len); + silc_buffer_pull_tail(client_id_list, len); + silc_buffer_put(client_id_list, tmp, len); + + /* Get client mode list */ + tmp = silc_argument_get_arg_type(cmd->args, 14, &len); + if (!tmp) goto out; + client_mode_list = silc_buffer_alloc(len); + silc_buffer_pull_tail(client_mode_list, len); + silc_buffer_put(client_mode_list, tmp, len); + + /* Get founder key */ + tmp = silc_argument_get_arg_type(cmd->args, 15, &len); + if (tmp) + silc_pkcs_public_key_payload_decode(tmp, len, &founder_key); + /* See whether we already have the channel. */ - entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL); + entry = silc_idlist_find_channel_by_name(server->local_list, + channel_name, &cache); if (!entry) { /* Add new channel */ @@ -422,16 +951,102 @@ SILC_SERVER_CMD_REPLY_FUNC(join) (created == 0 ? "existing" : "created"), channel_name, silc_id_render(id, SILC_ID_CHANNEL))); + /* If the channel is found from global list we must move it to the + local list. */ + entry = silc_idlist_find_channel_by_name(server->global_list, + channel_name, &cache); + if (entry) + silc_idlist_del_channel(server->global_list, entry); + /* Add the channel to our local list. */ - entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), - SILC_CHANNEL_MODE_NONE, id, - server->router, NULL); + entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), + SILC_CHANNEL_MODE_NONE, id, + server->router, NULL, hmac, 0); if (!entry) { silc_free(id); goto out; } + hmac = NULL; + server->stat.my_channels++; + server->stat.channels++; } else { - silc_free(id); + /* The entry exists. */ + + /* If ID has changed, then update it to the cache too. */ + if (!SILC_ID_CHANNEL_COMPARE(entry->id, id)) + silc_idlist_replace_channel_id(server->local_list, entry->id, id); + + entry->disabled = FALSE; + + /* Remove the founder auth data if the mode is not set but we have + them in the entry */ + if (!(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && entry->founder_key) { + silc_pkcs_public_key_free(entry->founder_key); + entry->founder_key = NULL; + } + } + + if (founder_key) { + if (entry->founder_key) + silc_pkcs_public_key_free(entry->founder_key); + entry->founder_key = founder_key; + founder_key = NULL; + } + + if (entry->hmac_name && (hmac || (!hmac && entry->hmac))) { + silc_free(entry->hmac_name); + entry->hmac_name = strdup(silc_hmac_get_name(hmac ? hmac : entry->hmac)); + } + + /* Get the ban list */ + tmp = silc_argument_get_arg_type(cmd->args, 8, &len); + if (tmp && len > 2) { + SilcArgumentPayload iargs; + SilcUInt16 iargc; + SILC_GET16_MSB(iargc, tmp); + iargs = silc_argument_payload_parse(tmp + 2, len - 2, iargc); + if (iargs) { + /* Delete old ban list */ + if (entry->ban_list) + silc_hash_table_free(entry->ban_list); + entry->ban_list = + silc_hash_table_alloc(0, silc_hash_ptr, + NULL, NULL, NULL, + silc_server_inviteban_destruct, entry, TRUE); + + /* Add new ban list */ + silc_server_inviteban_process(server, entry->ban_list, 0, iargs); + silc_argument_payload_free(iargs); + } + } + + /* Get the invite list */ + tmp = silc_argument_get_arg_type(cmd->args, 9, &len); + if (tmp && len > 2) { + SilcArgumentPayload iargs; + SilcUInt16 iargc; + SILC_GET16_MSB(iargc, tmp); + iargs = silc_argument_payload_parse(tmp + 2, len - 2, iargc); + if (iargs) { + /* Delete old invite list */ + if (entry->invite_list) + silc_hash_table_free(entry->invite_list); + entry->invite_list = + silc_hash_table_alloc(0, silc_hash_ptr, + NULL, NULL, NULL, + silc_server_inviteban_destruct, entry, TRUE); + + /* Add new invite list */ + silc_server_inviteban_process(server, entry->invite_list, 0, iargs); + silc_argument_payload_free(iargs); + } + } + + /* Get the topic */ + tmp = silc_argument_get_arg_type(cmd->args, 10, &len); + if (tmp) { + silc_free(entry->topic); + entry->topic = strdup(tmp); } /* If channel was not created we know there is global users on the @@ -449,14 +1064,72 @@ SILC_SERVER_CMD_REPLY_FUNC(join) entry->mode = mode; /* Save channel key */ - silc_server_save_channel_key(server, keyp, entry); - silc_buffer_free(keyp); + if (keyp) { + if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) + silc_server_save_channel_key(server, keyp, entry); + silc_buffer_free(keyp); + } + + /* Save the users to the channel */ + silc_server_save_users_on_channel(server, cmd->sock, entry, + client_id, client_id_list, + client_mode_list, list_count); + entry->users_resolved = TRUE; - /* Execute any pending commands */ + out: SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN); + err: + if (hmac) + silc_hmac_free(hmac); + silc_free(client_id); + silc_server_command_reply_free(cmd); + + silc_pkcs_public_key_free(founder_key); + if (client_id_list) + silc_buffer_free(client_id_list); + if (client_mode_list) + silc_buffer_free(client_mode_list); +} + +/* Received reply to STATS command. */ + +SILC_SERVER_CMD_REPLY_FUNC(stats) +{ + SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; + SilcServer server = cmd->server; + SilcStatus status, error; + unsigned char *tmp; + SilcUInt32 tmp_len; + SilcBufferStruct buf; + + COMMAND_CHECK_STATUS; + + /* Get statistics structure */ + tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); + if (server->server_type != SILC_ROUTER && tmp) { + silc_buffer_set(&buf, tmp, tmp_len); + silc_buffer_unformat(&buf, + SILC_STR_UI_INT(NULL), + SILC_STR_UI_INT(NULL), + SILC_STR_UI_INT(NULL), + SILC_STR_UI_INT(NULL), + SILC_STR_UI_INT(NULL), + SILC_STR_UI_INT(NULL), + SILC_STR_UI_INT(&server->stat.cell_clients), + SILC_STR_UI_INT(&server->stat.cell_channels), + SILC_STR_UI_INT(&server->stat.cell_servers), + SILC_STR_UI_INT(&server->stat.clients), + SILC_STR_UI_INT(&server->stat.channels), + SILC_STR_UI_INT(&server->stat.servers), + SILC_STR_UI_INT(&server->stat.routers), + SILC_STR_UI_INT(&server->stat.server_ops), + SILC_STR_UI_INT(&server->stat.router_ops), + SILC_STR_END); + } out: - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN); + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_STATS); + err: silc_server_command_reply_free(cmd); } @@ -464,14 +1137,14 @@ SILC_SERVER_CMD_REPLY_FUNC(users) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; SilcServer server = cmd->server; - SilcCommandStatus status; + SilcStatus status, error; SilcChannelEntry channel; SilcChannelID *channel_id = NULL; SilcBuffer client_id_list; SilcBuffer client_mode_list; unsigned char *tmp; - unsigned int tmp_len; - unsigned int list_count, i; + SilcUInt32 tmp_len; + SilcUInt32 list_count; COMMAND_CHECK_STATUS; @@ -479,10 +1152,38 @@ SILC_SERVER_CMD_REPLY_FUNC(users) tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); if (!tmp) goto out; - channel_id = silc_id_payload_parse_id(tmp, tmp_len); + channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); if (!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) { + SilcBuffer idp; + + if (server->server_type != SILC_SERVER) + goto out; + + idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL); + silc_server_send_command(server, SILC_PRIMARY_ROUTE(server), + SILC_COMMAND_IDENTIFY, ++server->cmd_ident, + 1, 5, idp->data, idp->len); + silc_buffer_free(idp); + + /* Register pending command callback. After we've received the channel + information we will reprocess this command reply by re-calling this + USERS command reply callback. */ + silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, + server->cmd_ident, + silc_server_command_reply_users, cmd); + return; + } + } + /* Get the list count */ tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); if (!tmp) @@ -507,84 +1208,189 @@ SILC_SERVER_CMD_REPLY_FUNC(users) silc_buffer_pull_tail(client_mode_list, tmp_len); silc_buffer_put(client_mode_list, tmp, tmp_len); - /* 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) - goto out; - } + /* Save the users to the channel */ + silc_server_save_users_on_channel(server, cmd->sock, channel, NULL, + client_id_list, client_mode_list, + list_count); - /* Cache the received Client ID's and modes. This cache expires - whenever server sends notify message to channel. It means two things; - some user has joined or leaved the channel. XXX! */ - for (i = 0; i < list_count; i++) { - unsigned short idp_len; - unsigned int mode; - SilcClientID *client_id; - SilcClientEntry client; + channel->global_users = silc_server_channel_has_global(channel); + channel->users_resolved = TRUE; - /* Client ID */ - SILC_GET16_MSB(idp_len, client_id_list->data + 2); - idp_len += 4; - client_id = silc_id_payload_parse_id(client_id_list->data, idp_len); - if (!client_id) - continue; - silc_buffer_pull(client_id_list, idp_len); - - /* Mode */ - SILC_GET32_MSB(mode, client_mode_list->data); - silc_buffer_pull(client_mode_list, 4); + silc_buffer_free(client_id_list); + silc_buffer_free(client_mode_list); + + out: + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS); + silc_free(channel_id); + err: + silc_server_command_reply_free(cmd); +} + +SILC_SERVER_CMD_REPLY_FUNC(getkey) +{ + SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; + SilcServer server = cmd->server; + SilcStatus status, error; + SilcClientEntry client = NULL; + SilcServerEntry server_entry = NULL; + SilcClientID *client_id = NULL; + SilcServerID *server_id = NULL; + unsigned char *tmp; + SilcUInt32 len; + SilcIDPayload idp = NULL; + SilcIdType id_type; + SilcPublicKey public_key = NULL; + + COMMAND_CHECK_STATUS; + + tmp = silc_argument_get_arg_type(cmd->args, 2, &len); + if (!tmp) + goto out; + idp = silc_id_payload_parse(tmp, len); + if (!idp) + goto out; + + /* Get the public key payload */ + tmp = silc_argument_get_arg_type(cmd->args, 3, &len); + if (!tmp) + goto out; + + /* Decode the public key payload */ + if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key)) + goto out; + + id_type = silc_id_payload_get_type(idp); + if (id_type == SILC_ID_CLIENT) { + client_id = silc_id_payload_get_id(idp); - /* Check if we have this client cached already. */ client = silc_idlist_find_client_by_id(server->local_list, client_id, - NULL); - if (!client) - client = silc_idlist_find_client_by_id(server->global_list, - client_id, NULL); + TRUE, NULL); if (!client) { - /* If router did not find such Client ID in its lists then this must - be bogus client or some router in the net is buggy. */ - if (server->server_type == SILC_ROUTER) + client = silc_idlist_find_client_by_id(server->global_list, + client_id, TRUE, NULL); + if (!client) goto out; - - /* We don't have that client anywhere, add it. The client is added - to global list since server didn't have it in the lists so it must be - global. */ - client = silc_idlist_add_client(server->global_list, NULL, NULL, - NULL, client_id, cmd->sock->user_data, - NULL); - if (!client) { - silc_free(client_id); - continue; - } - } else { - /* We have the client already. */ - silc_free(client_id); } - if (!silc_server_client_on_channel(client, channel)) { - /* Client was not on the channel, add it. */ - SilcChannelClientEntry chl = silc_calloc(1, sizeof(*chl)); - chl->client = client; - chl->mode = mode; - chl->channel = channel; - silc_list_add(channel->user_list, chl); - silc_list_add(client->channels, chl); + client->data.public_key = public_key; + public_key = NULL; + } else if (id_type == SILC_ID_SERVER) { + server_id = silc_id_payload_get_id(idp); + + server_entry = silc_idlist_find_server_by_id(server->local_list, server_id, + TRUE, NULL); + if (!server_entry) { + server_entry = silc_idlist_find_server_by_id(server->global_list, + server_id, TRUE, NULL); + if (!server_entry) + goto out; } + + server_entry->data.public_key = public_key; + public_key = NULL; + } else { + goto out; } - silc_buffer_free(client_id_list); - silc_buffer_free(client_mode_list); + out: + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY); + if (idp) + silc_id_payload_free(idp); + silc_free(client_id); + silc_free(server_id); + if (public_key) + silc_pkcs_public_key_free(public_key); + err: + silc_server_command_reply_free(cmd); +} - /* Execute any pending commands */ - SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS); +SILC_SERVER_CMD_REPLY_FUNC(list) +{ + SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; + SilcServer server = cmd->server; + SilcStatus status, error; + SilcChannelID *channel_id = NULL; + SilcChannelEntry channel; + SilcIDCacheEntry cache; + SilcUInt32 len; + unsigned char *tmp, *name, *topic; + SilcUInt32 usercount = 0; + bool global_list = FALSE; + + COMMAND_CHECK_STATUS; + + tmp = silc_argument_get_arg_type(cmd->args, 2, &len); + channel_id = silc_id_payload_parse_id(tmp, len, NULL); + if (!channel_id) + goto out; + + name = silc_argument_get_arg_type(cmd->args, 3, NULL); + topic = silc_argument_get_arg_type(cmd->args, 4, NULL); + tmp = silc_argument_get_arg_type(cmd->args, 5, NULL); + if (tmp) + SILC_GET32_MSB(usercount, tmp); + + /* Add the channel entry if we do not have it already */ + channel = silc_idlist_find_channel_by_name(server->local_list, + name, &cache); + if (!channel) { + channel = silc_idlist_find_channel_by_name(server->global_list, + name, &cache); + global_list = TRUE; + } + if (!channel) { + /* If router did not find such channel in its lists then this must + be bogus channel or some router in the net is buggy. */ + if (server->server_type != SILC_SERVER) + goto out; + + channel = silc_idlist_add_channel(server->global_list, strdup(name), + SILC_CHANNEL_MODE_NONE, channel_id, + server->router, NULL, NULL, + time(NULL) + 60); + if (!channel) + goto out; + channel_id = NULL; + } else { + /* Found, update expiry */ + if (global_list && server->server_type == SILC_SERVER) + cache->expire = time(NULL) + 60; + } + + channel->user_count = usercount; + + if (topic) { + silc_free(channel->topic); + channel->topic = strdup(topic); + } + + /* Pending callbacks are not executed if this was an list entry */ + if (status != SILC_STATUS_OK && + status != SILC_STATUS_LIST_END) { + silc_server_command_reply_free(cmd); + return; + } + + /* Now purge all old entries from the global list, otherwise we'll might + have non-existent entries for long periods of time in the cache. */ + silc_idcache_purge(server->global_list->channels); out: - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS); - if (channel_id) - silc_free(channel_id); + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_LIST); + silc_free(channel_id); + err: + silc_server_command_reply_free(cmd); +} + +SILC_SERVER_CMD_REPLY_FUNC(watch) +{ + SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; + SilcStatus status, error; + + COMMAND_CHECK_STATUS; + + out: + SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WATCH); + err: silc_server_command_reply_free(cmd); }