SILC_FSM_STATE(silc_client_command_continue_error)
{
/* Destructor will free all resources */
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/* Command reply callback to continue with the execution of a command.
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->name = name ? strdup(name) : NULL;
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 NULL;
}
-/* Free command context and its internals */
-
-static void silc_client_command_free(SilcClientCommandContext cmd)
-{
- 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_free(cmd);
-}
-
/* Command thread destructor */
static void silc_client_command_destructor(SilcFSMThread thread,
void *fsm_context,
void *destructor_context)
{
- silc_client_command_free(fsm_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. */
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);
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);
/****************************** 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);
+}
+
/* Executes a command */
SilcUInt16 silc_client_command_call(SilcClient client,
argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
if (!argv || !argv_lens || !argv_types)
- return FALSE;
+ return 0;
argv[argc] = silc_memdup(arg, strlen(arg));
if (!argv[argc])
- return FALSE;
+ return 0;
argv_lens[argc] = strlen(arg);
argv_types[argc] = argc;
argc++;
cmd->cmd_ident = silc_client_cmd_ident(conn);
cmd->called = TRUE;
cmd->verbose = TRUE;
+ silc_list_init(cmd->reply_callbacks,
+ struct SilcClientCommandReplyCallbackStruct, next);
/*** Call command */
SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
return 0;
cmd->conn = conn;
cmd->cmd = command;
+ silc_list_init(cmd->reply_callbacks,
+ struct SilcClientCommandReplyCallbackStruct, next);
/* Send the command */
va_start(ap, argc);
/* Given without arguments fetches client's own information */
if (cmd->argc < 2) {
- silc_client_command_send(conn->client, conn, SILC_COMMAND_WHOIS,
- NULL, NULL, 1, 4,
- silc_buffer_datalen(conn->internal->local_idp));
- goto out;
+ silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
+ silc_buffer_data(conn->internal->local_idp),
+ silc_buffer_len(conn->internal->local_idp));
+
+ /* Notify application */
+ COMMAND(SILC_STATUS_OK);
+
+ /** Wait for command reply */
+ silc_fsm_next(fsm, silc_client_command_reply_wait);
+ SILC_FSM_CONTINUE;
}
for (i = 1; i < cmd->argc; i++) {
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
out:
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/******************************** WHOWAS ************************************/
"Usage: /WHOWAS <nickname>[@<server>] [<count>]");
COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
SILC_STATUS_ERR_TOO_MANY_PARAMS));
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
if (cmd->argc == 2) {
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/******************************** IDENTIFY **********************************/
int c;
if (cmd->argc < 2 || cmd->argc > 3)
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
if (cmd->argc == 2) {
silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/********************************** NICK ************************************/
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
out:
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/********************************** LIST ************************************/
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/********************************** TOPIC ***********************************/
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
out:
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/********************************* INVITE ***********************************/
/* Parse the typed nickname. */
if (cmd->argc == 3) {
if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
- if (client->internal->params->nickname_parse)
- client->internal->params->nickname_parse(cmd->argv[2], &nickname);
- else
- nickname = strdup(cmd->argv[2]);
+ silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname);
/* Find client entry */
clients = silc_client_get_clients_local(client, conn, nickname,
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
out:
silc_free(nickname);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/********************************** QUIT ************************************/
SilcClientConnection conn = cmd->conn;
SilcClient client = conn->client;
+ SILC_LOG_DEBUG(("Quitting"));
+
/* Notify application */
COMMAND(SILC_STATUS_OK);
/* Call connection callback */
- conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED,
- 0, NULL, conn->callback_context);
+ if (!conn->internal->callback_called)
+ conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED,
+ 0, NULL, conn->callback_context);
+ conn->internal->callback_called = TRUE;
/* Signal to close connection */
- conn->internal->disconnected = TRUE;
- SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+ if (!conn->internal->disconnected) {
+ conn->internal->disconnected = TRUE;
+ SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
+ }
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/* Command QUIT. Closes connection with current server. */
/* We close the connection with a little timeout */
silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
- return SILC_FSM_WAIT;
+ SILC_FSM_WAIT;
}
/********************************** KILL ************************************/
SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
"Usage: /KILL <nickname> [<comment>] [-pubkey]");
COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/* Parse the typed nickname. */
- if (client->internal->params->nickname_parse)
- client->internal->params->nickname_parse(cmd->argv[1], &nickname);
- else
- nickname = strdup(cmd->argv[1]);
- if (!nickname)
- return SILC_FSM_FINISH;
+ if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
+ SILC_FSM_FINISH;
/* Get the target client */
clients = silc_client_get_clients_local(client, conn, nickname,
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/********************************** INFO ************************************/
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/********************************** STATS ***********************************/
SilcClientCommandContext cmd = fsm_context;
SilcClientConnection conn = cmd->conn;
- if (cmd->argc < 2) {
- COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- return SILC_FSM_FINISH;
- }
-
/* Send the command */
silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1, silc_buffer_datalen(conn->internal->
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/********************************** PING ************************************/
if (cmd->argc < 2) {
COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/* Send the command */
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/********************************** JOIN ************************************/
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
out:
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/********************************** MOTD ************************************/
"Usage: /MOTD [<server>]");
COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
SILC_STATUS_ERR_TOO_MANY_PARAMS));
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/* Send the command */
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/********************************** UMODE ***********************************/
SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
"Usage: /UMODE +|-<modes>");
COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
mode = conn->local_entry->mode;
break;
default:
COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
break;
}
}
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/********************************** CMODE ***********************************/
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
out:
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/********************************* CUMODE ***********************************/
}
/* Parse the typed nickname. */
- if (client->internal->params->nickname_parse)
- client->internal->params->nickname_parse(cmd->argv[3], &nickname);
- else
- nickname = strdup(cmd->argv[3]);
+ silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname);
/* Find client entry */
clients = silc_client_get_clients_local(client, conn, nickname,
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
out:
silc_client_list_free(client, conn, clients);
silc_free(nickname);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/********************************** KICK ************************************/
}
/* Parse the typed nickname. */
- if (client->internal->params->nickname_parse)
- client->internal->params->nickname_parse(cmd->argv[2], &nickname);
- else
- nickname = strdup(cmd->argv[2]);
+ silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname);
/* Get the target client */
clients = silc_client_get_clients_local(client, conn, nickname,
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
out:
silc_free(nickname);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/***************************** OPER & SILCOPER ******************************/
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/* OPER command. Used to obtain server operator privileges. */
SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
"Usage: /OPER <username> [-pubkey]");
COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/* Get passphrase */
if (cmd->argc < 3) {
oper = silc_calloc(1, sizeof(*oper));
if (!oper)
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
cmd->context = oper;
SILC_FSM_CALL(conn->client->internal->
ops->ask_passphrase(conn->client, conn,
}
silc_fsm_next(fsm, silc_client_command_oper_send);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/* SILCOPER command. Used to obtain router operator privileges. */
SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
"Usage: /SILCOPER <username> [-pubkey]");
COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/* Get passphrase */
if (cmd->argc < 3) {
oper = silc_calloc(1, sizeof(*oper));
if (!oper)
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
cmd->context = oper;
SILC_FSM_CALL(conn->client->internal->
ops->ask_passphrase(conn->client, conn,
}
silc_fsm_next(fsm, silc_client_command_oper_send);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/*********************************** BAN ************************************/
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
out:
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/********************************* DETACH ***********************************/
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/********************************** WATCH ***********************************/
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
out:
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/********************************** LEAVE ***********************************/
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
out:
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/********************************** USERS ***********************************/
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
out:
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/********************************* GETKEY ***********************************/
client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
"Usage: /GETKEY <nickname or server name>");
COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/* Parse the typed nickname. */
- if (client->internal->params->nickname_parse)
- client->internal->params->nickname_parse(cmd->argv[1], &nickname);
- else
- nickname = strdup(cmd->argv[1]);
- if (!nickname) {
+ if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname)) {
COMMAND_ERROR(SILC_STATUS_ERR_RESOURCE_LIMIT);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/* Find client entry */
/* Check whether user requested server */
server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
if (!server_entry) {
+ if (cmd->resolved) {
+ /* Resolving didn't find anything. We should never get here as
+ errors are handled in the resolving callback. */
+ COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
+ COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
+ SILC_FSM_FINISH;
+ }
+
/* No client or server exist with this name, query for both. */
+ cmd->resolved = TRUE;
SILC_FSM_CALL(silc_client_command_send(client, conn,
SILC_COMMAND_IDENTIFY,
silc_client_command_continue,
strlen(cmd->argv[1]),
2, cmd->argv[1],
strlen(cmd->argv[1])));
+ /* NOT REACHED */
}
idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
+ silc_client_unref_server(client, conn, server_entry);
} else {
client_entry = silc_dlist_get(clients);
idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/********************************* SERVICE **********************************/
SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
"Usage: /SERVICE [<service name>] [-pubkey]");
COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
name = cmd->argv[1];
/** Wait for command reply */
silc_fsm_next(fsm, silc_client_command_reply_wait);
- return SILC_FSM_CONTINUE;
+ SILC_FSM_CONTINUE;
}
/* Register all default commands provided by the client library for the
silc_buffer_len(&packet->buffer));
if (!payload) {
SILC_LOG_DEBUG(("Bad command packet"));
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}
/* Get arguments */
case SILC_COMMAND_WHOIS:
/* Ignore everything if requested by application */
- if (client->internal->params->ignore_requested_attributes)
+ if (conn->internal->params.ignore_requested_attributes)
break;
silc_client_command_process_whois(client, conn, payload, args);
}
silc_command_payload_free(payload);
- return SILC_FSM_FINISH;
+ SILC_FSM_FINISH;
}