X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=apps%2Fsilcd%2Fcommand_reply.c;h=a72e6c206a8ccdef7d0f727f6fe7ec6fbbd94fba;hp=76b641cc3a3db766e4e8c1f5aa2f81ccec2aebb8;hb=382d15d447b7a95390decfa783836ae4fe255b3d;hpb=5abf57fab042a9f9e4ea497cea5cdf6bb170ef62 diff --git a/apps/silcd/command_reply.c b/apps/silcd/command_reply.c index 76b641cc..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,26 +23,19 @@ #include "server_internal.h" #include "command_reply.h" -/* All functions that call the COMMAND_CHECK_STATUS or the - COMMAND_CHECK_STATUS_LIST macros must have out: goto label. */ - -#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) \ - goto out; \ -} 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) \ - goto out; \ +/* 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 @@ -55,9 +48,11 @@ SilcServerCommandReply silc_command_reply_list[] = 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 }, }; @@ -72,12 +67,11 @@ void silc_server_command_reply_process(SilcServer server, SilcServerCommandReplyContext ctx; SilcCommandPayload payload; SilcCommand command; - uint16 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")); @@ -91,16 +85,22 @@ void silc_server_command_reply_process(SilcServer server, 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 */ ctx->callbacks = - silc_server_command_pending_check(server, ctx, - silc_command_get(ctx->payload), - ident, &ctx->callbacks_count); + 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; @@ -126,40 +126,71 @@ void silc_server_command_reply_free(SilcServerCommandReplyContext cmd) } } +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; - unsigned char *tmp, *id_data; + 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; - uint32 mode = 0, len, id_len; + 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) { - SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s", - nickname ? nickname : "", - username ? username : "", - realname ? realname : "")); + if (!id_data || !nickname || !username || !realname) return FALSE; - } 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); + 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, @@ -185,7 +216,7 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) client = silc_idlist_add_client(server->global_list, nick, strdup(username), strdup(realname), client_id, - cmd->sock->user_data, NULL); + cmd->sock->user_data, NULL, 0); if (!client) { SILC_LOG_ERROR(("Could not add new client to the ID Cache")); return FALSE; @@ -224,10 +255,69 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) /* Create new cache entry */ silc_idcache_add(global ? server->global_list->clients : server->local_list->clients, nick, client->id, - client, FALSE); + client, 0, NULL); silc_free(client_id); } + /* 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); + } + + /* 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; } @@ -240,12 +330,17 @@ 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; + } /* Pending callbacks are not executed if this was an list entry */ if (status != SILC_STATUS_OK && @@ -255,8 +350,13 @@ SILC_SERVER_CMD_REPLY_FUNC(whois) } out: + silc_server_command_process_error(cmd, error); SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS); - SILC_SERVER_PENDING_DESTRUCTOR(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); } @@ -266,7 +366,7 @@ static char silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd) { SilcServer server = cmd->server; - uint32 len, id_len; + SilcUInt32 len, id_len; unsigned char *id_data; char *nickname, *username, *realname, *servername = NULL; SilcClientID *client_id; @@ -283,7 +383,7 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd) realname = silc_argument_get_arg_type(cmd->args, 5, &len); - client_id = silc_id_payload_parse_id(id_data, id_len); + client_id = silc_id_payload_parse_id(id_data, id_len, NULL); if (!client_id) return FALSE; @@ -312,16 +412,16 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd) 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); + 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 = silc_idlist_find_client_by_id(server->global_list, - client_id, TRUE, &cache); - cache->expire = SILC_ID_CACHE_EXPIRE_DEF; client->servername = servername; } else { /* We have the client already, update the data */ @@ -331,17 +431,31 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd) 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, FALSE); + 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); @@ -355,9 +469,9 @@ silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd) SILC_SERVER_CMD_REPLY_FUNC(whowas) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; - SilcCommandStatus status; + SilcStatus status, error; - COMMAND_CHECK_STATUS_LIST; + COMMAND_CHECK_STATUS; if (!silc_server_command_reply_whowas_save(cmd)) goto out; @@ -370,8 +484,13 @@ SILC_SERVER_CMD_REPLY_FUNC(whowas) } out: + silc_server_command_process_error(cmd, error); SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS); - SILC_SERVER_PENDING_DESTRUCTOR(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); } @@ -381,7 +500,7 @@ static char silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) { SilcServer server = cmd->server; - uint32 len, id_len; + SilcUInt32 len, id_len; unsigned char *id_data; char *name, *info; SilcClientID *client_id = NULL; @@ -394,11 +513,12 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) 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_data(id_data, id_len); + idp = silc_id_payload_parse(id_data, id_len); if (!idp) return FALSE; @@ -437,7 +557,8 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) global. */ client = silc_idlist_add_client(server->global_list, nick, info ? strdup(info) : NULL, NULL, - client_id, cmd->sock->user_data, 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; @@ -474,7 +595,19 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) /* Add new cache entry */ silc_idcache_add(global ? server->global_list->clients : server->local_list->clients, nick, client->id, - client, FALSE); + 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); @@ -483,6 +616,9 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) break; case SILC_ID_SERVER: + if (!name) + goto error; + server_id = silc_id_payload_get_id(idp); if (!server_id) goto error; @@ -503,7 +639,8 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) /* We don't have that server anywhere, add it. */ server_entry = silc_idlist_add_server(server->global_list, strdup(name), 0, - server_id, NULL, NULL); + server_id, server->router, + SILC_PRIMARY_ROUTE(server)); if (!server_entry) { silc_free(server_id); goto error; @@ -518,28 +655,30 @@ silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd) 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_id(server->local_list, - channel_id, NULL); + channel = silc_idlist_find_channel_by_name(server->local_list, + name, NULL); if (!channel) - channel = silc_idlist_find_channel_by_id(server->global_list, channel_id, - NULL); + 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 server anywhere, add it. */ + /* 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); + server->router, NULL, NULL, 0); if (!channel) { silc_free(channel_id); goto error; @@ -567,9 +706,9 @@ 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; @@ -582,8 +721,13 @@ SILC_SERVER_CMD_REPLY_FUNC(identify) } out: + silc_server_command_process_error(cmd, error); SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY); - SILC_SERVER_PENDING_DESTRUCTOR(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); } @@ -593,10 +737,10 @@ SILC_SERVER_CMD_REPLY_FUNC(info) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; SilcServer server = cmd->server; - SilcCommandStatus status; + SilcStatus status, error; SilcServerEntry entry; SilcServerID *server_id; - uint32 tmp_len; + SilcUInt32 tmp_len; unsigned char *tmp, *name; COMMAND_CHECK_STATUS; @@ -605,7 +749,7 @@ SILC_SERVER_CMD_REPLY_FUNC(info) 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); + server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); if (!server_id) goto out; @@ -623,7 +767,8 @@ SILC_SERVER_CMD_REPLY_FUNC(info) /* 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, NULL, NULL); + server_id, cmd->sock->user_data, + cmd->sock); if (!entry) { silc_free(server_id); goto out; @@ -641,7 +786,7 @@ SILC_SERVER_CMD_REPLY_FUNC(info) out: SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO); - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO); + err: silc_server_command_reply_free(cmd); } @@ -651,10 +796,10 @@ SILC_SERVER_CMD_REPLY_FUNC(motd) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; SilcServer server = cmd->server; - SilcCommandStatus status; + SilcStatus status, error; SilcServerEntry entry = NULL; SilcServerID *server_id; - uint32 tmp_len; + SilcUInt32 tmp_len; unsigned char *tmp; COMMAND_CHECK_STATUS; @@ -663,7 +808,7 @@ SILC_SERVER_CMD_REPLY_FUNC(motd) 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); + server_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); if (!server_id) goto out; @@ -685,7 +830,7 @@ SILC_SERVER_CMD_REPLY_FUNC(motd) out: SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_MOTD); - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD); + err: silc_server_command_reply_free(cmd); if (entry) @@ -701,16 +846,17 @@ SILC_SERVER_CMD_REPLY_FUNC(join) SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; SilcServer server = cmd->server; SilcIDCacheEntry cache = NULL; - SilcCommandStatus status; + SilcStatus status, error; SilcChannelID *id; SilcClientID *client_id = NULL; SilcChannelEntry entry; SilcHmac hmac = NULL; - uint32 id_len, len, list_count; + SilcUInt32 id_len, len, list_count; unsigned char *id_string; char *channel_name, *tmp; - uint32 mode, created; + SilcUInt32 mode, created; SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL; + SilcPublicKey founder_key = NULL; COMMAND_CHECK_STATUS; @@ -728,7 +874,7 @@ SILC_SERVER_CMD_REPLY_FUNC(join) tmp = silc_argument_get_arg_type(cmd->args, 4, &len); if (!tmp) goto out; - client_id = silc_id_payload_parse_id(tmp, len); + client_id = silc_id_payload_parse_id(tmp, len, NULL); if (!client_id) goto out; @@ -754,7 +900,8 @@ SILC_SERVER_CMD_REPLY_FUNC(join) silc_buffer_put(keyp, tmp, len); } - id = silc_id_payload_parse_id(id_string, id_len); + /* Parse the Channel ID */ + id = silc_id_payload_parse_id(id_string, id_len, NULL); if (!id) goto out; @@ -789,6 +936,11 @@ SILC_SERVER_CMD_REPLY_FUNC(join) 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_name(server->local_list, channel_name, &cache); @@ -801,66 +953,99 @@ SILC_SERVER_CMD_REPLY_FUNC(join) /* 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, + 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, hmac); + 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 { /* The entry exists. */ - silc_free(cache->id); - entry->id = id; - cache->id = entry->id; + + /* 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); - if (entry->founder_passwd) { - silc_free(entry->founder_passwd); - entry->founder_passwd = NULL; - } + entry->founder_key = NULL; } } - if (entry->hmac_name && hmac) { + 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)); + 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) { - if (entry->ban_list) - silc_free(entry->ban_list); - entry->ban_list = silc_calloc(len, sizeof(*entry->ban_list)); - memcpy(entry->ban_list, tmp, 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) { - if (entry->invite_list) - silc_free(entry->invite_list); - entry->invite_list = silc_calloc(len, sizeof(*entry->invite_list)); - memcpy(entry->invite_list, tmp, 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) { - if (entry->topic) - silc_free(entry->topic); + silc_free(entry->topic); entry->topic = strdup(tmp); } @@ -879,40 +1064,87 @@ SILC_SERVER_CMD_REPLY_FUNC(join) entry->mode = mode; /* Save channel key */ - if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) - silc_server_save_channel_key(server, keyp, entry); - if (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; out: SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN); - SILC_SERVER_PENDING_DESTRUCTOR(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_EXEC(cmd, SILC_COMMAND_STATS); + err: + silc_server_command_reply_free(cmd); +} + 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; - uint32 tmp_len; - uint32 list_count; + SilcUInt32 tmp_len; + SilcUInt32 list_count; COMMAND_CHECK_STATUS; @@ -920,7 +1152,7 @@ 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; @@ -937,7 +1169,7 @@ SILC_SERVER_CMD_REPLY_FUNC(users) goto out; idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL); - silc_server_send_command(server, server->router->connection, + 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); @@ -947,7 +1179,7 @@ SILC_SERVER_CMD_REPLY_FUNC(users) USERS command reply callback. */ silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, server->cmd_ident, - NULL, silc_server_command_reply_users, cmd); + silc_server_command_reply_users, cmd); return; } } @@ -981,13 +1213,16 @@ SILC_SERVER_CMD_REPLY_FUNC(users) client_id_list, client_mode_list, list_count); + channel->global_users = silc_server_channel_has_global(channel); + channel->users_resolved = TRUE; + silc_buffer_free(client_id_list); silc_buffer_free(client_mode_list); out: SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS); - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS); silc_free(channel_id); + err: silc_server_command_reply_free(cmd); } @@ -995,15 +1230,13 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; SilcServer server = cmd->server; - SilcCommandStatus status; + SilcStatus status, error; SilcClientEntry client = NULL; SilcServerEntry server_entry = NULL; SilcClientID *client_id = NULL; SilcServerID *server_id = NULL; - SilcSKEPKType type; - unsigned char *tmp, *pk; - uint32 len; - uint16 pk_len; + unsigned char *tmp; + SilcUInt32 len; SilcIDPayload idp = NULL; SilcIdType id_type; SilcPublicKey public_key = NULL; @@ -1013,7 +1246,7 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey) tmp = silc_argument_get_arg_type(cmd->args, 2, &len); if (!tmp) goto out; - idp = silc_id_payload_parse_data(tmp, len); + idp = silc_id_payload_parse(tmp, len); if (!idp) goto out; @@ -1022,16 +1255,8 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey) if (!tmp) goto out; - /* Decode the public key */ - - SILC_GET16_MSB(pk_len, tmp); - SILC_GET16_MSB(type, tmp + 2); - pk = tmp + 4; - - if (type != SILC_SKE_PK_TYPE_SILC) - goto out; - - if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key)) + /* 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); @@ -1048,6 +1273,7 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey) } client->data.public_key = public_key; + public_key = NULL; } else if (id_type == SILC_ID_SERVER) { server_id = silc_id_payload_get_id(idp); @@ -1061,19 +1287,20 @@ SILC_SERVER_CMD_REPLY_FUNC(getkey) } server_entry->data.public_key = public_key; + public_key = NULL; } else { goto out; } out: SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY); - SILC_SERVER_PENDING_DESTRUCTOR(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); } @@ -1081,17 +1308,19 @@ SILC_SERVER_CMD_REPLY_FUNC(list) { SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context; SilcServer server = cmd->server; - SilcCommandStatus status; + SilcStatus status, error; SilcChannelID *channel_id = NULL; SilcChannelEntry channel; - uint32 len; + SilcIDCacheEntry cache; + SilcUInt32 len; unsigned char *tmp, *name, *topic; - uint32 usercount = 0; + SilcUInt32 usercount = 0; + bool global_list = FALSE; - COMMAND_CHECK_STATUS_LIST; + COMMAND_CHECK_STATUS; tmp = silc_argument_get_arg_type(cmd->args, 2, &len); - channel_id = silc_id_payload_parse_id(tmp, len); + channel_id = silc_id_payload_parse_id(tmp, len, NULL); if (!channel_id) goto out; @@ -1102,26 +1331,34 @@ SILC_SERVER_CMD_REPLY_FUNC(list) SILC_GET32_MSB(usercount, tmp); /* Add the channel entry if we do not have it already */ - 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); + channel = silc_idlist_find_channel_by_name(server->local_list, + name, &cache); if (!channel) { - /* If router did not find such Channel ID in its lists then this must + 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); + 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); @@ -1134,9 +1371,26 @@ SILC_SERVER_CMD_REPLY_FUNC(list) 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_EXEC(cmd, SILC_COMMAND_GETKEY); - SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_GETKEY); + 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); }