+ return cmd_ident;
+}
+
+/* State to finish command thread after an error in resolving command */
+
+SILC_FSM_STATE(silc_client_command_continue_error)
+{
+ /* Destructor will free all resources */
+ return SILC_FSM_FINISH;
+}
+
+/* Command reply callback to continue with the execution of a command.
+ This will continue when first successful reply is received, and ignores
+ the rest. On the other hand, if only errors are received it will
+ wait for all errors before continuing. */
+
+static SilcBool silc_client_command_continue(SilcClient client,
+ SilcClientConnection conn,
+ SilcCommand command,
+ SilcStatus status,
+ SilcStatus error,
+ void *context,
+ va_list ap)
+{
+ SilcClientCommandContext cmd = context;
+
+ /* Continue immediately when successful reply is received */
+ if (status == SILC_STATUS_OK || !SILC_STATUS_IS_ERROR(error)) {
+ SILC_FSM_CALL_CONTINUE(&cmd->thread);
+ return FALSE;
+ }
+
+ /* Error */
+ COMMAND_ERROR(error);
+
+ /* Continue after last error is received */
+ if (SILC_STATUS_IS_ERROR(status) ||
+ (status == SILC_STATUS_LIST_END && SILC_STATUS_IS_ERROR(error))) {
+ silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
+ SILC_FSM_CALL_CONTINUE(&cmd->thread);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Continues after resolving completed. */
+
+static void silc_client_command_resolve_continue(SilcClient client,
+ SilcClientConnection conn,
+ SilcStatus status,
+ SilcDList clients,
+ void *context)
+{
+ SilcClientCommandContext cmd = context;
+
+ if (status != SILC_STATUS_OK)
+ silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
+
+ /* Continue with the command */
+ SILC_FSM_CALL_CONTINUE(&cmd->thread);
+}
+
+/* Dummy command callback. Nothing interesting to do here. Use this when
+ you just send command but don't care about reply. */
+
+SilcBool silc_client_command_called_dummy(SilcClient client,
+ SilcClientConnection conn,
+ SilcCommand command,
+ SilcStatus status,
+ SilcStatus error,
+ void *context,
+ va_list ap)
+{
+ return FALSE;
+}
+
+/* Dummy resolving callback. Nothing interesting to do here. Use this
+ when you just resolve entires but don't care about reply. */
+
+void silc_client_command_resolve_dummy(SilcClient client,
+ SilcClientConnection conn,
+ SilcStatus status,
+ SilcDList clients,
+ void *context)
+{
+ /* Nothing */
+}
+
+/* Register command to client */
+
+static SilcBool
+silc_client_command_register(SilcClient client,
+ SilcCommand command,
+ const char *name,
+ SilcFSMStateCallback command_func,
+ SilcFSMStateCallback command_reply_func,
+ SilcUInt8 max_args)
+{
+ SilcClientCommand cmd;
+
+ cmd = silc_calloc(1, sizeof(*cmd));
+ if (!cmd)
+ return FALSE;
+ cmd->cmd = command;
+ cmd->command = command_func;
+ cmd->reply = command_reply_func;
+ cmd->max_args = max_args;
+ cmd->name = name ? strdup(name) : NULL;
+ if (!cmd->name) {
+ silc_free(cmd);
+ return FALSE;
+ }
+
+ silc_list_add(client->internal->commands, cmd);
+
+ return TRUE;
+}
+
+/* Unregister command from client */
+
+static SilcBool
+silc_client_command_unregister(SilcClient client,
+ SilcCommand command,
+ SilcFSMStateCallback command_func,
+ SilcFSMStateCallback command_reply_func)
+{
+ SilcClientCommand cmd;
+
+ silc_list_start(client->internal->commands);
+ while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
+ if (cmd->cmd == command && cmd->command == command_func &&
+ cmd->reply == command_reply_func) {
+ silc_list_del(client->internal->commands, cmd);
+ silc_free(cmd->name);
+ silc_free(cmd);
+ return TRUE;
+ }
+ }
+
+ return FALSE;