+/* Command thread destructor */
+
+static void silc_client_command_destructor(SilcFSMThread thread,
+ void *fsm_context,
+ void *destructor_context)
+{
+ SilcClientCommandContext cmd = fsm_context;
+ SilcClientConnection conn = cmd->conn;
+
+ /* Removes commands that aren't waiting for reply but are waiting
+ for something. They may not have been removed yet. */
+ silc_list_del(conn->internal->pending_commands, cmd);
+
+ silc_client_command_free(cmd);
+}
+
+/* Add a command pending a command reply. Used internally by the library. */
+
+static SilcBool
+silc_client_command_add_pending(SilcClientConnection conn,
+ SilcClientCommandContext cmd,
+ SilcClientCommandReply reply,
+ void *context)
+{
+ SilcClientCommandReplyCallback cb;
+
+ silc_mutex_lock(conn->internal->lock);
+
+ /* Add pending callback, if defined */
+ if (reply) {
+ cb = silc_calloc(1, sizeof(*cb));
+ if (!cb) {
+ silc_mutex_unlock(conn->internal->lock);
+ return FALSE;
+ }
+ cb->reply = reply;
+ cb->context = context;
+ silc_list_add(cmd->reply_callbacks, cb);
+ }
+
+ /* Add pending reply */
+ silc_list_add(conn->internal->pending_commands, cmd);
+
+ silc_mutex_unlock(conn->internal->lock);
+
+ return TRUE;
+}
+
+/* Generic function to send any command. The arguments must be sent already
+ encoded into correct format and in correct order. Arguments come from
+ variable argument list pointer. */
+
+static SilcUInt16 silc_client_command_send_vap(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientCommandContext cmd,
+ SilcCommand command,
+ SilcClientCommandReply reply,
+ void *reply_context,
+ SilcUInt32 argc, va_list ap)
+{
+ SilcBuffer packet;
+
+ SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
+
+ if (conn->internal->disconnected)
+ return 0;
+
+ if (!cmd->cmd_ident)
+ cmd->cmd_ident = silc_client_cmd_ident(conn);
+
+ /* Encode command payload */
+ packet = silc_command_payload_encode_vap(command, cmd->cmd_ident, argc, ap);
+ if (!packet)
+ return 0;
+
+ /* Send the command */
+ if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
+ silc_buffer_datalen(packet))) {
+ silc_buffer_free(packet);
+ return 0;
+ }
+
+ /* Add the command pending command reply */
+ silc_client_command_add_pending(conn, cmd, reply, reply_context);
+
+ silc_buffer_free(packet);
+
+ return cmd->cmd_ident;
+}
+
+/* Generic function to send any command. The arguments must be sent already
+ encoded into correct format and in correct order. Arguments come from
+ arrays. */
+
+static SilcUInt16
+silc_client_command_send_arg_array(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientCommandContext cmd,
+ SilcCommand command,
+ SilcClientCommandReply reply,
+ void *reply_context,
+ SilcUInt32 argc,
+ unsigned char **argv,
+ SilcUInt32 *argv_lens,
+ SilcUInt32 *argv_types)
+{
+ SilcBuffer packet;
+
+ SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
+
+ if (conn->internal->disconnected)
+ return 0;
+
+ if (!cmd->cmd_ident)
+ cmd->cmd_ident = silc_client_cmd_ident(conn);
+
+ /* Encode command payload */
+ packet = silc_command_payload_encode(command, argc, argv, argv_lens,
+ argv_types, cmd->cmd_ident);
+ if (!packet)
+ return 0;
+
+ /* Send the command */
+ if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
+ silc_buffer_datalen(packet))) {
+ silc_buffer_free(packet);
+ return 0;
+ }
+
+ /* Add the command pending command reply */
+ silc_client_command_add_pending(conn, cmd, reply, reply_context);
+
+ silc_buffer_free(packet);
+
+ return cmd->cmd_ident;
+}
+
+/* Generic function to send any command. The arguments must be sent already
+ encoded into correct format and in correct order. This is used internally
+ by the library. */
+
+static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn,
+ SilcClientCommandContext cmd,
+ SilcCommand command,
+ SilcClientCommandReply reply,
+ void *reply_context,
+ SilcUInt32 argc, ...)
+{
+ va_list ap;
+ SilcUInt16 cmd_ident;
+
+ va_start(ap, argc);
+ cmd_ident = silc_client_command_send_vap(conn->client, conn, cmd, command,
+ reply, reply_context, argc, ap);
+ va_end(ap);
+
+ return cmd_ident;
+}
+
+/****************************** Command API *********************************/
+
+/* Free command context and its internals */
+
+void silc_client_command_free(SilcClientCommandContext cmd)
+{
+ SilcClientCommandReplyCallback cb;
+ int i;
+
+ for (i = 0; i < cmd->argc; i++)
+ silc_free(cmd->argv[i]);
+ silc_free(cmd->argv);
+ silc_free(cmd->argv_lens);
+ silc_free(cmd->argv_types);
+
+ silc_list_start(cmd->reply_callbacks);
+ while ((cb = silc_list_get(cmd->reply_callbacks)))
+ silc_free(cb);
+
+ silc_free(cmd);
+}
+