X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcclient%2Fcommand_reply.c;h=a737af8d524f1bc1b498fac34504a7488cd421c4;hp=a9d3ba704ceb4eabd1fb7e8d9dda66259fdcc487;hb=413da0f8686910f5e627393157566ae729ca99c4;hpb=2f93ad66ac16ad7c8bde97d1298bb78aad5b4f35 diff --git a/lib/silcclient/command_reply.c b/lib/silcclient/command_reply.c index a9d3ba70..a737af8d 100644 --- a/lib/silcclient/command_reply.c +++ b/lib/silcclient/command_reply.c @@ -2,21 +2,21 @@ command_reply.c - Author: Pekka Riikonen + Author: Pekka Riikonen - Copyright (C) 1997 - 2001 Pekka Riikonen + Copyright (C) 1997 - 2003 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 - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - + the Free Software Foundation; version 2 of the License. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +/* $Id$ */ /* * Command reply functions are "the otherside" of the command functions. * Reply to a command sent by server is handled by these functions. @@ -25,127 +25,51 @@ * application through command_reply client operation. The arguments are * exactly same and in same order as the server sent it. However, ID's are * not sent to the application. Instead, corresponding ID entry is sent - * to the application. For example, instead of sending Client ID the + * to the application. For example, instead of sending Client ID the * corresponding SilcClientEntry is sent to the application. The case is * same with for example Channel ID's. This way application has all the * necessary data already in hand without redundant searching. If ID is * received but ID entry does not exist, NULL is sent. */ -/* $Id$ */ -#include "clientlibincludes.h" +#include "silcincludes.h" +#include "silcclient.h" #include "client_internal.h" -/* Client command reply list. */ -SilcClientCommandReply silc_command_reply_list[] = -{ - SILC_CLIENT_CMD_REPLY(whois, WHOIS), - SILC_CLIENT_CMD_REPLY(whowas, WHOWAS), - SILC_CLIENT_CMD_REPLY(identify, IDENTIFY), - SILC_CLIENT_CMD_REPLY(nick, NICK), - SILC_CLIENT_CMD_REPLY(list, LIST), - SILC_CLIENT_CMD_REPLY(topic, TOPIC), - SILC_CLIENT_CMD_REPLY(invite, INVITE), - SILC_CLIENT_CMD_REPLY(kill, KILL), - SILC_CLIENT_CMD_REPLY(info, INFO), - SILC_CLIENT_CMD_REPLY(connect, CONNECT), - SILC_CLIENT_CMD_REPLY(ping, PING), - SILC_CLIENT_CMD_REPLY(oper, OPER), - SILC_CLIENT_CMD_REPLY(join, JOIN), - SILC_CLIENT_CMD_REPLY(motd, MOTD), - SILC_CLIENT_CMD_REPLY(umode, UMODE), - SILC_CLIENT_CMD_REPLY(cmode, CMODE), - SILC_CLIENT_CMD_REPLY(cumode, CUMODE), - SILC_CLIENT_CMD_REPLY(kick, KICK), - SILC_CLIENT_CMD_REPLY(ban, BAN), - SILC_CLIENT_CMD_REPLY(close, CLOSE), - SILC_CLIENT_CMD_REPLY(shutdown, SHUTDOWN), - SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER), - SILC_CLIENT_CMD_REPLY(leave, LEAVE), - SILC_CLIENT_CMD_REPLY(users, USERS), - SILC_CLIENT_CMD_REPLY(getkey, GETKEY), - - { NULL, 0 }, -}; - -const SilcCommandStatusMessage silc_command_status_messages[] = { - - { STAT(NO_SUCH_NICK), "There was no such nickname" }, - { STAT(NO_SUCH_CHANNEL), "There was no such channel" }, - { STAT(NO_SUCH_SERVER), "No such server" }, - { STAT(TOO_MANY_TARGETS), "Duplicate recipients. No message delivered" }, - { STAT(NO_RECIPIENT), "No recipient given" }, - { STAT(UNKNOWN_COMMAND), "Unknown command" }, - { STAT(WILDCARDS), "Unknown command" }, - { STAT(NO_CLIENT_ID), "No Client ID given" }, - { STAT(NO_CHANNEL_ID), "No Channel ID given" }, - { STAT(NO_SERVER_ID), "No Server ID given" }, - { STAT(BAD_CLIENT_ID), "Bad Client ID" }, - { STAT(BAD_CHANNEL_ID), "Bad Channel ID" }, - { STAT(NO_SUCH_CLIENT_ID), "There is no such client" }, - { STAT(NO_SUCH_CHANNEL_ID),"There is no such channel" }, - { STAT(NICKNAME_IN_USE), "Nickname already exists" }, - { STAT(NOT_ON_CHANNEL), "You are not on that channel" }, - { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" }, - { STAT(USER_ON_CHANNEL), "User already on the channel" }, - { STAT(NOT_REGISTERED), "You have not registered" }, - { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" }, - { STAT(TOO_MANY_PARAMS), "Too many parameters" }, - { STAT(PERM_DENIED), "Permission denied" }, - { STAT(BANNED_FROM_SERVER),"You are banned from this server" }, - { STAT(BAD_PASSWORD), "Cannot join channel. Incorrect password" }, - { STAT(CHANNEL_IS_FULL), "Cannot join channel. Channel is full" }, - { STAT(NOT_INVITED), "Cannot join channel. You have not been invited" }, - { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" }, - { STAT(UNKNOWN_MODE), "Unknown mode" }, - { STAT(NOT_YOU), "Cannot change mode for other users" }, - { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" }, - { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" }, - { STAT(NO_SERVER_PRIV), "Permission denied. You are not server operator" }, - { STAT(NO_ROUTER_PRIV), "Permission denied. You are not SILC operator" }, - { STAT(BAD_NICKNAME), "Bad nickname" }, - { STAT(BAD_CHANNEL), "Bad channel name" }, - { STAT(AUTH_FAILED), "Authentication failed" }, - { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" }, - { STAT(NO_SUCH_SERVER_ID), "No such Server ID" }, - - { 0, NULL } -}; -/* Command reply operation that is called at the end of all command replys. - Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */ -#define COMMAND_REPLY(args) cmd->client->ops->command_reply args -#define ARGS cmd->client, cmd->sock->user_data, \ - cmd->payload, TRUE, silc_command_get(cmd->payload), status - -/* Error reply to application. Usage: COMMAND_REPLY_ERROR; */ -#define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \ - cmd->sock->user_data, cmd->payload, FALSE, \ - silc_command_get(cmd->payload), status) - -/* 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) { \ - COMMAND_REPLY_ERROR; \ - goto out; \ - } \ +#define SAY cmd->client->internal->ops->say + +/* All functions that call the COMMAND_CHECK_STATUS macro must have + out: and err: goto labels. out label should call the pending + command replies, and the err label just handle error condition. */ + +#define COMMAND_CHECK_STATUS \ +do { \ + SILC_LOG_DEBUG(("Start")); \ + if (!silc_command_get_status(cmd->payload, NULL, NULL)) { \ + if (SILC_STATUS_IS_ERROR(cmd->status)) { \ + /* Single error */ \ + COMMAND_REPLY_ERROR(cmd->status); \ + goto out; \ + } \ + /* List of errors */ \ + COMMAND_REPLY_ERROR(cmd->error); \ + if (cmd->status == SILC_STATUS_LIST_END) \ + goto out; \ + goto err; \ + } \ } 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) { \ - COMMAND_REPLY_ERROR; \ - goto out; \ - } \ +/* Same as COMMAND_CHECK_STATUS but doesn't call client operation */ +#define COMMAND_CHECK_STATUS_I \ +do { \ + SILC_LOG_DEBUG(("Start")); \ + if (!silc_command_get_status(cmd->payload, NULL, NULL)) { \ + if (SILC_STATUS_IS_ERROR(cmd->status)) \ + goto out; \ + if (cmd->status == SILC_STATUS_LIST_END) \ + goto out; \ + goto err; \ + } \ } while(0) /* Process received command reply. */ @@ -155,11 +79,11 @@ void silc_client_command_reply_process(SilcClient client, SilcPacketContext *packet) { SilcBuffer buffer = packet->buffer; - SilcClientCommandReply *cmd; + SilcClientCommand cmd; SilcClientCommandReplyContext ctx; SilcCommandPayload payload; SilcCommand command; - uint16 ident; + SilcCommandCb reply = NULL; /* Get command reply payload from packet */ payload = silc_command_payload_parse(buffer->data, buffer->len); @@ -168,107 +92,119 @@ void silc_client_command_reply_process(SilcClient client, SILC_LOG_DEBUG(("Bad command reply packet")); return; } - + /* Allocate command reply context. This must be free'd by the command reply routine receiving it. */ ctx = silc_calloc(1, sizeof(*ctx)); + ctx->users++; ctx->client = client; ctx->sock = sock; ctx->payload = payload; ctx->args = silc_command_get_args(ctx->payload); ctx->packet = packet; - ident = silc_command_get_ident(ctx->payload); - + ctx->ident = silc_command_get_ident(ctx->payload); + silc_command_get_status(ctx->payload, &ctx->status, &ctx->error); + /* Check for pending commands and mark to be exeucted */ - silc_client_command_pending_check(sock->user_data, ctx, - silc_command_get(ctx->payload), ident); + ctx->callbacks = + silc_client_command_pending_check(sock->user_data, ctx, + silc_command_get(ctx->payload), + 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); - return; + /* Try to find matching the command identifier */ + silc_list_start(client->internal->commands); + while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) { + if (cmd->cmd == command && !cmd->ident) + reply = cmd->reply; + if (cmd->cmd == command && cmd->ident == ctx->ident) { + (*cmd->reply)((void *)ctx, NULL); + break; + } } - cmd->cb(ctx, NULL); + if (cmd == SILC_LIST_END) { + if (reply) + /* No specific identifier for command reply, call first one found */ + (*reply)(ctx, NULL); + else + silc_free(ctx); + } } -/* Returns status message string */ +/* Duplicate Command Reply Context by adding reference counter. The context + won't be free'd untill it hits zero. */ -char *silc_client_command_status_message(SilcCommandStatus status) +SilcClientCommandReplyContext +silc_client_command_reply_dup(SilcClientCommandReplyContext cmd) { - int i; - - for (i = 0; silc_command_status_messages[i].message; i++) { - if (silc_command_status_messages[i].status == status) - break; - } - - if (silc_command_status_messages[i].message == NULL) - return NULL; - - return silc_command_status_messages[i].message; + cmd->users++; + SILC_LOG_DEBUG(("Command reply context %p refcnt %d->%d", cmd, + cmd->users - 1, cmd->users)); + return cmd; } /* Free command reply context and its internals. */ void silc_client_command_reply_free(SilcClientCommandReplyContext cmd) { - if (cmd) { + cmd->users--; + SILC_LOG_DEBUG(("Command reply context %p refcnt %d->%d", cmd, + cmd->users + 1, cmd->users)); + if (cmd->users < 1) { silc_command_payload_free(cmd->payload); silc_free(cmd); } } -static void +static void silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd, - SilcCommandStatus status) + SilcStatus status, + bool notify) { SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; SilcClientID *client_id; - SilcIDCacheEntry id_cache = NULL; SilcClientEntry client_entry = NULL; - int argc; - uint32 len; + SilcUInt32 len; unsigned char *id_data, *tmp; char *nickname = NULL, *username = NULL; char *realname = NULL; - uint32 idle = 0, mode = 0; - SilcBuffer channels = NULL; + SilcUInt32 idle = 0, mode = 0; + SilcBufferStruct channels, ch_user_modes; + bool has_channels = FALSE, has_user_modes = FALSE; unsigned char *fingerprint; - uint32 fingerprint_len; - - argc = silc_argument_get_arg_num(cmd->args); + SilcUInt32 fingerprint_len; id_data = silc_argument_get_arg_type(cmd->args, 2, &len); if (!id_data) { - COMMAND_REPLY_ERROR; + if (notify) + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); return; } - - client_id = silc_id_payload_parse_id(id_data, len); + + client_id = silc_id_payload_parse_id(id_data, len, NULL); if (!client_id) { - COMMAND_REPLY_ERROR; + if (notify) + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); return; } - + 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 (!nickname || !username || !realname) { - COMMAND_REPLY_ERROR; + if (notify) + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); return; } tmp = silc_argument_get_arg_type(cmd->args, 6, &len); if (tmp) { - channels = silc_buffer_alloc(len); - silc_buffer_pull_tail(channels, SILC_BUFFER_END(channels)); - silc_buffer_put(channels, tmp, len); + silc_buffer_set(&channels, tmp, len); + has_channels = TRUE; } tmp = silc_argument_get_arg_type(cmd->args, 7, &len); @@ -281,40 +217,46 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd, fingerprint = silc_argument_get_arg_type(cmd->args, 9, &fingerprint_len); + tmp = silc_argument_get_arg_type(cmd->args, 10, &len); + if (tmp) { + silc_buffer_set(&ch_user_modes, tmp, len); + has_user_modes = TRUE; + } + /* Check if we have this client cached already. */ - if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, - NULL, NULL, - silc_hash_client_id_compare, NULL, - &id_cache)) { + client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); + if (!client_entry) { SILC_LOG_DEBUG(("Adding new client entry")); - client_entry = + client_entry = silc_client_add_client(cmd->client, conn, nickname, username, realname, client_id, mode); } else { - client_entry = (SilcClientEntry)id_cache->context; - silc_client_update_client(cmd->client, conn, client_entry, + silc_client_update_client(cmd->client, conn, client_entry, nickname, username, realname, mode); silc_free(client_id); } if (fingerprint && !client_entry->fingerprint) { - client_entry->fingerprint = - silc_calloc(fingerprint_len, - sizeof(*client_entry->fingerprint)); - memcpy(client_entry->fingerprint, fingerprint, fingerprint_len); + client_entry->fingerprint = silc_memdup(fingerprint, fingerprint_len); client_entry->fingerprint_len = fingerprint_len; } - if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) - client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING; + /* Take Requested Attributes if set. */ + tmp = silc_argument_get_arg_type(cmd->args, 11, &len); + if (tmp) { + if (client_entry->attrs) + silc_attribute_payload_list_free(client_entry->attrs); + client_entry->attrs = silc_attribute_payload_parse(tmp, len); + } - /* Notify application */ - if (!cmd->callback) - COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, - channels, mode, idle, fingerprint)); + client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING; - if (channels) - silc_buffer_free(channels); + /* Notify application */ + if (!cmd->callbacks_count && notify) + COMMAND_REPLY((SILC_ARGS, client_entry, nickname, username, realname, + has_channels ? &channels : NULL, mode, idle, + fingerprint, has_user_modes ? &ch_user_modes : NULL, + client_entry->attrs)); } /* Received reply for WHOIS command. This maybe called several times @@ -323,23 +265,44 @@ silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd, SILC_CLIENT_CMD_REPLY_FUNC(whois) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcCommandStatus status; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - COMMAND_CHECK_STATUS_LIST; + COMMAND_CHECK_STATUS; /* Save WHOIS info */ - silc_client_command_reply_whois_save(cmd, status); + silc_client_command_reply_whois_save(cmd, cmd->status, TRUE); /* Pending callbacks are not executed if this was an list entry */ - if (status != SILC_STATUS_OK && - status != SILC_STATUS_LIST_END) { + if (cmd->status != SILC_STATUS_OK && + cmd->status != SILC_STATUS_LIST_END) { silc_client_command_reply_free(cmd); return; } out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS); + + err: + /* If we received notify for invalid ID we'll remove the ID if we + have it cached. */ + if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) { + SilcClientEntry client_entry; + SilcUInt32 tmp_len; + unsigned char *tmp = + silc_argument_get_arg_type(silc_command_get_args(cmd->payload), + 2, &tmp_len); + if (tmp) { + SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); + if (client_id) { + client_entry = silc_client_get_client_by_id(cmd->client, conn, + client_id); + if (client_entry) + silc_client_del_client(cmd->client, conn, client_entry); + silc_free(client_id); + } + } + } + silc_client_command_reply_free(cmd); } @@ -349,93 +312,86 @@ SILC_CLIENT_CMD_REPLY_FUNC(whowas) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; SilcClientID *client_id; - SilcIDCacheEntry id_cache = NULL; SilcClientEntry client_entry = NULL; - uint32 len; + SilcUInt32 len; unsigned char *id_data; char *nickname, *username; char *realname = NULL; - COMMAND_CHECK_STATUS_LIST; + COMMAND_CHECK_STATUS; id_data = silc_argument_get_arg_type(cmd->args, 2, &len); if (!id_data) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - - client_id = silc_id_payload_parse_id(id_data, len); + + client_id = silc_id_payload_parse_id(id_data, len, NULL); if (!client_id) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } /* Get the client entry, if exists */ - if (silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, - NULL, NULL, - silc_hash_client_id_compare, NULL, - &id_cache)) - client_entry = (SilcClientEntry)id_cache->context; + client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); silc_free(client_id); 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 (!nickname || !username) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } /* Notify application. We don't save any history information to any - cache. Just pass the data to the application for displaying on + cache. Just pass the data to the application for displaying on the screen. */ - COMMAND_REPLY((ARGS, client_entry, nickname, username, realname)); + COMMAND_REPLY((SILC_ARGS, client_entry, nickname, username, realname)); /* Pending callbacks are not executed if this was an list entry */ - if (status != SILC_STATUS_OK && - status != SILC_STATUS_LIST_END) { + if (cmd->status != SILC_STATUS_OK && + cmd->status != SILC_STATUS_LIST_END) { silc_client_command_reply_free(cmd); return; } out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOWAS); + err: silc_client_command_reply_free(cmd); } -static void +static void silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd, - SilcCommandStatus status) + SilcStatus status, + bool notify) { SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; SilcClient client = cmd->client; SilcClientID *client_id = NULL; SilcServerID *server_id = NULL; SilcChannelID *channel_id = NULL; - SilcIDCacheEntry id_cache = NULL; SilcClientEntry client_entry; SilcServerEntry server_entry; SilcChannelEntry channel_entry; - int argc; - uint32 len; + SilcUInt32 len; unsigned char *id_data; char *name = NULL, *info = NULL; SilcIDPayload idp = NULL; SilcIdType id_type; - - argc = silc_argument_get_arg_num(cmd->args); id_data = silc_argument_get_arg_type(cmd->args, 2, &len); if (!id_data) { - COMMAND_REPLY_ERROR; + if (notify) + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); return; } idp = silc_id_payload_parse(id_data, len); if (!idp) { - COMMAND_REPLY_ERROR; + if (notify) + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); return; } @@ -451,26 +407,22 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd, SILC_LOG_DEBUG(("Received client information")); /* Check if we have this client cached already. */ - if (!silc_idcache_find_by_id_one_ext(conn->client_cache, - (void *)client_id, - NULL, NULL, - silc_hash_client_id_compare, NULL, - &id_cache)) { + client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); + if (!client_entry) { SILC_LOG_DEBUG(("Adding new client entry")); - client_entry = + client_entry = silc_client_add_client(cmd->client, conn, name, info, NULL, silc_id_dup(client_id, id_type), 0); } else { - client_entry = (SilcClientEntry)id_cache->context; - silc_client_update_client(cmd->client, conn, client_entry, + silc_client_update_client(cmd->client, conn, client_entry, name, info, NULL, 0); } - if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) - client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING; + client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING; /* Notify application */ - COMMAND_REPLY((ARGS, client_entry, name, info)); + if (notify) + COMMAND_REPLY((SILC_ARGS, client_entry, name, info)); break; case SILC_ID_SERVER: @@ -479,27 +431,25 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd, SILC_LOG_DEBUG(("Received server information")); /* Check if we have this server cached already. */ - if (!silc_idcache_find_by_id_one(conn->server_cache, - (void *)server_id, &id_cache)) { + server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id); + if (!server_entry) { SILC_LOG_DEBUG(("Adding new server entry")); - - server_entry = silc_calloc(1, sizeof(*server_entry)); - server_entry->server_id = silc_id_dup(server_id, id_type); - if (name) - server_entry->server_name = strdup(name); - if (info) - server_entry->server_info = strdup(info); - - /* Add server to cache */ - silc_idcache_add(conn->server_cache, server_entry->server_name, - server_entry->server_id, (void *)server_entry, - 0, NULL); + server_entry = silc_client_add_server(cmd->client, conn, name, info, + silc_id_dup(server_id, id_type)); + if (!server_entry) { + if (notify) + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + return; + } } else { - server_entry = (SilcServerEntry)id_cache->context; + silc_client_update_server(client, conn, server_entry, name, info); } + server_entry->resolve_cmd_ident = 0; + /* Notify application */ - COMMAND_REPLY((ARGS, server_entry, name, info)); + if (notify) + COMMAND_REPLY((SILC_ARGS, server_entry, name, info)); break; case SILC_ID_CHANNEL: @@ -508,20 +458,20 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd, SILC_LOG_DEBUG(("Received channel information")); /* Check if we have this channel cached already. */ - if (!silc_idcache_find_by_id_one(conn->channel_cache, - (void *)channel_id, &id_cache)) { + channel_entry = silc_client_get_channel_by_id(client, conn, channel_id); + if (!channel_entry) { if (!name) break; - SILC_LOG_DEBUG(("Adding new channel entry")); - channel_entry = silc_client_new_channel_id(client, conn->sock, - strdup(name), 0, idp); - } else { - channel_entry = (SilcChannelEntry)id_cache->context; + /* Add new channel entry */ + channel_entry = silc_client_add_channel(client, conn, name, 0, + channel_id); + channel_id = NULL; } /* Notify application */ - COMMAND_REPLY((ARGS, channel_entry, name, info)); + if (notify) + COMMAND_REPLY((SILC_ARGS, channel_entry, name, info)); break; } @@ -532,29 +482,50 @@ silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd, } /* Received reply for IDENTIFY command. This maybe called several times - for one IDENTIFY command as server may reply with list of results. + for one IDENTIFY command as server may reply with list of results. This is totally silent and does not print anything on screen. */ SILC_CLIENT_CMD_REPLY_FUNC(identify) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcCommandStatus status; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - COMMAND_CHECK_STATUS_LIST; + COMMAND_CHECK_STATUS; /* Save IDENTIFY info */ - silc_client_command_reply_identify_save(cmd, status); + silc_client_command_reply_identify_save(cmd, cmd->status, TRUE); /* Pending callbacks are not executed if this was an list entry */ - if (status != SILC_STATUS_OK && - status != SILC_STATUS_LIST_END) { + if (cmd->status != SILC_STATUS_OK && + cmd->status != SILC_STATUS_LIST_END) { silc_client_command_reply_free(cmd); return; } out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY); + + err: + /* If we received notify for invalid ID we'll remove the ID if we + have it cached. */ + if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) { + SilcClientEntry client_entry; + SilcUInt32 tmp_len; + unsigned char *tmp = + silc_argument_get_arg_type(silc_command_get_args(cmd->payload), + 2, &tmp_len); + if (tmp) { + SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); + if (client_id) { + client_entry = silc_client_get_client_by_id(cmd->client, conn, + client_id); + if (client_entry) + silc_client_del_client(cmd->client, conn, client_entry); + silc_free(client_id); + } + } + } + silc_client_command_reply_free(cmd); } @@ -565,49 +536,61 @@ SILC_CLIENT_CMD_REPLY_FUNC(nick) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; SilcIDPayload idp; unsigned char *tmp; - uint32 argc, len; + SilcUInt32 argc, len; + SilcClientID old_client_id; SILC_LOG_DEBUG(("Start")); - SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Cannot set nickname: %s", - silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot set nickname: %s", + silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } argc = silc_argument_get_arg_num(cmd->args); - if (argc < 2 || argc > 2) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Cannot set nickname: bad reply to command"); - COMMAND_REPLY_ERROR; + if (argc < 2 || argc > 3) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot set nickname: bad reply to command"); + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } + /* Save old Client ID */ + old_client_id = *conn->local_id; + /* Take received Client ID */ tmp = silc_argument_get_arg_type(cmd->args, 2, &len); idp = silc_id_payload_parse(tmp, len); if (!idp) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } silc_client_receive_new_id(cmd->client, cmd->sock, idp); - + + /* Take the new nickname too */ + tmp = silc_argument_get_arg_type(cmd->args, 3, &len); + if (tmp) { + silc_idcache_del_by_context(conn->internal->client_cache, + conn->local_entry); + if (conn->nickname) + silc_free(conn->nickname); + conn->nickname = strdup(tmp); + conn->local_entry->nickname = conn->nickname; + silc_client_nickname_format(cmd->client, conn, conn->local_entry); + silc_idcache_add(conn->internal->client_cache, strdup(tmp), + conn->local_entry->id, conn->local_entry, 0, NULL); + } + /* Notify application */ - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK); - COMMAND_REPLY((ARGS, conn->local_entry)); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK); - silc_client_command_reply_free(cmd); - return; + COMMAND_REPLY((SILC_ARGS, conn->local_entry, conn->local_entry->nickname, + (const SilcClientID *)&old_client_id)); out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK); silc_client_command_reply_free(cmd); } @@ -616,31 +599,65 @@ SILC_CLIENT_CMD_REPLY_FUNC(nick) SILC_CLIENT_CMD_REPLY_FUNC(list) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcCommandStatus status; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; unsigned char *tmp, *name, *topic; - uint32 usercount = 0; + SilcUInt32 usercount = 0, len; + SilcChannelID *channel_id = NULL; + SilcChannelEntry channel_entry; + + COMMAND_CHECK_STATUS; - COMMAND_CHECK_STATUS_LIST; + tmp = silc_argument_get_arg_type(cmd->args, 2, &len); + if (!tmp) { + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + channel_id = silc_id_payload_parse_id(tmp, len, NULL); + if (!channel_id) { + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } name = silc_argument_get_arg_type(cmd->args, 3, NULL); + if (!name) { + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + 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); + /* Check whether the channel exists, and add it to cache if it doesn't. */ + channel_entry = silc_client_get_channel_by_id(cmd->client, conn, + channel_id); + if (!channel_entry) { + /* Add new channel entry */ + channel_entry = silc_client_add_channel(cmd->client, conn, name, 0, + channel_id); + if (!channel_entry) { + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + channel_id = NULL; + } + /* Notify application */ - COMMAND_REPLY((ARGS, NULL, name, topic, usercount)); + COMMAND_REPLY((SILC_ARGS, channel_entry, name, topic, usercount)); /* Pending callbacks are not executed if this was an list entry */ - if (status != SILC_STATUS_OK && - status != SILC_STATUS_LIST_END) { + if (cmd->status != SILC_STATUS_OK && + cmd->status != SILC_STATUS_LIST_END) { silc_client_command_reply_free(cmd); return; } out: + silc_free(channel_id); SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LIST); + err: silc_client_command_reply_free(cmd); } @@ -650,25 +667,22 @@ SILC_CLIENT_CMD_REPLY_FUNC(topic) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; SilcChannelEntry channel; SilcChannelID *channel_id = NULL; - SilcIDCacheEntry id_cache = NULL; unsigned char *tmp; char *topic; - uint32 argc, len; + SilcUInt32 argc, len; - SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot set topic: %s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } argc = silc_argument_get_arg_num(cmd->args); if (argc < 1 || argc > 3) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -682,26 +696,23 @@ SILC_CLIENT_CMD_REPLY_FUNC(topic) if (!topic) goto out; - channel_id = silc_id_payload_parse_id(tmp, len); + channel_id = silc_id_payload_parse_id(tmp, len, NULL); if (!channel_id) goto out; /* Get the channel entry */ - if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id, - &id_cache)) { + channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + if (!channel) { silc_free(channel_id); - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - - channel = (SilcChannelEntry)id_cache->context; /* Notify application */ - COMMAND_REPLY((ARGS, channel, topic)); + COMMAND_REPLY((SILC_ARGS, channel, topic)); out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC); silc_client_command_reply_free(cmd); } @@ -711,19 +722,16 @@ SILC_CLIENT_CMD_REPLY_FUNC(invite) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; SilcChannelEntry channel; SilcChannelID *channel_id; - SilcIDCacheEntry id_cache; unsigned char *tmp; - uint32 len; + SilcUInt32 len; + SilcBufferStruct buf; - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot invite: %s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } @@ -732,54 +740,67 @@ SILC_CLIENT_CMD_REPLY_FUNC(invite) if (!tmp) goto out; - channel_id = silc_id_payload_parse_id(tmp, len); + channel_id = silc_id_payload_parse_id(tmp, len, NULL); if (!channel_id) goto out; /* Get the channel entry */ - if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id, - &id_cache)) { + channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + if (!channel) { silc_free(channel_id); - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - - channel = (SilcChannelEntry)id_cache->context; /* Get the invite list */ tmp = silc_argument_get_arg_type(cmd->args, 3, &len); + if (tmp) + silc_buffer_set(&buf, tmp, len); /* Notify application */ - COMMAND_REPLY((ARGS, channel, tmp)); + COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL)); out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE); silc_client_command_reply_free(cmd); } /* Received reply to the KILL command. */ - + SILC_CLIENT_CMD_REPLY_FUNC(kill) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; + SilcClientID *client_id; + SilcClientEntry client_entry = NULL; + SilcUInt32 len; + unsigned char *id_data; - SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot kill: %s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } + id_data = silc_argument_get_arg_type(cmd->args, 2, &len); + if (id_data) { + client_id = silc_id_payload_parse_id(id_data, len, NULL); + if (!client_id) { + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + /* Get the client entry, if exists */ + client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); + silc_free(client_id); + } + /* Notify application */ - COMMAND_REPLY((ARGS)); + COMMAND_REPLY((SILC_ARGS, client_entry)); out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KILL); silc_client_command_reply_free(cmd); } @@ -790,20 +811,18 @@ SILC_CLIENT_CMD_REPLY_FUNC(info) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; unsigned char *tmp; - SilcIDCacheEntry id_cache; SilcServerEntry server; SilcServerID *server_id = NULL; char *server_name, *server_info; - uint32 len; + SilcUInt32 len; - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + SILC_LOG_DEBUG(("Start")); + + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", + silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } @@ -812,7 +831,7 @@ SILC_CLIENT_CMD_REPLY_FUNC(info) if (!tmp) goto out; - server_id = silc_id_payload_parse_id(tmp, len); + server_id = silc_id_payload_parse_id(tmp, len, NULL); if (!server_id) goto out; @@ -827,77 +846,97 @@ SILC_CLIENT_CMD_REPLY_FUNC(info) goto out; /* See whether we have this server cached. If not create it. */ - if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id, - &id_cache)) { + server = silc_client_get_server_by_id(cmd->client, conn, server_id); + if (!server) { SILC_LOG_DEBUG(("New server entry")); - - server = silc_calloc(1, sizeof(*server)); - server->server_name = strdup(server_name); - server->server_info = strdup(server_info); - server->server_id = silc_id_dup(server_id, SILC_ID_SERVER); - - /* Add it to the cache */ - silc_idcache_add(conn->server_cache, server->server_name, - server->server_id, (void *)server, 0, NULL); - - if (SILC_ID_SERVER_COMPARE(server_id, conn->remote_id)) + server = silc_client_add_server(cmd->client, conn, server_name, + server_info, + silc_id_dup(server_id, SILC_ID_SERVER)); + if (!server) goto out; - } else { - server = (SilcServerEntry)id_cache->context; } - + /* Notify application */ - COMMAND_REPLY((ARGS, server, server->server_name, server->server_info)); + COMMAND_REPLY((SILC_ARGS, server, server->server_name, server->server_info)); out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO); silc_free(server_id); silc_client_command_reply_free(cmd); } +/* Received reply to STATS command. */ + +SILC_CLIENT_CMD_REPLY_FUNC(stats) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + unsigned char *tmp, *buf = NULL; + SilcUInt32 len, buf_len = 0; + + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); + goto out; + } + + /* Get server ID */ + tmp = silc_argument_get_arg_type(cmd->args, 2, &len); + if (!tmp) + goto out; + + /* Get statistics structure */ + buf = silc_argument_get_arg_type(cmd->args, 3, &buf_len); + + /* Notify application */ + COMMAND_REPLY((SILC_ARGS, buf, buf_len)); + + out: + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_STATS); + silc_client_command_reply_free(cmd); +} + /* Received reply to PING command. The reply time is shown to user. */ SILC_CLIENT_CMD_REPLY_FUNC(ping) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; void *id; int i; time_t diff, curtime; - SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } curtime = time(NULL); id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len, cmd->packet->src_id_type); - if (!id || !conn->ping) { - COMMAND_REPLY_ERROR; + if (!id || !conn->internal->ping) { + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - for (i = 0; i < conn->ping_count; i++) { - if (!conn->ping[i].dest_id) + for (i = 0; i < conn->internal->ping_count; i++) { + if (!conn->internal->ping[i].dest_id) continue; - if (SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) { - diff = curtime - conn->ping[i].start_time; - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "Ping reply from %s: %d second%s", - conn->ping[i].dest_name, diff, - diff == 1 ? "" : "s"); - - conn->ping[i].start_time = 0; - silc_free(conn->ping[i].dest_id); - conn->ping[i].dest_id = NULL; - silc_free(conn->ping[i].dest_name); - conn->ping[i].dest_name = NULL; + if (SILC_ID_SERVER_COMPARE(conn->internal->ping[i].dest_id, id)) { + diff = curtime - conn->internal->ping[i].start_time; + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, + "Ping reply from %s: %d second%s", + conn->internal->ping[i].dest_name, diff, + diff == 1 ? "" : "s"); + + conn->internal->ping[i].start_time = 0; + silc_free(conn->internal->ping[i].dest_id); + conn->internal->ping[i].dest_id = NULL; + silc_free(conn->internal->ping[i].dest_name); + conn->internal->ping[i].dest_name = NULL; break; } } @@ -905,11 +944,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(ping) silc_free(id); /* Notify application */ - COMMAND_REPLY((ARGS)); + COMMAND_REPLY((SILC_ARGS)); out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING); silc_client_command_reply_free(cmd); } @@ -919,58 +957,55 @@ SILC_CLIENT_CMD_REPLY_FUNC(join) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; - SilcIDPayload idp = NULL; SilcChannelEntry channel; - SilcIDCacheEntry id_cache = NULL; SilcChannelUser chu; - uint32 argc, mode, len, list_count; + SilcChannelID *channel_id; + SilcUInt32 argc, mode = 0, len, list_count; char *topic, *tmp, *channel_name = NULL, *hmac; SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL; + SilcPublicKey founder_key = NULL; + SilcBufferStruct chpklist; int i; SILC_LOG_DEBUG(("Start")); - SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); - if (status != SILC_STATUS_OK) { - if (status != SILC_STATUS_ERR_USER_ON_CHANNEL) - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + if (cmd->error != SILC_STATUS_ERR_USER_ON_CHANNEL) + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot join channel: %s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } argc = silc_argument_get_arg_num(cmd->args); - if (argc < 7 || argc > 14) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Cannot join channel: Bad reply packet"); - COMMAND_REPLY_ERROR; + if (argc < 7) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot join channel: Bad reply packet"); + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } /* Get channel name */ tmp = silc_argument_get_arg_type(cmd->args, 2, NULL); if (!tmp) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Cannot join channel: Bad reply packet"); - COMMAND_REPLY_ERROR; + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot join channel: Bad reply packet"); + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - channel_name = strdup(tmp); + channel_name = tmp; /* Get Channel ID */ tmp = silc_argument_get_arg_type(cmd->args, 3, &len); if (!tmp) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Cannot join channel: Bad reply packet"); - COMMAND_REPLY_ERROR; - silc_free(channel_name); + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot join channel: Bad reply packet"); + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - idp = silc_id_payload_parse(tmp, len); - if (!idp) { - COMMAND_REPLY_ERROR; - silc_free(channel_name); + channel_id = silc_id_payload_parse_id(tmp, len, NULL); + if (!channel_id) { + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -978,8 +1013,6 @@ SILC_CLIENT_CMD_REPLY_FUNC(join) tmp = silc_argument_get_arg_type(cmd->args, 5, NULL); if (tmp) SILC_GET32_MSB(mode, tmp); - else - mode = 0; /* Get channel key */ tmp = silc_argument_get_arg_type(cmd->args, 7, &len); @@ -992,15 +1025,16 @@ SILC_CLIENT_CMD_REPLY_FUNC(join) /* Get topic */ topic = silc_argument_get_arg_type(cmd->args, 10, NULL); - /* If we have the channel entry, remove it and create a new one */ + /* Check whether we have this channel entry already. */ channel = silc_client_get_channel(cmd->client, conn, channel_name); - if (channel) - silc_client_del_channel(cmd->client, conn, channel); - - /* Save received Channel ID. This actually creates the channel */ - channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, - mode, idp); - silc_id_payload_free(idp); + if (channel) { + if (!SILC_ID_CHANNEL_COMPARE(channel->id, channel_id)) + silc_client_replace_channel_id(cmd->client, conn, channel, channel_id); + } else { + /* Create new channel entry */ + channel = silc_client_add_channel(cmd->client, conn, channel_name, + mode, channel_id); + } conn->current_channel = channel; @@ -1008,11 +1042,9 @@ SILC_CLIENT_CMD_REPLY_FUNC(join) hmac = silc_argument_get_arg_type(cmd->args, 11, NULL); if (hmac) { if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Cannot join channel: Unsupported HMAC `%s'", - hmac); - COMMAND_REPLY_ERROR; - silc_free(channel_name); + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot join channel: Unsupported HMAC `%s'", hmac); + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } } @@ -1043,15 +1075,15 @@ SILC_CLIENT_CMD_REPLY_FUNC(join) /* Add clients we received in the reply to the channel */ for (i = 0; i < list_count; i++) { - uint16 idp_len; - uint32 mode; + SilcUInt16 idp_len; + SilcUInt32 mode; SilcClientID *client_id; SilcClientEntry client_entry; /* 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); + client_id = silc_id_payload_parse_id(client_id_list->data, idp_len, NULL); if (!client_id) continue; @@ -1059,59 +1091,61 @@ SILC_CLIENT_CMD_REPLY_FUNC(join) SILC_GET32_MSB(mode, client_mode_list->data); /* Check if we have this client cached already. */ - if (!silc_idcache_find_by_id_one_ext(conn->client_cache, - (void *)client_id, - NULL, NULL, - silc_hash_client_id_compare, NULL, - &id_cache)) { + client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); + if (!client_entry) { /* No, we don't have it, add entry for it. */ - client_entry = + client_entry = silc_client_add_client(cmd->client, conn, NULL, NULL, NULL, silc_id_dup(client_id, SILC_ID_CLIENT), 0); - } else { - /* Yes, we have it already */ - client_entry = (SilcClientEntry)id_cache->context; } - /* Join the client to the channel */ - chu = silc_calloc(1, sizeof(*chu)); - chu->client = client_entry; - chu->mode = mode; - silc_list_add(channel->clients, chu); - silc_free(client_id); + /* Join client to the channel */ + if (!silc_client_on_channel(channel, client_entry)) { + chu = silc_calloc(1, sizeof(*chu)); + chu->client = client_entry; + chu->channel = channel; + chu->mode = mode; + silc_hash_table_add(channel->user_list, client_entry, chu); + silc_hash_table_add(client_entry->channels, channel, chu); + } + silc_free(client_id); silc_buffer_pull(client_id_list, idp_len); silc_buffer_pull(client_mode_list, 4); } - silc_buffer_push(client_id_list, client_id_list->data - + silc_buffer_push(client_id_list, client_id_list->data - client_id_list->head); - silc_buffer_push(client_mode_list, client_mode_list->data - + silc_buffer_push(client_mode_list, client_mode_list->data - client_mode_list->head); /* Save channel key */ - if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) - silc_client_save_channel_key(conn, keyp, channel); + if (keyp && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) + silc_client_save_channel_key(cmd->client, conn, keyp, channel); + + /* 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); - /* Client is now joined to the channel */ - channel->on_channel = TRUE; + /* Get channel public key list */ + tmp = silc_argument_get_arg_type(cmd->args, 16, &len); + if (tmp) + silc_buffer_set(&chpklist, tmp, len); /* Notify application */ - COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, + COMMAND_REPLY((SILC_ARGS, channel_name, channel, mode, 0, keyp ? keyp->head : NULL, NULL, - NULL, topic, hmac, list_count, client_id_list, - client_mode_list)); + NULL, topic, hmac, list_count, client_id_list, + client_mode_list, founder_key, tmp ? &chpklist : NULL)); out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN); silc_client_command_reply_free(cmd); - - if (keyp) - silc_buffer_free(keyp); - if (client_id_list) - silc_buffer_free(client_id_list); - if (client_mode_list) - silc_buffer_free(client_mode_list); + if (founder_key) + silc_pkcs_public_key_free(founder_key); + silc_buffer_free(keyp); + silc_buffer_free(client_id_list); + silc_buffer_free(client_mode_list); } /* Received reply for MOTD command */ @@ -1120,30 +1154,26 @@ SILC_CLIENT_CMD_REPLY_FUNC(motd) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; - uint32 argc, i; - unsigned char *tmp; + SilcUInt32 argc, i; char *motd = NULL, *cp, line[256]; - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); return; } argc = silc_argument_get_arg_num(cmd->args); if (argc > 3) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } if (argc == 3) { motd = silc_argument_get_arg_type(cmd->args, 3, NULL); if (!motd) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -1152,15 +1182,14 @@ SILC_CLIENT_CMD_REPLY_FUNC(motd) while(cp[i] != 0) { if (cp[i++] == '\n') { memset(line, 0, sizeof(line)); - strncat(line, cp, i - 1); + silc_strncat(line, sizeof(line), cp, i - 1); cp += i; - + if (i == 2) line[0] = ' '; - - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, - "%s", line); - + + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line); + if (!strlen(cp)) break; i = 0; @@ -1169,11 +1198,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(motd) } /* Notify application */ - COMMAND_REPLY((ARGS, motd)); + COMMAND_REPLY((SILC_ARGS, motd)); out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD); silc_client_command_reply_free(cmd); } @@ -1183,22 +1211,19 @@ SILC_CLIENT_CMD_REPLY_FUNC(umode) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; unsigned char *tmp; - uint32 mode; + SilcUInt32 mode; - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot change mode: %s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } tmp = silc_argument_get_arg_type(cmd->args, 2, NULL); if (!tmp) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -1206,11 +1231,10 @@ SILC_CLIENT_CMD_REPLY_FUNC(umode) conn->local_entry->mode = mode; /* Notify application */ - COMMAND_REPLY((ARGS, mode)); + COMMAND_REPLY((SILC_ARGS, mode)); out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_UMODE); silc_client_command_reply_free(cmd); } @@ -1220,19 +1244,18 @@ SILC_CLIENT_CMD_REPLY_FUNC(cmode) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; unsigned char *tmp; - uint32 mode; - SilcIDCacheEntry id_cache; + SilcUInt32 mode; SilcChannelID *channel_id; SilcChannelEntry channel; - uint32 len; + SilcUInt32 len; + SilcPublicKey public_key = NULL; + SilcBufferStruct channel_pubkeys; - SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot change mode: %s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } @@ -1240,25 +1263,23 @@ SILC_CLIENT_CMD_REPLY_FUNC(cmode) tmp = silc_argument_get_arg_type(cmd->args, 2, &len); if (!tmp) goto out; - channel_id = silc_id_payload_parse_id(tmp, len); + channel_id = silc_id_payload_parse_id(tmp, len, NULL); if (!channel_id) goto out; /* Get the channel entry */ - if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id, - &id_cache)) { + channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + if (!channel) { silc_free(channel_id); - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - - channel = (SilcChannelEntry)id_cache->context; /* Get channel mode */ tmp = silc_argument_get_arg_type(cmd->args, 3, NULL); if (!tmp) { silc_free(channel_id); - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -1266,14 +1287,28 @@ SILC_CLIENT_CMD_REPLY_FUNC(cmode) SILC_GET32_MSB(mode, tmp); channel->mode = mode; + /* Get founder public key */ + tmp = silc_argument_get_arg_type(cmd->args, 4, &len); + if (tmp) { + if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key)) + public_key = NULL; + } + + /* Get channel public key(s) */ + tmp = silc_argument_get_arg_type(cmd->args, 5, &len); + if (tmp) + silc_buffer_set(&channel_pubkeys, tmp, len); + /* Notify application */ - COMMAND_REPLY((ARGS, channel, mode)); + COMMAND_REPLY((SILC_ARGS, channel, mode, public_key, + tmp ? &channel_pubkeys : NULL)); silc_free(channel_id); out: + if (public_key) + silc_pkcs_public_key_free(public_key); SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE); silc_client_command_reply_free(cmd); } @@ -1283,28 +1318,25 @@ SILC_CLIENT_CMD_REPLY_FUNC(cumode) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; - SilcIDCacheEntry id_cache = NULL; SilcClientID *client_id; SilcChannelID *channel_id; SilcClientEntry client_entry; SilcChannelEntry channel; SilcChannelUser chu; unsigned char *modev, *tmp, *id; - uint32 len, mode; - - SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + SilcUInt32 len, mode; + + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot change mode: %s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } - + /* Get channel mode */ modev = silc_argument_get_arg_type(cmd->args, 2, NULL); if (!modev) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -1312,65 +1344,54 @@ SILC_CLIENT_CMD_REPLY_FUNC(cumode) tmp = silc_argument_get_arg_type(cmd->args, 3, &len); if (!tmp) goto out; - channel_id = silc_id_payload_parse_id(tmp, len); + channel_id = silc_id_payload_parse_id(tmp, len, NULL); if (!channel_id) goto out; /* Get the channel entry */ - if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id, - &id_cache)) { + channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + if (!channel) { silc_free(channel_id); - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - - channel = (SilcChannelEntry)id_cache->context; /* Get Client ID */ id = silc_argument_get_arg_type(cmd->args, 4, &len); if (!id) { silc_free(channel_id); - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - client_id = silc_id_payload_parse_id(id, len); + client_id = silc_id_payload_parse_id(id, len, NULL); if (!client_id) { silc_free(channel_id); - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - + /* Get client entry */ - if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, - NULL, NULL, - silc_hash_client_id_compare, NULL, - &id_cache)) { + client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); + if (!client_entry) { silc_free(channel_id); silc_free(client_id); - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - client_entry = (SilcClientEntry)id_cache->context; - /* Save the mode */ SILC_GET32_MSB(mode, modev); - silc_list_start(channel->clients); - while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) { - if (chu->client == client_entry) { - chu->mode = mode; - break; - } - } + chu = silc_client_on_channel(channel, client_entry); + if (chu) + chu->mode = mode; /* Notify application */ - COMMAND_REPLY((ARGS, mode, channel, client_entry)); + COMMAND_REPLY((SILC_ARGS, mode, channel, client_entry)); silc_free(client_id); silc_free(channel_id); - + out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE); silc_client_command_reply_free(cmd); } @@ -1378,24 +1399,61 @@ SILC_CLIENT_CMD_REPLY_FUNC(kick) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; + SilcClientID *client_id = NULL; + SilcChannelID *channel_id = NULL; + SilcClientEntry client_entry = NULL; + SilcChannelEntry channel = NULL; unsigned char *tmp; + SilcUInt32 len; - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Cannot kick: %s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } + /* Take Channel ID */ + tmp = silc_argument_get_arg_type(cmd->args, 2, &len); + if (tmp) { + channel_id = silc_id_payload_parse_id(tmp, len, NULL); + if (!channel_id) { + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + /* Get the channel entry */ + channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + if (!channel) { + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + } + + /* Get Client ID */ + tmp = silc_argument_get_arg_type(cmd->args, 3, &len); + if (tmp) { + client_id = silc_id_payload_parse_id(tmp, len, NULL); + if (!client_id) { + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + /* Get client entry */ + client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); + if (!client_entry) { + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + } + /* Notify application */ - COMMAND_REPLY((ARGS)); + COMMAND_REPLY((SILC_ARGS, channel, client_entry)); out: + silc_free(channel_id); + silc_free(client_id); SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK); silc_client_command_reply_free(cmd); } @@ -1403,24 +1461,19 @@ SILC_CLIENT_CMD_REPLY_FUNC(silcoper) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; - unsigned char *tmp; - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } /* Notify application */ - COMMAND_REPLY((ARGS)); + COMMAND_REPLY((SILC_ARGS)); out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SILCOPER); silc_client_command_reply_free(cmd); } @@ -1428,49 +1481,69 @@ SILC_CLIENT_CMD_REPLY_FUNC(oper) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; - unsigned char *tmp; - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } /* Notify application */ - COMMAND_REPLY((ARGS)); + COMMAND_REPLY((SILC_ARGS)); out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_OPER); silc_client_command_reply_free(cmd); } -SILC_CLIENT_CMD_REPLY_FUNC(connect) +SILC_CLIENT_CMD_REPLY_FUNC(detach) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; - unsigned char *tmp; + SilcBuffer detach; + + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); + goto out; + } - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + /* Notify application */ + COMMAND_REPLY((SILC_ARGS)); + + /* Generate the detachment data and deliver it to the client in the + detach client operation */ + detach = silc_client_get_detach_data(cmd->client, conn); + if (detach) { + cmd->client->internal->ops->detach(cmd->client, conn, + detach->data, detach->len); + silc_buffer_free(detach); + } + + out: + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH); + silc_client_command_reply_free(cmd); +} + +SILC_CLIENT_CMD_REPLY_FUNC(watch) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } /* Notify application */ - COMMAND_REPLY((ARGS)); + COMMAND_REPLY((SILC_ARGS)); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT); + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WATCH); silc_client_command_reply_free(cmd); } @@ -1478,19 +1551,16 @@ SILC_CLIENT_CMD_REPLY_FUNC(ban) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; - SilcIDCacheEntry id_cache = NULL; SilcChannelEntry channel; SilcChannelID *channel_id; unsigned char *tmp; - uint32 len; + SilcUInt32 len; + SilcBufferStruct buf; - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } @@ -1499,156 +1569,151 @@ SILC_CLIENT_CMD_REPLY_FUNC(ban) if (!tmp) goto out; - channel_id = silc_id_payload_parse_id(tmp, len); + channel_id = silc_id_payload_parse_id(tmp, len, NULL); if (!channel_id) goto out; /* Get the channel entry */ - if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id, - &id_cache)) { + channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + if (!channel) { silc_free(channel_id); - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - - channel = (SilcChannelEntry)id_cache->context; /* Get the ban list */ tmp = silc_argument_get_arg_type(cmd->args, 3, &len); + if (tmp) + silc_buffer_set(&buf, tmp, len); /* Notify application */ - COMMAND_REPLY((ARGS, channel, tmp)); + COMMAND_REPLY((SILC_ARGS, channel, tmp ? &buf : NULL)); out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_BAN); silc_client_command_reply_free(cmd); } -SILC_CLIENT_CMD_REPLY_FUNC(close) +/* Reply to LEAVE command. */ + +SILC_CLIENT_CMD_REPLY_FUNC(leave) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; + SilcChannelID *channel_id; + SilcChannelEntry channel = NULL; + SilcChannelUser chu; unsigned char *tmp; + SilcUInt32 len; - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } - /* Notify application */ - COMMAND_REPLY((ARGS)); + /* From protocol version 1.1 we get the channel ID of the left channel */ + tmp = silc_argument_get_arg_type(cmd->args, 2, &len); + if (tmp) { + channel_id = silc_id_payload_parse_id(tmp, len, NULL); + if (!channel_id) + goto out; - out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE); - silc_client_command_reply_free(cmd); -} - -SILC_CLIENT_CMD_REPLY_FUNC(shutdown) -{ - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; - unsigned char *tmp; + /* Get the channel entry */ + channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + if (!channel) { + silc_free(channel_id); + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; - goto out; + /* Remove us from this channel. */ + chu = silc_client_on_channel(channel, conn->local_entry); + if (chu) { + silc_hash_table_del(chu->client->channels, chu->channel); + silc_hash_table_del(chu->channel->user_list, chu->client); + silc_free(chu); + } + + silc_free(channel_id); } /* Notify application */ - COMMAND_REPLY((ARGS)); + COMMAND_REPLY((SILC_ARGS, channel)); + + /* Now delete the channel. */ + if (channel) + silc_client_del_channel(cmd->client, conn, channel); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN); + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE); silc_client_command_reply_free(cmd); } - -/* Reply to LEAVE command. */ -SILC_CLIENT_CMD_REPLY_FUNC(leave) -{ - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; - unsigned char *tmp; +/* Channel resolving callback for USERS command reply. */ - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; - goto out; +static void silc_client_command_reply_users_cb(SilcClient client, + SilcClientConnection conn, + SilcChannelEntry *channels, + SilcUInt32 channels_count, + void *context) +{ + if (!channels_count) { + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + + cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL; + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS); + silc_client_command_reply_free(cmd); + return; } - /* Notify application */ - COMMAND_REPLY((ARGS)); - - out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE); - silc_client_command_reply_free(cmd); + silc_client_command_reply_users(context, NULL); } -/* Reply to USERS command. Received list of client ID's and theirs modes - on the channel we requested. */ - -SILC_CLIENT_CMD_REPLY_FUNC(users) +static int +silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd, + SilcStatus status, + bool notify, + bool resolve, + SilcGetChannelCallback get_channel, + SilcCommandCb get_clients) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; - SilcIDCacheEntry id_cache = NULL; SilcChannelEntry channel; + SilcClientEntry client_entry; SilcChannelUser chu; SilcChannelID *channel_id = NULL; - SilcBuffer client_id_list = NULL; - SilcBuffer client_mode_list = NULL; + SilcBufferStruct client_id_list, client_mode_list; unsigned char *tmp; - uint32 tmp_len, list_count; + SilcUInt32 tmp_len, list_count; int i; unsigned char **res_argv = NULL; - uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0; + SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0; + bool wait_res = FALSE; SILC_LOG_DEBUG(("Start")); - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; - goto out; - } - /* Get channel ID */ tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); if (!tmp) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); 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) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - + /* Get the list count */ tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); if (!tmp) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } SILC_GET32_MSB(list_count, tmp); @@ -1656,114 +1721,79 @@ SILC_CLIENT_CMD_REPLY_FUNC(users) /* Get Client ID list */ tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len); if (!tmp) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - - client_id_list = silc_buffer_alloc(tmp_len); - silc_buffer_pull_tail(client_id_list, tmp_len); - silc_buffer_put(client_id_list, tmp, tmp_len); + silc_buffer_set(&client_id_list, tmp, tmp_len); /* Get client mode list */ tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len); if (!tmp) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - - client_mode_list = silc_buffer_alloc(tmp_len); - silc_buffer_pull_tail(client_mode_list, tmp_len); - silc_buffer_put(client_mode_list, tmp, tmp_len); + silc_buffer_set(&client_mode_list, tmp, tmp_len); /* Get channel entry */ - if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id, - &id_cache)) { + channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + if (!channel) { /* Resolve the channel from server */ - silc_idlist_get_channel_by_id(cmd->client, conn, channel_id, TRUE); - - /* 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_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident, - NULL, silc_client_command_reply_users, cmd); - return; - } else { - channel = (SilcChannelEntry)id_cache->context; + silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id, + get_channel, cmd); + silc_free(channel_id); + return 1; } - /* Remove old client list from channel. */ - silc_list_start(channel->clients); - while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) { - silc_list_del(channel->clients, chu); - silc_free(chu); - } + SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count)); /* Cache the received Client ID's and modes. */ for (i = 0; i < list_count; i++) { - uint16 idp_len; - uint32 mode; + SilcUInt16 idp_len; + SilcUInt32 mode; SilcClientID *client_id; - SilcClientEntry client; /* Client ID */ - SILC_GET16_MSB(idp_len, client_id_list->data + 2); + 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); + client_id = silc_id_payload_parse_id(client_id_list.data, idp_len, NULL); if (!client_id) continue; /* Mode */ - SILC_GET32_MSB(mode, client_mode_list->data); + SILC_GET32_MSB(mode, client_mode_list.data); /* Check if we have this client cached already. */ - id_cache = NULL; - silc_idcache_find_by_id_one_ext(conn->client_cache, - (void *)client_id, - NULL, NULL, - silc_hash_client_id_compare, NULL, - &id_cache); - - if (!id_cache || !((SilcClientEntry)id_cache->context)->username || - !((SilcClientEntry)id_cache->context)->realname) { - - if (id_cache && id_cache->context) { - SilcClientEntry client_entry = (SilcClientEntry)id_cache->context; - if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) { - client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING; - silc_buffer_pull(client_id_list, idp_len); - silc_buffer_pull(client_mode_list, 4); - continue; - } - client_entry->status |= SILC_CLIENT_STATUS_RESOLVING; + client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); + if (!client_entry || !client_entry->username || !client_entry->realname) { + if (resolve) { + /* No we don't have it (or it is incomplete in information), query + it from the server. Assemble argument table that will be sent + for the WHOIS command later. */ + res_argv = silc_realloc(res_argv, sizeof(*res_argv) * + (res_argc + 1)); + res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) * + (res_argc + 1)); + res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) * + (res_argc + 1)); + res_argv[res_argc] = client_id_list.data; + res_argv_lens[res_argc] = idp_len; + res_argv_types[res_argc] = res_argc + 4; + res_argc++; } - - /* No we don't have it (or it is incomplete in information), query - it from the server. Assemble argument table that will be sent - for the WHOIS command later. */ - res_argv = silc_realloc(res_argv, sizeof(*res_argv) * - (res_argc + 1)); - res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) * - (res_argc + 1)); - res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) * - (res_argc + 1)); - res_argv[res_argc] = client_id_list->data; - res_argv_lens[res_argc] = idp_len; - res_argv_types[res_argc] = res_argc + 3; - res_argc++; } else { - /* Found the client, join it to the channel */ - client = (SilcClientEntry)id_cache->context; - chu = silc_calloc(1, sizeof(*chu)); - chu->client = client; - chu->mode = mode; - silc_list_add(channel->clients, chu); - - silc_free(client_id); - id_cache = NULL; + if (!silc_client_on_channel(channel, client_entry)) { + chu = silc_calloc(1, sizeof(*chu)); + chu->client = client_entry; + chu->mode = mode; + chu->channel = channel; + silc_hash_table_add(channel->user_list, client_entry, chu); + silc_hash_table_add(client_entry->channels, channel, chu); + } } - silc_buffer_pull(client_id_list, idp_len); - silc_buffer_pull(client_mode_list, 4); + silc_free(client_id); + silc_buffer_pull(&client_id_list, idp_len); + silc_buffer_pull(&client_mode_list, 4); } /* Query the client information from server if the list included clients @@ -1772,10 +1802,13 @@ SILC_CLIENT_CMD_REPLY_FUNC(users) SilcBuffer res_cmd; /* Send the WHOIS command to server */ + silc_client_command_register(cmd->client, SILC_COMMAND_WHOIS, NULL, NULL, + silc_client_command_reply_whois_i, 0, + ++conn->cmd_ident); res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS, res_argc, res_argv, res_argv_lens, - res_argv_types, ++conn->cmd_ident); - silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, + res_argv_types, conn->cmd_ident); + silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len, TRUE); @@ -1783,30 +1816,77 @@ SILC_CLIENT_CMD_REPLY_FUNC(users) command reply we will reprocess this command reply by re-calling this USERS command reply callback. */ silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident, - NULL, silc_client_command_reply_users, cmd); + get_clients, cmd); silc_buffer_free(res_cmd); - if (channel_id) - silc_free(channel_id); - + silc_free(channel_id); silc_free(res_argv); silc_free(res_argv_lens); silc_free(res_argv_types); - return; + return 1; } + if (wait_res) + return 1; + + silc_buffer_push(&client_id_list, (client_id_list.data - + client_id_list.head)); + silc_buffer_push(&client_mode_list, (client_mode_list.data - + client_mode_list.head)); + /* Notify application */ - COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list)); + if (notify) + COMMAND_REPLY((SILC_ARGS, channel, list_count, &client_id_list, + &client_mode_list)); + + out: + silc_free(channel_id); + return 0; +} + +/* Reply to USERS command. Received list of client ID's and theirs modes + on the channel we requested. */ + +SILC_CLIENT_CMD_REPLY_FUNC(users) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcClientCommandReplyContext r = (SilcClientCommandReplyContext)context2; + + SILC_LOG_DEBUG(("Start")); + + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Query failed: %s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); + goto out; + } + + if (r && !silc_command_get_status(r->payload, NULL, &cmd->error)) { + if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) { + /* Do not resolve anymore. Server may be sending us some non-existent + Client ID (a bug in server), and we want to show the users list + anyway. */ + silc_client_command_reply_users_save(cmd, cmd->status, TRUE, FALSE, + silc_client_command_reply_users_cb, + silc_client_command_reply_users); + goto out; + } else { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "Query failed: %s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + } + + if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE, TRUE, + silc_client_command_reply_users_cb, + silc_client_command_reply_users)) + return; out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS); silc_client_command_reply_free(cmd); - silc_free(channel_id); - if (client_id_list) - silc_buffer_free(client_id_list); - if (client_mode_list) - silc_buffer_free(client_mode_list); } /* Received command reply to GETKEY command. WE've received the remote @@ -1816,96 +1896,78 @@ SILC_CLIENT_CMD_REPLY_FUNC(getkey) { SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcCommandStatus status; - SilcIDCacheEntry id_cache; SilcIDPayload idp = NULL; SilcClientID *client_id = NULL; SilcClientEntry client_entry; SilcServerID *server_id = NULL; SilcServerEntry server_entry; - SilcSKEPKType type; - unsigned char *tmp, *pk; - uint32 len; - uint16 pk_len; + unsigned char *tmp; + SilcUInt32 len; SilcIdType id_type; SilcPublicKey public_key = NULL; SILC_LOG_DEBUG(("Start")); - tmp = silc_argument_get_arg_type(cmd->args, 1, NULL); - SILC_GET16_MSB(status, tmp); - if (status != SILC_STATUS_OK) { - cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(status)); - COMMAND_REPLY_ERROR; + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); goto out; } tmp = silc_argument_get_arg_type(cmd->args, 2, &len); if (!tmp) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } idp = silc_id_payload_parse(tmp, len); if (!idp) { - COMMAND_REPLY_ERROR; + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } /* Get the public key payload */ tmp = silc_argument_get_arg_type(cmd->args, 3, &len); if (tmp) { - /* 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) { - COMMAND_REPLY_ERROR; - goto out; - } - - if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key)) { - COMMAND_REPLY_ERROR; - goto out; - } - } - + if (!silc_pkcs_public_key_payload_decode(tmp, len, &public_key)) + public_key = NULL; + } + id_type = silc_id_payload_get_type(idp); if (id_type == SILC_ID_CLIENT) { /* Received client's public key */ client_id = silc_id_payload_get_id(idp); - if (!silc_idcache_find_by_id_one_ext(conn->client_cache, - (void *)client_id, - NULL, NULL, - silc_hash_client_id_compare, NULL, - &id_cache)) { - COMMAND_REPLY_ERROR; + client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); + if (!client_entry) { + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - client_entry = (SilcClientEntry)id_cache->context; + /* Save fingerprint */ + if (!client_entry->fingerprint) { + client_entry->fingerprint = silc_calloc(20, sizeof(unsigned char)); + client_entry->fingerprint_len = 20; + silc_hash_make(cmd->client->sha1hash, tmp + 4, len - 4, + client_entry->fingerprint); + } /* Notify application */ - COMMAND_REPLY((ARGS, id_type, client_entry, public_key)); + COMMAND_REPLY((SILC_ARGS, id_type, client_entry, public_key)); } else if (id_type == SILC_ID_SERVER) { /* Received server's public key */ server_id = silc_id_payload_get_id(idp); - if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id, - &id_cache)) { - COMMAND_REPLY_ERROR; + server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id); + if (!server_entry) { + COMMAND_REPLY_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - server_entry = (SilcServerEntry)id_cache->context; - /* Notify application */ - COMMAND_REPLY((ARGS, id_type, server_entry, public_key)); + COMMAND_REPLY((SILC_ARGS, id_type, server_entry, public_key)); } out: SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY); - SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_GETKEY); if (idp) silc_id_payload_free(idp); if (public_key) @@ -1914,3 +1976,269 @@ SILC_CLIENT_CMD_REPLY_FUNC(getkey) silc_free(server_id); silc_client_command_reply_free(cmd); } + +SILC_CLIENT_CMD_REPLY_FUNC(quit) +{ + silc_client_command_reply_free(context); +} + + +/****************************************************************************** + + Internal command reply functions + +******************************************************************************/ + +SILC_CLIENT_CMD_REPLY_FUNC(whois_i) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + + COMMAND_CHECK_STATUS_I; + + /* Save WHOIS info */ + silc_client_command_reply_whois_save(cmd, cmd->status, FALSE); + + /* Pending callbacks are not executed if this was an list entry */ + if (cmd->status != SILC_STATUS_OK && + cmd->status != SILC_STATUS_LIST_END) { + silc_client_command_reply_free(cmd); + return; + } + + out: + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS); + + err: + /* If we received notify for invalid ID we'll remove the ID if we + have it cached. */ + if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) { + SilcClientEntry client_entry; + SilcUInt32 tmp_len; + unsigned char *tmp = + silc_argument_get_arg_type(silc_command_get_args(cmd->payload), + 2, &tmp_len); + if (tmp) { + SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); + if (client_id) { + client_entry = silc_client_get_client_by_id(cmd->client, conn, + client_id); + if (client_entry) + silc_client_del_client(cmd->client, conn, client_entry); + silc_free(client_id); + } + } + } + + /* Unregister this command reply */ + silc_client_command_unregister(cmd->client, SILC_COMMAND_WHOIS, + NULL, silc_client_command_reply_whois_i, + cmd->ident); + + silc_client_command_reply_free(cmd); +} + +SILC_CLIENT_CMD_REPLY_FUNC(identify_i) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + + COMMAND_CHECK_STATUS_I; + + /* Save IDENTIFY info */ + silc_client_command_reply_identify_save(cmd, cmd->status, FALSE); + + /* Pending callbacks are not executed if this was an list entry */ + if (cmd->status != SILC_STATUS_OK && + cmd->status != SILC_STATUS_LIST_END) { + silc_client_command_reply_free(cmd); + return; + } + + out: + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY); + + err: + /* If we received notify for invalid ID we'll remove the ID if we + have it cached. */ + if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) { + SilcClientEntry client_entry; + SilcUInt32 tmp_len; + unsigned char *tmp = + silc_argument_get_arg_type(silc_command_get_args(cmd->payload), + 2, &tmp_len); + if (tmp) { + SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); + if (client_id) { + client_entry = silc_client_get_client_by_id(cmd->client, conn, + client_id); + if (client_entry) + silc_client_del_client(cmd->client, conn, client_entry); + silc_free(client_id); + } + } + } + + /* Unregister this command reply */ + silc_client_command_unregister(cmd->client, SILC_COMMAND_IDENTIFY, + NULL, silc_client_command_reply_identify_i, + cmd->ident); + + silc_client_command_reply_free(cmd); +} + +SILC_CLIENT_CMD_REPLY_FUNC(info_i) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + unsigned char *tmp; + SilcServerEntry server; + SilcServerID *server_id = NULL; + char *server_name, *server_info; + SilcUInt32 len; + + COMMAND_CHECK_STATUS_I; + + /* Get server ID */ + tmp = silc_argument_get_arg_type(cmd->args, 2, &len); + if (!tmp) + goto out; + + server_id = silc_id_payload_parse_id(tmp, len, NULL); + if (!server_id) + goto out; + + /* Get server name */ + server_name = silc_argument_get_arg_type(cmd->args, 3, NULL); + if (!server_name) + goto out; + + /* Get server info */ + server_info = silc_argument_get_arg_type(cmd->args, 4, NULL); + if (!server_info) + goto out; + + /* See whether we have this server cached. If not create it. */ + server = silc_client_get_server_by_id(cmd->client, conn, server_id); + if (!server) { + SILC_LOG_DEBUG(("New server entry")); + silc_client_add_server(cmd->client, conn, server_name, server_info, + silc_id_dup(server_id, SILC_ID_SERVER)); + } + + out: + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO); + silc_free(server_id); + err: + silc_client_command_reply_free(cmd); +} + +static void silc_client_command_reply_users_i_cb(SilcClient client, + SilcClientConnection conn, + SilcChannelEntry *channels, + SilcUInt32 channels_count, + void *context) +{ + if (!channels_count) { + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + + cmd->status = cmd->error = SILC_STATUS_ERR_NO_SUCH_CHANNEL; + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS); + silc_client_command_reply_free(cmd); + return; + } + + silc_client_command_reply_users_i(context, NULL); +} + +SILC_CLIENT_CMD_REPLY_FUNC(users_i) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + + COMMAND_CHECK_STATUS_I; + + /* Save USERS info */ + if (silc_client_command_reply_users_save( + cmd, cmd->status, FALSE, TRUE, + silc_client_command_reply_users_i_cb, + silc_client_command_reply_users_i)) + return; + + out: + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS); + + err: + /* Unregister this command reply */ + silc_client_command_unregister(cmd->client, SILC_COMMAND_USERS, + NULL, silc_client_command_reply_users_i, + cmd->ident); + + silc_client_command_reply_free(cmd); +} + +/* Private range commands, specific to this implementation (and compatible + with SILC Server >= 0.9). */ + +SILC_CLIENT_CMD_REPLY_FUNC(connect) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); + goto out; + } + + /* Notify application */ + COMMAND_REPLY((SILC_ARGS)); + + out: + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT); + silc_client_command_reply_free(cmd); +} + +SILC_CLIENT_CMD_REPLY_FUNC(close) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); + goto out; + } + + /* Notify application */ + COMMAND_REPLY((SILC_ARGS)); + + out: + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE); + silc_client_command_reply_free(cmd); +} + +SILC_CLIENT_CMD_REPLY_FUNC(shutdown) +{ + SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; + SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + + if (cmd->error != SILC_STATUS_OK) { + SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, + "%s", silc_get_status_message(cmd->error)); + COMMAND_REPLY_ERROR(cmd->error); + goto out; + } + + /* Notify application */ + COMMAND_REPLY((SILC_ARGS)); + + out: + SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN); + silc_client_command_reply_free(cmd); +}