+Fri Apr 12 20:09:08 EEST 2002 Pekka Riikonen <priikone@silcnet.org>
+
+ Added resolve_cmd_ident field to the SilcClientEntry structure
+ too so that if the entry is for example being resolved so
+ another command may attach to the same pending command reply
+ without requiring to resolve the same entry again. Added
+ support for adding multiple pending commands for one
+ command idenfier. Affected files lib/silcclient/command.[ch],
+ lib/silcclient/command_reply.[ch], lib/silcclient/idlist.h.
+
Fri Apr 12 10:17:51 EEST 2002 Pekka Riikonen <priikone@silcnet.org>
* Defined that server receives WHOIS command reply for private
{
SILC_SERVER_REC *server = conn->context;
- if (!server || status == SILC_CLIENT_CONN_ERROR) {
+ if (!server) {
silc_client_close_connection(client, conn);
return;
}
SilcClientEntry founder = NULL;
NICK_REC *ownnick;
+ SILC_LOG_DEBUG(("Start, channel %s, %d users", channel->channel_name,
+ silc_hash_table_count(channel->user_list)));
+
if (!clients)
return;
#include "window-item-def.h"
#include "fe-common/core/printtext.h"
+#include "fe-common/core/fe-channels.h"
+#include "fe-common/core/keyboard.h"
#include "fe-common/silc/module-formats.h"
#include "silc-commands.h"
server);
server->conn = conn;
+ if (params.detach_data)
+ keyboard_entry_redirect(NULL,
+ "-- Resuming old session, may take a while ...",
+ ENTRY_REDIRECT_FLAG_HIDDEN, server);
+
silc_free(params.detach_data);
unlink(file);
/* As router we must deliver this packet directly to the original
server whom this client was earlier. */
- if (server->server_type == SILC_ROUTER && detached_client->router)
+ if (server->server_type == SILC_ROUTER && detached_client->router &&
+ detached_client->router->server_type != SILC_ROUTER)
silc_server_packet_send(server, detached_client->router->connection,
SILC_PACKET_RESUME_CLIENT, 0,
buf->data, buf->len, TRUE);
}
}
+ if (server->server_type == SILC_ROUTER &&
+ sock->type == SILC_SOCKET_TYPE_ROUTER &&
+ server_entry->server_type == SILC_ROUTER)
+ local = FALSE;
+
SILC_LOG_DEBUG(("Resuming detached client"));
/* Change the client to correct list. */
client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
goto out;
}
- client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
silc_client_notify_by_server_resolve(client, conn, packet,
SILC_ID_CLIENT, client_id);
+ client_entry->status |= SILC_CLIENT_STATUS_RESOLVING;
+ client_entry->resolve_cmd_ident = conn->cmd_ident;
goto out;
} else {
if (client_entry != conn->local_entry)
goto out;
}
remote_client->status |= SILC_CLIENT_STATUS_RESOLVING;
+ remote_client->resolve_cmd_ident = conn->cmd_ident + 1;
}
/* Resolve the client info */
SilcClientResumeSessionCallback callback;
void *context;
SilcUInt32 channel_count;
+ SilcUInt32 *cmd_idents;
+ SilcUInt32 cmd_idents_count;
bool success;
} *SilcClientResumeSession;
-/* Generic command reply callback */
+/* Generic command reply callback. */
SILC_CLIENT_CMD_REPLY_FUNC(resume)
{
SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
-
SILC_LOG_DEBUG(("Start"));
+ SILC_CLIENT_PENDING_EXEC(cmd, silc_command_get(cmd->payload));
+}
+
+/* Special command reply callback for IDENTIFY callbacks. This calls
+ the pending callback for every returned command entry. */
+
+SILC_CLIENT_CMD_REPLY_FUNC(resume_special)
+{
+ SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
+ int i;
- if (cmd->callback)
- (*cmd->callback)(cmd->context, cmd);
+ SILC_LOG_DEBUG(("Start"));
+ for (i = 0; i < cmd->callbacks_count; i++)
+ if (cmd->callbacks[i].callback)
+ (*cmd->callbacks[i].callback)(cmd->callbacks[i].context, cmd);
}
/* Completion calling callback */
SILC_TASK_CALLBACK(silc_client_resume_call_completion)
{
SilcClientResumeSession session = context;
+ int i;
+
session->callback(session->client, session->conn, session->success,
session->context);
+
+ for (i = 0; i < session->cmd_idents_count; i++)
+ silc_client_command_pending_del(session->conn, SILC_COMMAND_IDENTIFY,
+ session->cmd_idents[i]);
+ silc_free(session->cmd_idents);
+
+ memset(session, 'F', sizeof(*session));
+ silc_free(session);
}
/* This function is used to perform the resuming procedure after the
/* Send the IDENTIFY command */
SILC_LOG_DEBUG(("Sending IDENTIFY"));
silc_client_command_register(client, SILC_COMMAND_IDENTIFY, NULL, NULL,
- silc_client_command_reply_resume,
+ silc_client_command_reply_resume_special,
0, ++conn->cmd_ident);
- tmp = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
- res_argc, res_argv, res_argv_lens,
- res_argv_types, conn->cmd_ident);
silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
conn->cmd_ident,
silc_client_command_resume_identify,
session);
+
+ tmp = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
+ res_argc, res_argv, res_argv_lens,
+ res_argv_types, conn->cmd_ident);
silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
NULL, 0, NULL, NULL, tmp->data, tmp->len, TRUE);
+ session->cmd_idents = silc_realloc(session->cmd_idents,
+ sizeof(*session->cmd_idents) *
+ (session->cmd_idents_count + 1));
+ session->cmd_idents[session->cmd_idents_count] = conn->cmd_ident;
+ session->cmd_idents_count++;
+
for (i = 0; i < res_argc; i++)
silc_free(res_argv[i]);
silc_free(res_argv);
SILC_LOG_DEBUG(("Sending TOPIC"));
tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
- conn->cmd_ident, 1, 1, tmp, tmp_len);
+ ++conn->cmd_ident, 1, 1, tmp, tmp_len);
/* Call the completion callback after we've got reply to all of
our channels */
}
/* Add new pending command to be executed when reply to a command has been
- received. The `reply_cmd' is the command that will call the `callback'
- with `context' when reply has been received. If `ident is non-zero
- the `callback' will be executed when received reply with command
- identifier `ident'. */
+ received. The `reply_cmd' is the command that will call the `callback'
+ with `context' when reply has been received. It can be SILC_COMMAND_NONE
+ to match any command with the `ident'. If `ident' is non-zero
+ the `callback' will be executed when received reply with command
+ identifier `ident'. If there already exists pending command for the
+ specified command, ident, callback and context this function has no
+ effect. */
void silc_client_command_pending(SilcClientConnection conn,
SilcCommand reply_cmd,
{
SilcClientCommandPending *reply;
+ /* Check whether identical pending already exists for same command,
+ ident, callback and callback context. If it does then it would be
+ error to register it again. */
+ silc_dlist_start(conn->pending_commands);
+ while ((reply = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
+ if (reply->reply_cmd == reply_cmd && reply->ident == ident &&
+ reply->callback == callback && reply->context == context)
+ return;
+ }
+
reply = silc_calloc(1, sizeof(*reply));
reply->reply_cmd = reply_cmd;
reply->ident = ident;
}
/* Checks for pending commands and marks callbacks to be called from
- the command reply function. Returns TRUE if there were pending command. */
-
-int silc_client_command_pending_check(SilcClientConnection conn,
- SilcClientCommandReplyContext ctx,
- SilcCommand command,
- SilcUInt16 ident)
+ the command reply function. */
+
+SilcClientCommandPendingCallbacks
+silc_client_command_pending_check(SilcClientConnection conn,
+ SilcClientCommandReplyContext ctx,
+ SilcCommand command,
+ SilcUInt16 ident,
+ SilcUInt32 *callbacks_count)
{
SilcClientCommandPending *r;
+ SilcClientCommandPendingCallbacks callbacks = NULL;
+ int i = 0;
silc_dlist_start(conn->pending_commands);
while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
- if (r->reply_cmd == command && r->ident == ident) {
- ctx->context = r->context;
- ctx->callback = r->callback;
+ if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE)
+ && r->ident == ident) {
+ callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
+ callbacks[i].context = r->context;
+ callbacks[i].callback = r->callback;
ctx->ident = ident;
- return TRUE;
+ i++;
}
}
- return FALSE;
+ *callbacks_count = i;
+ return callbacks;
}
/* Allocate Command Context */
void silc_client_command_##func(void *context, void *context2)
/* Executed pending command callback */
-#define SILC_CLIENT_PENDING_EXEC(ctx, cmd) \
-do { \
- if ((ctx)->callback) \
- (*ctx->callback)(ctx->context, ctx); \
- silc_client_command_pending_del((ctx)->sock->user_data, (cmd), \
- (ctx)->ident); \
+#define SILC_CLIENT_PENDING_EXEC(ctx, cmd) \
+do { \
+ int _i; \
+ for (_i = 0; _i < ctx->callbacks_count; _i++) \
+ if (ctx->callbacks[_i].callback) \
+ (*ctx->callbacks[_i].callback)(ctx->callbacks[_i].context, ctx); \
+ silc_client_command_pending_del(ctx->sock->user_data, cmd, ctx->ident); \
} while(0)
bool silc_client_command_register(SilcClient client,
void silc_client_command_pending_del(SilcClientConnection conn,
SilcCommand reply_cmd,
SilcUInt16 ident);
-int silc_client_command_pending_check(SilcClientConnection conn,
- SilcClientCommandReplyContext ctx,
- SilcCommand command,
- SilcUInt16 ident);
-
+SilcClientCommandPendingCallbacks
+silc_client_command_pending_check(SilcClientConnection conn,
+ SilcClientCommandReplyContext ctx,
+ SilcCommand command,
+ SilcUInt16 ident,
+ SilcUInt32 *callbacks_count);
SILC_CLIENT_CMD_FUNC(whois);
SILC_CLIENT_CMD_FUNC(whowas);
SILC_CLIENT_CMD_FUNC(identify);
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),
- ctx->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 */
client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
/* Notify application */
- if (!cmd->callback && notify)
+ if (!cmd->callbacks_count && notify)
COMMAND_REPLY((ARGS, client_entry, nickname, username, realname,
has_channels ? &channels : NULL, mode, idle,
fingerprint, has_user_modes ? &ch_user_modes : NULL));
int i;
unsigned char **res_argv = NULL;
SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
+ bool wait_res = FALSE;
SILC_LOG_DEBUG(("Start"));
return 1;
}
+ 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++) {
SilcUInt16 idp_len;
if (!client_entry || !client_entry->username || !client_entry->realname) {
if (client_entry) {
if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) {
+ /* Attach to this resolving and wait until it finishes */
+ silc_client_command_pending(conn, SILC_COMMAND_NONE,
+ client_entry->resolve_cmd_ident,
+ get_clients, cmd);
+ wait_res = TRUE;
+
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->resolve_cmd_ident = conn->cmd_ident + 1;
}
/* No we don't have it (or it is incomplete in information), query
silc_free(res_argv_types);
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 -
SilcCommand cmd;
} SilcClientCommandReply;
+/* Context holding pending command callbacks. */
+typedef struct {
+ SilcCommandCb callback;
+ void *context;
+} *SilcClientCommandPendingCallbacks;
+
/* Context sent as argument to all command reply functions */
struct SilcClientCommandReplyContextStruct {
SilcClient client;
SilcPacketContext *packet;
/* If defined this executes the pending command. */
- SilcCommandCb callback;
- void *context;
+ SilcClientCommandPendingCallbacks callbacks;
+ SilcUInt32 callbacks_count;
SilcUInt16 ident;
};
unsigned char **res_argv = NULL;
SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
GetClientsByListInternal in;
+ bool wait_res = FALSE;
SILC_LOG_DEBUG(("Start"));
if (entry) {
if (entry->status & SILC_CLIENT_STATUS_RESOLVING) {
- entry->status &= ~SILC_CLIENT_STATUS_RESOLVING;
+ /* Attach to this resolving and wait until it finishes */
+ silc_client_command_pending(
+ conn, SILC_COMMAND_NONE,
+ entry->resolve_cmd_ident,
+ silc_client_command_get_clients_list_callback,
+ (void *)in);
+ wait_res = TRUE;
+
silc_free(client_id);
silc_buffer_pull(client_id_list, idp_len);
continue;
}
entry->status |= SILC_CLIENT_STATUS_RESOLVING;
+ entry->resolve_cmd_ident = conn->cmd_ident + 1;
}
/* No we don't have it, query it from the server. Assemble argument
silc_buffer_pull(client_id_list, idp_len);
}
+ silc_buffer_push(client_id_list, client_id_list->data -
+ client_id_list->head);
+
/* Query the client information from server if the list included clients
that we don't know about. */
if (res_argc) {
silc_client_command_get_clients_list_callback,
(void *)in);
- silc_buffer_push(client_id_list, client_id_list->data -
- client_id_list->head);
silc_buffer_free(res_cmd);
silc_free(res_argv);
silc_free(res_argv_lens);
return;
}
- silc_buffer_push(client_id_list, client_id_list->data -
- client_id_list->head);
+ if (wait_res)
+ return;
/* We have the clients in cache, get them and call the completion */
silc_client_command_get_clients_list_callback((void *)in, NULL);
SilcClientID *id; /* The Client ID */
unsigned char *fingerprint; /* Fingerprint of client's public key */
SilcUInt32 fingerprint_len; /* Length of the fingerprint */
- bool valid; /* FALSE if this entry is not valid */
SilcCipher send_key; /* Private message key for sending */
SilcCipher receive_key; /* Private message key for receiving */
- unsigned char *key; /* Set only if appliation provided the
- key material. NULL if the library
- generated the key. */
- SilcUInt32 key_len;
- bool generated; /* TRUE if library generated the key */
SilcClientKeyAgreement ke; /* Current key agreement context or NULL */
SilcClientStatus status; /* Status mask */
SilcHashTable channels; /* All channels client has joined */
+ unsigned char *key; /* Set only if appliation provided the
+ key material. NULL if the library
+ generated the key. */
+ SilcUInt32 key_len; /* Key length */
+ SilcUInt16 resolve_cmd_ident; /* Command identifier when resolving */
+ bool generated; /* TRUE if library generated `key' */
+ bool valid; /* FALSE if this entry is not valid */
};
/* Client and its mode on a channel */