X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcclient%2Fcommand_reply.c;h=ede887535fdcdc97b6219d0c8d73f11b45867021;hp=415f6d49b2568d061250ac3eb84c88c1db7a0ddd;hb=HEAD;hpb=57fe1d5d88e8687ac876aa3725028bd3343f5067 diff --git a/lib/silcclient/command_reply.c b/lib/silcclient/command_reply.c index 415f6d49..ede88753 100644 --- a/lib/silcclient/command_reply.c +++ b/lib/silcclient/command_reply.c @@ -1,10 +1,10 @@ /* - command_reply.c + command_reply.c Author: Pekka Riikonen - Copyright (C) 1997 - 2002 Pekka Riikonen + Copyright (C) 1997 - 2007 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 @@ -17,1092 +17,1359 @@ */ /* $Id$ */ -/* - * Command reply functions are "the otherside" of the command functions. - * Reply to a command sent by server is handled by these functions. - * - * The arguments received from server are also passed to the calling - * 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 - * 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. - */ - -#include "silcincludes.h" + +#include "silc.h" #include "silcclient.h" #include "client_internal.h" -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), "There was 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 } -}; - -#define SAY cmd->client->internal->ops->say - -/* All functions that call the COMMAND_CHECK_STATUS macro must have - out: goto label. */ - -#define COMMAND_CHECK_STATUS \ +/************************** Types and definitions ***************************/ + +/* Calls error command reply callback back to command sender. */ +#define ERROR_CALLBACK(err) \ do { \ - SILC_LOG_DEBUG(("Start")); \ - if (!silc_command_get_status(cmd->payload, NULL, NULL)) { \ - COMMAND_REPLY_ERROR; \ - goto out; \ - } \ + void *arg1 = NULL, *arg2 = NULL; \ + if (cmd->status != SILC_STATUS_OK) \ + silc_status_get_args(cmd->status, args, &arg1, &arg2); \ + else \ + cmd->status = cmd->error = err; \ + SILC_LOG_DEBUG(("Error in command reply: %s", \ + silc_get_status_message(cmd->status))); \ + silc_client_command_callback(cmd, arg1, arg2); \ } while(0) -/* Process received command reply. */ +/* Check for error */ +#define CHECK_STATUS(msg) \ + SILC_LOG_DEBUG(("%s", silc_get_command_name(cmd->cmd))); \ + if (cmd->error != SILC_STATUS_OK) { \ + if (cmd->verbose) \ + SAY(cmd->conn->client, cmd->conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR, \ + msg "%s", silc_get_status_message(cmd->error)); \ + ERROR_CALLBACK(cmd->error); \ + silc_client_command_process_error(cmd, state_context, cmd->error); \ + silc_fsm_next(fsm, silc_client_command_reply_processed); \ + return SILC_FSM_CONTINUE; \ + } + +/* Check for correct arguments */ +#define CHECK_ARGS(min, max) \ + if (silc_argument_get_arg_num(args) < min || \ + silc_argument_get_arg_num(args) > max) { \ + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); \ + silc_fsm_next(fsm, silc_client_command_reply_processed); \ + return SILC_FSM_CONTINUE; \ + } + +#define SAY cmd->conn->client->internal->ops->say + +/************************ Static utility functions **************************/ + +/* Delivers the command reply back to application */ + +static inline void +silc_client_command_callback(SilcClientCommandContext cmd, ...) +{ + SilcClientCommandReplyCallback cb; + SilcList list; + va_list ap, cp; + + va_start(ap, cmd); + + /* Default reply callback */ + if (cmd->called) { + silc_va_copy(cp, ap); + cmd->conn->client->internal->ops->command_reply( + cmd->conn->client, cmd->conn, cmd->cmd, cmd->status, + cmd->error, cp); + va_end(cp); + } + + /* Reply callback */ + list = cmd->reply_callbacks; + silc_list_start(list); + while ((cb = silc_list_get(list))) + if (!cb->do_not_call) { + silc_va_copy(cp, ap); + cb->do_not_call = !cb->reply(cmd->conn->client, cmd->conn, cmd->cmd, + cmd->status, cmd->error, cb->context, cp); + va_end(cp); + } + + va_end(ap); +} + +/* Handles common error status types. */ + +static void silc_client_command_process_error(SilcClientCommandContext cmd, + SilcCommandPayload payload, + SilcStatus error) +{ + SilcClient client = cmd->conn->client; + SilcClientConnection conn = cmd->conn; + SilcArgumentPayload args = silc_command_get_args(payload); + SilcID id; + + if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) { + SilcClientEntry client_entry; + + /* Remove unknown client entry from cache */ + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) + return; + + client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); + if (client_entry) { + silc_client_remove_from_channels(client, conn, client_entry); + client_entry->internal.valid = FALSE; + silc_client_del_client(client, conn, client_entry); + silc_client_unref_client(client, conn, client_entry); + } + return; + } + + if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID) { + SilcChannelEntry channel; + + /* Remove unknown channel entry from cache */ + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) + return; + + channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id); + if (channel) { + silc_client_empty_channel(client, conn, channel); + silc_client_del_channel(client, conn, channel); + silc_client_unref_channel(client, conn, channel); + } + return; + } + + if (cmd->error == SILC_STATUS_ERR_NO_SUCH_SERVER_ID) { + SilcServerEntry server_entry; -void silc_client_command_reply_process(SilcClient client, - SilcSocketConnection sock, - SilcPacketContext *packet) + /* Remove unknown server entry from cache */ + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) + return; + + server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id); + if (server_entry) { + silc_client_del_server(client, conn, server_entry); + silc_client_unref_server(client, conn, server_entry); + } + return; + } +} + +/***************************** Command Reply ********************************/ + +/* Process received command reply packet */ + +SILC_FSM_STATE(silc_client_command_reply) { - SilcBuffer buffer = packet->buffer; - SilcClientCommand cmd; - SilcClientCommandReplyContext ctx; + SilcClientConnection conn = fsm_context; + SilcPacket packet = state_context; + SilcClientCommandContext cmd; SilcCommandPayload payload; SilcCommand command; - SilcCommandCb reply = NULL; - + SilcUInt16 cmd_ident; + /* Get command reply payload from packet */ - payload = silc_command_payload_parse(buffer->data, buffer->len); + payload = silc_command_payload_parse(silc_buffer_datalen(&packet->buffer)); + silc_packet_free(packet); if (!payload) { - /* Silently ignore bad reply packet */ SILC_LOG_DEBUG(("Bad command reply packet")); - return; + return SILC_FSM_FINISH; } - - /* Allocate command reply context. This must be free'd by the - command reply routine receiving it. */ - ctx = silc_calloc(1, sizeof(*ctx)); - ctx->client = client; - ctx->sock = sock; - ctx->payload = payload; - ctx->args = silc_command_get_args(ctx->payload); - ctx->packet = packet; - 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), - ctx->ident); - - /* Execute command reply */ - - command = silc_command_get(ctx->payload); - - /* 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); + + cmd_ident = silc_command_get_ident(payload); + command = silc_command_get(payload); + + /* Find the command pending reply */ + silc_mutex_lock(conn->internal->lock); + silc_list_start(conn->internal->pending_commands); + while ((cmd = silc_list_get(conn->internal->pending_commands))) { + if ((cmd->cmd == command || cmd->cmd == SILC_COMMAND_NONE) + && cmd->cmd_ident == cmd_ident) { + silc_list_del(conn->internal->pending_commands, cmd); break; } } + silc_mutex_unlock(conn->internal->lock); - if (cmd == SILC_LIST_END) { - if (reply) - /* No specific identifier for command reply, call first one found */ - (*reply)(ctx, NULL); - else - silc_free(ctx); + if (!cmd) { + SILC_LOG_DEBUG(("Unknown command reply %s, ident %d", + silc_get_command_name(command), cmd_ident)); + silc_command_payload_free(payload); + return SILC_FSM_FINISH; } + + /* Signal command thread that command reply has arrived. We continue + command reply processing synchronously because we save the command + payload into state context. No other reply may arrive to this command + while we're processing this reply. */ + silc_fsm_set_state_context(&cmd->thread, payload); + silc_fsm_next(&cmd->thread, silc_client_command_reply_process); + silc_fsm_continue_sync(&cmd->thread); + + return SILC_FSM_FINISH; } -/* Returns status message string */ +/* Wait here for command reply to arrive from remote host */ -char *silc_client_command_status_message(SilcCommandStatus status) +SILC_FSM_STATE(silc_client_command_reply_wait) { - int i; + SilcClientCommandContext cmd = fsm_context; - for (i = 0; silc_command_status_messages[i].message; i++) { - if (silc_command_status_messages[i].status == status) - break; + SILC_LOG_DEBUG(("Wait for command reply")); + + /** Wait for command reply */ + silc_fsm_set_state_context(fsm, NULL); + silc_fsm_next_later(fsm, silc_client_command_reply_timeout, + cmd->cmd != SILC_COMMAND_PING ? 40 : 60, 0); + return SILC_FSM_WAIT; +} + +/* Timeout occurred while waiting command reply */ + +SILC_FSM_STATE(silc_client_command_reply_timeout) +{ + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcArgumentPayload args = NULL; + + if (conn->internal->disconnected) { + SILC_LOG_DEBUG(("Command %s canceled", silc_get_command_name(cmd->cmd))); + silc_list_del(conn->internal->pending_commands, cmd); + if (!cmd->called) + ERROR_CALLBACK(SILC_STATUS_ERR_TIMEDOUT); + return SILC_FSM_FINISH; } - if (silc_command_status_messages[i].message == NULL) - return NULL; + SILC_LOG_DEBUG(("Command %s timeout", silc_get_command_name(cmd->cmd))); - return silc_command_status_messages[i].message; + /* Timeout, reply not received in timely fashion */ + silc_list_del(conn->internal->pending_commands, cmd); + ERROR_CALLBACK(SILC_STATUS_ERR_TIMEDOUT); + return SILC_FSM_FINISH; } -/* Free command reply context and its internals. */ +/* Process received command reply payload */ -void silc_client_command_reply_free(SilcClientCommandReplyContext cmd) +SILC_FSM_STATE(silc_client_command_reply_process) { - if (cmd) { - silc_command_payload_free(cmd->payload); - silc_free(cmd); + SilcClientCommandContext cmd = fsm_context; + SilcCommandPayload payload = state_context; + + silc_command_get_status(payload, &cmd->status, &cmd->error); + + switch (cmd->cmd) { + case SILC_COMMAND_WHOIS: + /** WHOIS */ + silc_fsm_next(fsm, silc_client_command_reply_whois); + break; + case SILC_COMMAND_WHOWAS: + /** WHOWAS */ + silc_fsm_next(fsm, silc_client_command_reply_whowas); + break; + case SILC_COMMAND_IDENTIFY: + /** IDENTIFY */ + silc_fsm_next(fsm, silc_client_command_reply_identify); + break; + case SILC_COMMAND_NICK: + /** NICK */ + silc_fsm_next(fsm, silc_client_command_reply_nick); + break; + case SILC_COMMAND_LIST: + /** LIST */ + silc_fsm_next(fsm, silc_client_command_reply_list); + break; + case SILC_COMMAND_TOPIC: + /** TOPIC */ + silc_fsm_next(fsm, silc_client_command_reply_topic); + break; + case SILC_COMMAND_INVITE: + /** INVITE */ + silc_fsm_next(fsm, silc_client_command_reply_invite); + break; + case SILC_COMMAND_QUIT: + /** QUIT */ + silc_fsm_next(fsm, silc_client_command_reply_quit); + break; + case SILC_COMMAND_KILL: + /** KILL */ + silc_fsm_next(fsm, silc_client_command_reply_kill); + break; + case SILC_COMMAND_INFO: + /** INFO */ + silc_fsm_next(fsm, silc_client_command_reply_info); + break; + case SILC_COMMAND_STATS: + /** STATS */ + silc_fsm_next(fsm, silc_client_command_reply_stats); + break; + case SILC_COMMAND_PING: + /** PING */ + silc_fsm_next(fsm, silc_client_command_reply_ping); + break; + case SILC_COMMAND_OPER: + /** OPER */ + silc_fsm_next(fsm, silc_client_command_reply_oper); + break; + case SILC_COMMAND_JOIN: + /** JOIN */ + silc_fsm_next(fsm, silc_client_command_reply_join); + break; + case SILC_COMMAND_MOTD: + /** MOTD */ + silc_fsm_next(fsm, silc_client_command_reply_motd); + break; + case SILC_COMMAND_UMODE: + /** UMODE */ + silc_fsm_next(fsm, silc_client_command_reply_umode); + break; + case SILC_COMMAND_CMODE: + /** CMODE */ + silc_fsm_next(fsm, silc_client_command_reply_cmode); + break; + case SILC_COMMAND_CUMODE: + /** CUMODE */ + silc_fsm_next(fsm, silc_client_command_reply_cumode); + break; + case SILC_COMMAND_KICK: + /** KICK */ + silc_fsm_next(fsm, silc_client_command_reply_kick); + break; + case SILC_COMMAND_BAN: + /** BAN */ + silc_fsm_next(fsm, silc_client_command_reply_ban); + break; + case SILC_COMMAND_DETACH: + /** DETACH */ + silc_fsm_next(fsm, silc_client_command_reply_detach); + break; + case SILC_COMMAND_WATCH: + /** WATCH */ + silc_fsm_next(fsm, silc_client_command_reply_watch); + break; + case SILC_COMMAND_SILCOPER: + /** SILCOPER */ + silc_fsm_next(fsm, silc_client_command_reply_silcoper); + break; + case SILC_COMMAND_LEAVE: + /** LEAVE */ + silc_fsm_next(fsm, silc_client_command_reply_leave); + break; + case SILC_COMMAND_USERS: + /** USERS */ + silc_fsm_next(fsm, silc_client_command_reply_users); + break; + case SILC_COMMAND_GETKEY: + /** GETKEY */ + silc_fsm_next(fsm, silc_client_command_reply_getkey); + break; + case SILC_COMMAND_SERVICE: + /** SERVICE */ + silc_fsm_next(fsm, silc_client_command_reply_service); + break; + default: + return SILC_FSM_FINISH; } + + return SILC_FSM_CONTINUE; +} + +/* Completes command reply processing */ + +SILC_FSM_STATE(silc_client_command_reply_processed) +{ + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcCommandPayload payload = state_context; + + silc_command_payload_free(payload); + + if (cmd->status == SILC_STATUS_OK || cmd->status == SILC_STATUS_LIST_END || + SILC_STATUS_IS_ERROR(cmd->status)) + return SILC_FSM_FINISH; + + /* Add back to pending command reply list */ + silc_mutex_lock(conn->internal->lock); + cmd->resolved = FALSE; + silc_list_add(conn->internal->pending_commands, cmd); + silc_mutex_unlock(conn->internal->lock); + + /** Wait more command payloads */ + silc_fsm_next(fsm, silc_client_command_reply_wait); + return SILC_FSM_CONTINUE; } -static void -silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd, - SilcCommandStatus status, - bool notify) +/******************************** WHOIS *************************************/ + +/* Received reply for WHOIS command. */ + +SILC_FSM_STATE(silc_client_command_reply_whois) { - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcClientID *client_id; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); SilcClientEntry client_entry = NULL; - SilcUInt32 len; - unsigned char *id_data, *tmp; - char *nickname = NULL, *username = NULL; - char *realname = NULL; - SilcUInt32 idle = 0, mode = 0; + SilcUInt32 idle = 0, mode = 0, fingerprint_len, len, *umodes = NULL; SilcBufferStruct channels, ch_user_modes; - bool has_channels = FALSE, has_user_modes = FALSE; - unsigned char *fingerprint; - SilcUInt32 fingerprint_len; - - id_data = silc_argument_get_arg_type(cmd->args, 2, &len); - if (!id_data) { - if (notify) - COMMAND_REPLY_ERROR; - return; - } - - client_id = silc_id_payload_parse_id(id_data, len, NULL); - if (!client_id) { - if (notify) - COMMAND_REPLY_ERROR; - return; + SilcBool has_channels = FALSE; + SilcDList channel_list = NULL; + SilcID id; + char *nickname = NULL, *username = NULL, *realname = NULL; + unsigned char *fingerprint, *tmp; + + CHECK_STATUS("WHOIS: "); + CHECK_ARGS(5, 11); + + /* Get Client ID */ + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; } - - 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); + + /* Get names */ + nickname = silc_argument_get_arg_type(args, 3, NULL); + username = silc_argument_get_arg_type(args, 4, NULL); + realname = silc_argument_get_arg_type(args, 5, NULL); if (!nickname || !username || !realname) { - if (notify) - COMMAND_REPLY_ERROR; - return; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; } - tmp = silc_argument_get_arg_type(cmd->args, 6, &len); + /* Get joined channel list */ + memset(&channels, 0, sizeof(channels)); + tmp = silc_argument_get_arg_type(args, 6, &len); if (tmp) { - silc_buffer_set(&channels, tmp, len); has_channels = TRUE; + silc_buffer_set(&channels, tmp, len); + + /* Get channel user mode list */ + tmp = silc_argument_get_arg_type(args, 10, &len); + if (!tmp) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + silc_buffer_set(&ch_user_modes, tmp, len); } - tmp = silc_argument_get_arg_type(cmd->args, 7, &len); + /* Get user mode */ + tmp = silc_argument_get_arg_type(args, 7, &len); if (tmp) SILC_GET32_MSB(mode, tmp); - tmp = silc_argument_get_arg_type(cmd->args, 8, &len); + /* Get idle time */ + tmp = silc_argument_get_arg_type(args, 8, &len); if (tmp) SILC_GET32_MSB(idle, tmp); - 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; - } + /* Get fingerprint */ + fingerprint = silc_argument_get_arg_type(args, 9, &fingerprint_len); /* Check if we have this client cached already. */ - client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); + client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); if (!client_entry) { - SILC_LOG_DEBUG(("Adding new client entry")); - client_entry = - silc_client_add_client(cmd->client, conn, nickname, username, realname, - client_id, mode); + SILC_LOG_DEBUG(("Adding new client entry (WHOIS)")); + client_entry = + silc_client_add_client(client, conn, nickname, username, realname, + &id.u.client_id, mode); + if (!client_entry) { + ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT); + goto out; + } + silc_client_ref_client(client, conn, client_entry); } else { - silc_client_update_client(cmd->client, conn, client_entry, + silc_client_update_client(client, conn, client_entry, nickname, username, realname, mode); - silc_free(client_id); - } - - if (fingerprint && !client_entry->fingerprint) { - 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; - - /* Notify application */ - if (!cmd->callback && notify) - COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, - has_channels ? &channels : NULL, mode, idle, - fingerprint, has_user_modes ? &ch_user_modes : NULL)); -} - -/* Received reply for WHOIS command. This maybe called several times - for one WHOIS command as server may reply with list of results. */ + silc_rwlock_wrlock(client_entry->internal.lock); -SILC_CLIENT_CMD_REPLY_FUNC(whois) -{ - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + if (fingerprint && fingerprint_len == sizeof(client_entry->fingerprint)) + memcpy(client_entry->fingerprint, fingerprint, fingerprint_len); - COMMAND_CHECK_STATUS; + /* Get user attributes */ + tmp = silc_argument_get_arg_type(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); + } - /* Save WHOIS info */ - silc_client_command_reply_whois_save(cmd, cmd->status, TRUE); + silc_rwlock_unlock(client_entry->internal.lock); - /* 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; + /* Parse channel and channel user mode list */ + if (has_channels) { + channel_list = silc_channel_payload_parse_list(silc_buffer_data(&channels), + silc_buffer_len(&channels)); + if (channel_list) + silc_get_mode_list(&ch_user_modes, silc_dlist_count(channel_list), + &umodes); } - out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS); + /* Notify application */ + silc_client_command_callback(cmd, client_entry, nickname, username, + realname, channel_list, mode, idle, fingerprint, + umodes, client_entry->attrs); - /* 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_unref_client(client, conn, client_entry); + if (has_channels) { + silc_channel_payload_list_free(channel_list); + silc_free(umodes); } - silc_client_command_reply_free(cmd); + out: + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } +/******************************** WHOWAS ************************************/ + /* Received reply for WHOWAS command. */ -SILC_CLIENT_CMD_REPLY_FUNC(whowas) +SILC_FSM_STATE(silc_client_command_reply_whowas) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcClientID *client_id; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); SilcClientEntry client_entry = NULL; - SilcUInt32 len; - unsigned char *id_data; + SilcID id; char *nickname, *username; char *realname = NULL; - COMMAND_CHECK_STATUS; + CHECK_STATUS("WHOWAS: "); + CHECK_ARGS(4, 5); - id_data = silc_argument_get_arg_type(cmd->args, 2, &len); - if (!id_data) { - COMMAND_REPLY_ERROR; - goto out; - } - - client_id = silc_id_payload_parse_id(id_data, len, NULL); - if (!client_id) { - COMMAND_REPLY_ERROR; + /* Get Client ID */ + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(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); + /* Get the client entry */ + client_entry = silc_client_get_client_by_id(client, conn, &id.u.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); + /* Get names */ + nickname = silc_argument_get_arg_type(args, 3, NULL); + username = silc_argument_get_arg_type(args, 4, NULL); + realname = silc_argument_get_arg_type(args, 5, NULL); if (!nickname || !username) { - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(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 - the screen. */ - COMMAND_REPLY((ARGS, client_entry, nickname, username, realname)); - - /* 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; - } + cache. Just pass the data to the application. */ + silc_client_command_callback(cmd, client_entry, nickname, username, + realname); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS); - silc_client_command_reply_free(cmd); + silc_client_unref_client(client, conn, client_entry); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } -static void -silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd, - SilcCommandStatus status, - bool notify) +/******************************** IDENTIFY **********************************/ + +/* Received reply for IDENTIFY command. */ + +SILC_FSM_STATE(silc_client_command_reply_identify) { - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcClient client = cmd->client; - SilcClientID *client_id = NULL; - SilcServerID *server_id = NULL; - SilcChannelID *channel_id = NULL; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); SilcClientEntry client_entry; SilcServerEntry server_entry; SilcChannelEntry channel_entry; SilcUInt32 len; - unsigned char *id_data; + SilcID id; char *name = NULL, *info = NULL; - SilcIDPayload idp = NULL; - SilcIdType id_type; - - id_data = silc_argument_get_arg_type(cmd->args, 2, &len); - if (!id_data) { - if (notify) - COMMAND_REPLY_ERROR; - return; - } - idp = silc_id_payload_parse(id_data, len); - if (!idp) { - if (notify) - COMMAND_REPLY_ERROR; - return; - } - name = silc_argument_get_arg_type(cmd->args, 3, &len); - info = silc_argument_get_arg_type(cmd->args, 4, &len); + CHECK_STATUS("IDENTIFY: "); + CHECK_ARGS(2, 4); - id_type = silc_id_payload_get_type(idp); + /* Get the ID */ + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } - switch (id_type) { - case SILC_ID_CLIENT: - client_id = silc_id_payload_get_id(idp); + /* Get names */ + name = silc_argument_get_arg_type(args, 3, &len); + info = silc_argument_get_arg_type(args, 4, &len); + switch (id.type) { + case SILC_ID_CLIENT: SILC_LOG_DEBUG(("Received client information")); /* Check if we have this client cached already. */ - client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); + client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); if (!client_entry) { - SILC_LOG_DEBUG(("Adding new client entry")); - client_entry = - silc_client_add_client(cmd->client, conn, name, info, NULL, - silc_id_dup(client_id, id_type), 0); + SILC_LOG_DEBUG(("Adding new client entry (IDENTIFY)")); + client_entry = + silc_client_add_client(client, conn, name, info, NULL, + &id.u.client_id, 0); + if (!client_entry) { + ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT); + goto out; + } + silc_client_ref_client(client, conn, client_entry); } else { - silc_client_update_client(cmd->client, conn, client_entry, + silc_client_update_client(client, conn, client_entry, name, info, NULL, 0); } - if (client_entry->status & SILC_CLIENT_STATUS_RESOLVING) - client_entry->status &= ~SILC_CLIENT_STATUS_RESOLVING; - /* Notify application */ - if (notify) - COMMAND_REPLY((ARGS, client_entry, name, info)); + silc_client_command_callback(cmd, client_entry, name, info); + silc_client_unref_client(client, conn, client_entry); break; case SILC_ID_SERVER: - server_id = silc_id_payload_get_id(idp); - SILC_LOG_DEBUG(("Received server information")); /* Check if we have this server cached already. */ - server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id); + server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id); if (!server_entry) { - SILC_LOG_DEBUG(("Adding new server entry")); - server_entry = silc_client_add_server(cmd->client, conn, name, info, - silc_id_dup(server_id, id_type)); + SILC_LOG_DEBUG(("Adding new server entry (IDENTIFY)")); + server_entry = silc_client_add_server(client, conn, name, info, + &id.u.server_id); if (!server_entry) { - if (notify) - COMMAND_REPLY_ERROR; - return; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; } + silc_client_ref_server(client, conn, server_entry); + } else { + silc_client_update_server(client, conn, server_entry, name, info); } + server_entry->internal.resolve_cmd_ident = 0; /* Notify application */ - if (notify) - COMMAND_REPLY((ARGS, server_entry, name, info)); + silc_client_command_callback(cmd, server_entry, name, info); + silc_client_unref_server(client, conn, server_entry); break; case SILC_ID_CHANNEL: - channel_id = silc_id_payload_get_id(idp); - SILC_LOG_DEBUG(("Received channel information")); /* Check if we have this channel cached already. */ - channel_entry = silc_client_get_channel_by_id(client, conn, channel_id); + channel_entry = silc_client_get_channel_by_id(client, conn, + &id.u.channel_id); if (!channel_entry) { - if (!name) - break; + SILC_LOG_DEBUG(("Adding new channel entry (IDENTIFY)")); + + if (!name) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } /* Add new channel entry */ channel_entry = silc_client_add_channel(client, conn, name, 0, - channel_id); - channel_id = NULL; + &id.u.channel_id); + if (!channel_entry) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + silc_client_ref_channel(client, conn, channel_entry); } /* Notify application */ - if (notify) - COMMAND_REPLY((ARGS, channel_entry, name, info)); + silc_client_command_callback(cmd, channel_entry, + channel_entry->channel_name, info); + silc_client_unref_channel(client, conn, channel_entry); break; } - silc_id_payload_free(idp); - silc_free(client_id); - silc_free(server_id); - silc_free(channel_id); -} - -/* Received reply for IDENTIFY command. This maybe called several times - 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; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - - COMMAND_CHECK_STATUS; - - /* Save IDENTIFY info */ - silc_client_command_reply_identify_save(cmd, cmd->status, TRUE); - - /* 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); - - /* 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); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } -/* Received reply for command NICK. If everything went without errors - we just received our new Client ID. */ +/********************************** NICK ************************************/ -SILC_CLIENT_CMD_REPLY_FUNC(nick) -{ - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcIDPayload idp; - unsigned char *tmp; - SilcUInt32 argc, len; +/* Received reply for command NICK. */ - SILC_LOG_DEBUG(("Start")); +SILC_FSM_STATE(silc_client_command_reply_nick) +{ + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); + unsigned char *nick, *idp; + SilcUInt32 len, idp_len; + SilcClientID old_client_id; + SilcID id; + + /* Sanity checks */ + CHECK_STATUS("Cannot set nickname: "); + CHECK_ARGS(2, 3); - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Cannot set nickname: %s", - silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; + /* Take received Client ID */ + idp = silc_argument_get_arg_type(args, 2, &idp_len); + if (!idp) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + if (!silc_id_payload_parse_id(idp, idp_len, &id)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - argc = silc_argument_get_arg_num(cmd->args); - if (argc < 2 || argc > 2) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Cannot set nickname: bad reply to command"); - COMMAND_REPLY_ERROR; + /* Take the new nickname */ + nick = silc_argument_get_arg_type(args, 3, &len); + if (!nick) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - /* 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; + silc_rwlock_wrlock(conn->local_entry->internal.lock); + + /* Change the nickname */ + old_client_id = *conn->local_id; + if (!silc_client_change_nickname(client, conn, conn->local_entry, + nick, &id.u.client_id, idp, idp_len)) { + ERROR_CALLBACK(SILC_STATUS_ERR_BAD_NICKNAME); + silc_rwlock_unlock(conn->local_entry->internal.lock); goto out; } - silc_client_receive_new_id(cmd->client, cmd->sock, idp); - + + silc_rwlock_unlock(conn->local_entry->internal.lock); + /* Notify application */ - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK); - COMMAND_REPLY((ARGS, conn->local_entry)); - silc_client_command_reply_free(cmd); - return; + silc_client_command_callback(cmd, conn->local_entry, + conn->local_entry->nickname, &old_client_id); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK); - silc_client_command_reply_free(cmd); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } +/********************************** LIST ************************************/ + /* Received reply to the LIST command. */ -SILC_CLIENT_CMD_REPLY_FUNC(list) +SILC_FSM_STATE(silc_client_command_reply_list) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); unsigned char *tmp, *name, *topic; - SilcUInt32 usercount = 0, len; - SilcChannelID *channel_id = NULL; - SilcChannelEntry channel_entry; + SilcUInt32 usercount = 0; + SilcChannelEntry channel_entry = NULL; + SilcID id; - COMMAND_CHECK_STATUS; + /* Sanity checks */ + CHECK_STATUS("Cannot list channels: "); - tmp = silc_argument_get_arg_type(cmd->args, 2, &len); - if (!tmp) { - COMMAND_REPLY_ERROR; - goto out; + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + /* There were no channels in the network. */ + silc_client_command_callback(cmd, NULL, NULL, NULL, 0); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } - channel_id = silc_id_payload_parse_id(tmp, len, NULL); - if (!channel_id) { - COMMAND_REPLY_ERROR; - goto out; - } + CHECK_ARGS(3, 5); - name = silc_argument_get_arg_type(cmd->args, 3, NULL); + name = silc_argument_get_arg_type(args, 3, NULL); if (!name) { - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(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); + topic = silc_argument_get_arg_type(args, 4, NULL); + tmp = silc_argument_get_arg_type(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); + channel_entry = silc_client_get_channel_by_id(client, conn, + &id.u.channel_id); if (!channel_entry) { /* Add new channel entry */ - channel_entry = silc_client_add_channel(cmd->client, conn, name, 0, - channel_id); + channel_entry = silc_client_add_channel(client, conn, name, 0, + &id.u.channel_id); if (!channel_entry) { - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - channel_id = NULL; + silc_client_ref_channel(client, conn, channel_entry); } /* Notify application */ - COMMAND_REPLY((ARGS, channel_entry, name, topic, usercount)); - - /* 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; - } + silc_client_command_callback(cmd, channel_entry, channel_entry->channel_name, + topic, usercount); out: - silc_free(channel_id); - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST); - silc_client_command_reply_free(cmd); + silc_client_unref_channel(client, conn, channel_entry); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } +/********************************* TOPIC ************************************/ + /* Received reply to topic command. */ -SILC_CLIENT_CMD_REPLY_FUNC(topic) +SILC_FSM_STATE(silc_client_command_reply_topic) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcChannelEntry channel; - SilcChannelID *channel_id = NULL; - unsigned char *tmp; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); + SilcChannelEntry channel = NULL; char *topic; - SilcUInt32 argc, len; + SilcUInt32 len; + SilcID id; - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; + /* Sanity checks */ + CHECK_STATUS("Cannot set topic: "); + CHECK_ARGS(2, 3); + + /* Take Channel ID */ + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - argc = silc_argument_get_arg_num(cmd->args); - if (argc < 1 || argc > 3) { - COMMAND_REPLY_ERROR; + /* Get the channel entry */ + channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id); + if (!channel) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - /* Take Channel ID */ - tmp = silc_argument_get_arg_type(cmd->args, 2, &len); - if (!tmp) - goto out; + silc_rwlock_wrlock(channel->internal.lock); /* Take topic */ - topic = silc_argument_get_arg_type(cmd->args, 3, NULL); - if (!topic) - goto out; + topic = silc_argument_get_arg_type(args, 3, &len); + if (topic) { + silc_free(channel->topic); + channel->topic = silc_memdup(topic, len); + } - channel_id = silc_id_payload_parse_id(tmp, len, NULL); - if (!channel_id) - goto out; + silc_rwlock_unlock(channel->internal.lock); - /* 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; - goto out; - } - /* Notify application */ - COMMAND_REPLY((ARGS, channel, topic)); + silc_client_command_callback(cmd, channel, channel->topic); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC); - silc_client_command_reply_free(cmd); + silc_client_unref_channel(client, conn, channel); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } +/********************************* INVITE ***********************************/ + /* Received reply to invite command. */ -SILC_CLIENT_CMD_REPLY_FUNC(invite) +SILC_FSM_STATE(silc_client_command_reply_invite) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcChannelEntry channel; - SilcChannelID *channel_id; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); + SilcChannelEntry channel = NULL; unsigned char *tmp; SilcUInt32 len; + SilcArgumentPayload invite_args = NULL; + SilcID id; - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } + /* Sanity checks */ + CHECK_STATUS("Cannot invite: "); + CHECK_ARGS(2, 3); /* Take Channel ID */ - tmp = silc_argument_get_arg_type(cmd->args, 2, &len); - if (!tmp) - goto out; - - channel_id = silc_id_payload_parse_id(tmp, len, NULL); - if (!channel_id) + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; + } /* Get the channel entry */ - channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id); if (!channel) { - silc_free(channel_id); - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } /* Get the invite list */ - tmp = silc_argument_get_arg_type(cmd->args, 3, &len); + tmp = silc_argument_get_arg_type(args, 3, &len); + if (tmp) + invite_args = silc_argument_list_parse(tmp, len); /* Notify application */ - COMMAND_REPLY((ARGS, channel, tmp)); + silc_client_command_callback(cmd, channel, invite_args); + + if (invite_args) + silc_argument_payload_free(invite_args); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE); - silc_client_command_reply_free(cmd); + silc_client_unref_channel(client, conn, channel); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } +/********************************** KILL ************************************/ + /* Received reply to the KILL command. */ - -SILC_CLIENT_CMD_REPLY_FUNC(kill) + +SILC_FSM_STATE(silc_client_command_reply_kill) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); + SilcClientEntry client_entry; + SilcID id; - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; + /* Sanity checks */ + CHECK_STATUS("Cannot kill: "); + CHECK_ARGS(2, 2); + + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } + /* Get the client entry, if exists */ + client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); + /* Notify application */ - COMMAND_REPLY((ARGS)); + silc_client_command_callback(cmd, client_entry); + + /* Remove the client */ + if (client_entry) { + silc_client_remove_from_channels(client, conn, client_entry); + client_entry->internal.valid = FALSE; + silc_client_del_client(client, conn, client_entry); + silc_client_unref_client(client, conn, client_entry); + } out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL); - silc_client_command_reply_free(cmd); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } +/********************************** INFO ************************************/ + /* Received reply to INFO command. We receive the server ID and some information about the server user requested. */ -SILC_CLIENT_CMD_REPLY_FUNC(info) +SILC_FSM_STATE(silc_client_command_reply_info) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - unsigned char *tmp; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); SilcServerEntry server; - SilcServerID *server_id = NULL; char *server_name, *server_info; - SilcUInt32 len; + SilcID id; - SILC_LOG_DEBUG(("Start")); - - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s", - silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } + /* Sanity checks */ + CHECK_STATUS("Cannot get info: "); + CHECK_ARGS(4, 4); /* 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) + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; + } /* Get server name */ - server_name = silc_argument_get_arg_type(cmd->args, 3, NULL); - if (!server_name) + server_name = silc_argument_get_arg_type(args, 3, NULL); + if (!server_name) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; + } /* Get server info */ - server_info = silc_argument_get_arg_type(cmd->args, 4, NULL); - if (!server_info) + server_info = silc_argument_get_arg_type(args, 4, NULL); + if (!server_info) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); 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); + server = silc_client_get_server_by_id(client, conn, &id.u.server_id); if (!server) { - SILC_LOG_DEBUG(("New server entry")); - server = silc_client_add_server(cmd->client, conn, server_name, - server_info, - silc_id_dup(server_id, SILC_ID_SERVER)); + SILC_LOG_DEBUG(("Add new server entry (INFO)")); + server = silc_client_add_server(client, conn, server_name, + server_info, &id.u.server_id); if (!server) goto out; + silc_client_ref_server(client, conn, server); } /* Notify application */ - COMMAND_REPLY((ARGS, server, server->server_name, server->server_info)); + silc_client_command_callback(cmd, server, server->server_name, + server->server_info); + silc_client_unref_server(client, conn, server); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO); - silc_free(server_id); - silc_client_command_reply_free(cmd); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } -/* Received reply to PING command. The reply time is shown to user. */ +/********************************** STATS ***********************************/ -SILC_CLIENT_CMD_REPLY_FUNC(ping) +/* Received reply to STATS command. */ + +SILC_FSM_STATE(silc_client_command_reply_stats) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - void *id; - int i; - time_t diff, curtime; + SilcClientCommandContext cmd = fsm_context; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); + SilcClientStats stats; + unsigned char *buf = NULL; + SilcUInt32 buf_len = 0; + SilcBufferStruct b; + SilcID id; + + /* Sanity checks */ + CHECK_STATUS("Cannot get stats: "); + CHECK_ARGS(2, 3); - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; + /* Get server ID */ + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + /* Get statistics structure */ + memset(&stats, 0, sizeof(stats)); + buf = silc_argument_get_arg_type(args, 3, &buf_len); + if (buf) { + silc_buffer_set(&b, buf, buf_len); + silc_buffer_unformat(&b, + SILC_STR_UI_INT(&stats.starttime), + SILC_STR_UI_INT(&stats.uptime), + SILC_STR_UI_INT(&stats.my_clients), + SILC_STR_UI_INT(&stats.my_channels), + SILC_STR_UI_INT(&stats.my_server_ops), + SILC_STR_UI_INT(&stats.my_router_ops), + SILC_STR_UI_INT(&stats.cell_clients), + SILC_STR_UI_INT(&stats.cell_channels), + SILC_STR_UI_INT(&stats.cell_servers), + SILC_STR_UI_INT(&stats.clients), + SILC_STR_UI_INT(&stats.channels), + SILC_STR_UI_INT(&stats.servers), + SILC_STR_UI_INT(&stats.routers), + SILC_STR_UI_INT(&stats.server_ops), + SILC_STR_UI_INT(&stats.router_ops), + SILC_STR_END); } - 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; - goto out; - } + /* Notify application */ + silc_client_command_callback(cmd, &stats); - for (i = 0; i < conn->ping_count; i++) { - if (!conn->ping[i].dest_id) - continue; - if (SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) { - diff = curtime - conn->ping[i].start_time; - 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; - break; - } - } + out: + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; +} - silc_free(id); +/********************************** PING ************************************/ + +/* Received reply to PING command. */ + +SILC_FSM_STATE(silc_client_command_reply_ping) +{ + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcInt64 diff; + + diff = silc_time() - SILC_PTR_TO_64(cmd->context); + if (cmd->verbose) + SAY(client, conn, SILC_CLIENT_MESSAGE_INFO, + "Ping reply from %s: %d second%s", conn->remote_host, + (int)diff, diff == 1 ? "" : "s"); /* Notify application */ - COMMAND_REPLY((ARGS)); + silc_client_command_callback(cmd); - out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING); - silc_client_command_reply_free(cmd); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; +} + +/********************************** JOIN ************************************/ + +/* Continue JOIN command reply processing after resolving unknown users */ + +static void +silc_client_command_reply_join_resolved(SilcClient client, + SilcClientConnection conn, + SilcStatus status, + SilcDList clients, + void *context) +{ + SilcClientCommandContext cmd = context; + SilcChannelEntry channel = cmd->context; + SilcCommandPayload payload = silc_fsm_get_state_context(&cmd->thread); + SilcArgumentPayload args = silc_command_get_args(payload); + SilcUInt32 list_count; + unsigned char *tmp; + char msg[512]; + + if (!clients) { + silc_snprintf(msg, sizeof(msg), "Error resolving channel %s user list", + channel->channel_name); + SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR, msg); + } else { + tmp = silc_argument_get_arg_type(args, 12, NULL); + if (tmp) { + SILC_GET32_MSB(list_count, tmp); + if (list_count - silc_dlist_count(clients) > 5) { + silc_snprintf(msg, sizeof(msg), + "Channel %s user list was not fully resolved. " + "The channel may not be fully synced.", + channel->channel_name); + SAY(client, conn, SILC_CLIENT_MESSAGE_WARNING, msg); + } + } + } + + channel->internal.resolve_cmd_ident = 0; + silc_client_unref_channel(client, conn, channel); + + SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread); } + /* Received reply for JOIN command. */ -SILC_CLIENT_CMD_REPLY_FUNC(join) +SILC_FSM_STATE(silc_client_command_reply_join) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); SilcChannelEntry channel; - SilcChannelUser chu; - SilcChannelID *channel_id; - SilcUInt32 argc, mode = 0, len, list_count; + SilcUInt32 mode = 0, len, list_count; char *topic, *tmp, *channel_name = NULL, *hmac; - SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL; + const char *cipher; + SilcBufferStruct client_id_list, client_mode_list, keyp; + SilcHashTableList htl; + SilcID id; int i; - SILC_LOG_DEBUG(("Start")); - - if (cmd->error != SILC_STATUS_OK) { - if (cmd->error != SILC_STATUS_ERR_USER_ON_CHANNEL) - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } - - argc = silc_argument_get_arg_num(cmd->args); - if (argc < 7 || argc > 14) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Cannot join channel: Bad reply packet"); - COMMAND_REPLY_ERROR; - goto out; - } + /* Sanity checks */ + CHECK_STATUS("Cannot join channel: "); + CHECK_ARGS(9, 17); /* Get channel name */ - tmp = silc_argument_get_arg_type(cmd->args, 2, NULL); - if (!tmp) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Cannot join channel: Bad reply packet"); - COMMAND_REPLY_ERROR; + channel_name = silc_argument_get_arg_type(args, 2, NULL); + if (!channel_name) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - channel_name = tmp; /* Get Channel ID */ - tmp = silc_argument_get_arg_type(cmd->args, 3, &len); - if (!tmp) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Cannot join channel: Bad reply packet"); - COMMAND_REPLY_ERROR; - goto out; - } - channel_id = silc_id_payload_parse_id(tmp, len, NULL); - if (!channel_id) { - COMMAND_REPLY_ERROR; + if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - /* Get channel mode */ - tmp = silc_argument_get_arg_type(cmd->args, 5, NULL); - if (tmp) - SILC_GET32_MSB(mode, tmp); - - /* Get channel key */ - tmp = silc_argument_get_arg_type(cmd->args, 7, &len); - if (tmp) { - keyp = silc_buffer_alloc(len); - silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp)); - silc_buffer_put(keyp, tmp, len); - } - - /* Get topic */ - topic = silc_argument_get_arg_type(cmd->args, 10, NULL); - /* Check whether we have this channel entry already. */ - channel = silc_client_get_channel(cmd->client, conn, channel_name); + channel = silc_client_get_channel(client, conn, channel_name); if (channel) { - if (!SILC_ID_CHANNEL_COMPARE(channel->id, channel_id)) - silc_client_replace_channel_id(cmd->client, conn, channel, channel_id); + if (!SILC_ID_CHANNEL_COMPARE(&channel->id, &id.u.channel_id)) + silc_client_replace_channel_id(client, conn, channel, &id.u.channel_id); } else { /* Create new channel entry */ - channel = silc_client_add_channel(cmd->client, conn, channel_name, - mode, channel_id); - } - - conn->current_channel = channel; - - /* Get hmac */ - hmac = silc_argument_get_arg_type(cmd->args, 11, NULL); - if (hmac) { - if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Cannot join channel: Unsupported HMAC `%s'", hmac); - COMMAND_REPLY_ERROR; + channel = silc_client_add_channel(client, conn, channel_name, + mode, &id.u.channel_id); + if (!channel) { + ERROR_CALLBACK(SILC_STATUS_ERR_BAD_CHANNEL); goto out; } + silc_client_ref_channel(client, conn, channel); } /* Get the list count */ - tmp = silc_argument_get_arg_type(cmd->args, 12, &len); - if (!tmp) + tmp = silc_argument_get_arg_type(args, 12, &len); + if (!tmp || len != 4) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; + } SILC_GET32_MSB(list_count, tmp); /* Get Client ID list */ - tmp = silc_argument_get_arg_type(cmd->args, 13, &len); - if (!tmp) + tmp = silc_argument_get_arg_type(args, 13, &len); + if (!tmp) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; + } + silc_buffer_set(&client_id_list, tmp, len); - client_id_list = silc_buffer_alloc(len); - silc_buffer_pull_tail(client_id_list, len); - silc_buffer_put(client_id_list, tmp, len); + /* Resolve users we do not know about */ + if (!cmd->resolved) { + cmd->resolved = TRUE; + cmd->context = channel; + SILC_FSM_CALL(channel->internal.resolve_cmd_ident = + silc_client_get_clients_by_list( + client, conn, list_count, &client_id_list, + silc_client_command_reply_join_resolved, cmd)); + /* NOT REACHED */ + } /* Get client mode list */ - tmp = silc_argument_get_arg_type(cmd->args, 14, &len); - if (!tmp) + tmp = silc_argument_get_arg_type(args, 14, &len); + if (!tmp) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; + } + silc_buffer_set(&client_mode_list, tmp, len); - client_mode_list = silc_buffer_alloc(len); - silc_buffer_pull_tail(client_mode_list, len); - silc_buffer_put(client_mode_list, tmp, len); + silc_rwlock_wrlock(channel->internal.lock); /* Add clients we received in the reply to the channel */ for (i = 0; i < list_count; i++) { SilcUInt16 idp_len; - SilcUInt32 mode; - SilcClientID *client_id; + SilcID id; SilcClientEntry client_entry; /* 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, NULL); - if (!client_id) + if (!silc_id_payload_parse_id(client_id_list.data, idp_len, &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. */ - 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 = - silc_client_add_client(cmd->client, conn, NULL, NULL, NULL, - silc_id_dup(client_id, SILC_ID_CLIENT), 0); + /* Get client entry */ + client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); + if (client_entry && client_entry->internal.valid) { + /* Join client to the channel */ + silc_rwlock_wrlock(client_entry->internal.lock); + silc_client_add_to_channel(client, conn, channel, client_entry, mode); + silc_rwlock_unlock(client_entry->internal.lock); + } + silc_client_unref_client(client, conn, client_entry); + + if (!silc_buffer_pull(&client_id_list, idp_len)) { + silc_rwlock_unlock(channel->internal.lock); + goto out; + } + if (!silc_buffer_pull(&client_mode_list, 4)) { + silc_rwlock_unlock(channel->internal.lock); + goto out; + } + } + + /* Get hmac */ + hmac = silc_argument_get_arg_type(args, 11, NULL); + if (hmac) { + if (!silc_hmac_alloc(hmac, NULL, &channel->internal.hmac)) { + if (cmd->verbose) + SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR, + "Cannot join channel: Unsupported HMAC `%s'", hmac); + ERROR_CALLBACK(SILC_STATUS_ERR_UNKNOWN_ALGORITHM); + silc_rwlock_unlock(channel->internal.lock); + goto out; } + } - /* 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); + /* Get channel mode */ + tmp = silc_argument_get_arg_type(args, 5, &len); + if (tmp && len == 4) + SILC_GET32_MSB(mode, tmp); + channel->mode = mode; + + /* Get channel key and save it */ + tmp = silc_argument_get_arg_type(args, 7, &len); + if (tmp) { + /* If channel key already exists on the channel then while resolving + the user list we have already received new key from server. Don't + replace it with this old key. */ + if (!channel->internal.send_key) { + silc_buffer_set(&keyp, tmp, len); + silc_client_save_channel_key(client, conn, &keyp, channel); } + } - silc_free(client_id); - silc_buffer_pull(client_id_list, idp_len); - silc_buffer_pull(client_mode_list, 4); + /* Get topic */ + topic = silc_argument_get_arg_type(args, 10, NULL); + if (topic) { + silc_free(channel->topic); + channel->topic = silc_memdup(topic, strlen(topic)); + } + + /* Get founder key */ + tmp = silc_argument_get_arg_type(args, 15, &len); + if (tmp) { + if (channel->founder_key) + silc_pkcs_public_key_free(channel->founder_key); + channel->founder_key = NULL; + silc_public_key_payload_decode(tmp, len, &channel->founder_key); } - 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); - /* Save channel key */ - if (keyp && !(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) - silc_client_save_channel_key(cmd->client, conn, keyp, channel); + /* Get user limit */ + tmp = silc_argument_get_arg_type(args, 17, &len); + if (tmp && len == 4) + SILC_GET32_MSB(channel->user_limit, tmp); + if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT)) + channel->user_limit = 0; + + /* Get channel public key list */ + tmp = silc_argument_get_arg_type(args, 16, &len); + if (tmp) + silc_client_channel_save_public_keys(channel, tmp, len, FALSE); + + /* Set current channel */ + conn->current_channel = channel; + + silc_rwlock_unlock(channel->internal.lock); + + cipher = (channel->internal.send_key ? + silc_cipher_get_name(channel->internal.send_key) : NULL); + silc_hash_table_list(channel->user_list, &htl); /* Notify application */ - COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, - keyp ? keyp->head : NULL, NULL, - NULL, topic, hmac, list_count, client_id_list, - client_mode_list)); + silc_client_command_callback(cmd, channel->channel_name, channel, mode, &htl, + topic, cipher, hmac, channel->founder_key, + channel->channel_pubkeys, channel->user_limit); + + silc_hash_table_list_reset(&htl); + silc_client_unref_channel(client, conn, channel); out: - SILC_CLIENT_PENDING_EXEC(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); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } +/********************************** MOTD ************************************/ + /* Received reply for MOTD command */ -SILC_CLIENT_CMD_REPLY_FUNC(motd) +SILC_FSM_STATE(silc_client_command_reply_motd) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcUInt32 argc, i; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); + SilcUInt32 i; char *motd = NULL, *cp, line[256]; - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - return; - } - - argc = silc_argument_get_arg_num(cmd->args); - if (argc > 3) { - COMMAND_REPLY_ERROR; - goto out; - } + /* Sanity checks */ + CHECK_STATUS("Cannot get motd: "); + CHECK_ARGS(2, 3); - if (argc == 3) { - motd = silc_argument_get_arg_type(cmd->args, 3, NULL); + if (silc_argument_get_arg_num(args) == 3) { + motd = silc_argument_get_arg_type(args, 3, NULL); if (!motd) { - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -1111,14 +1378,15 @@ 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] = ' '; - - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line); - + + if (cmd->verbose) + SAY(client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line); + if (!strlen(cp)) break; i = 0; @@ -1127,942 +1395,781 @@ SILC_CLIENT_CMD_REPLY_FUNC(motd) } /* Notify application */ - COMMAND_REPLY((ARGS, motd)); + silc_client_command_callback(cmd, motd); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD); - silc_client_command_reply_free(cmd); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } -/* Received reply tot he UMODE command. Save the current user mode */ +/********************************** UMODE ***********************************/ -SILC_CLIENT_CMD_REPLY_FUNC(umode) +/* Received reply to the UMODE command. Save the current user mode */ + +SILC_FSM_STATE(silc_client_command_reply_umode) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); unsigned char *tmp; - SilcUInt32 mode; + SilcUInt32 mode, len; - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } + /* Sanity checks */ + CHECK_STATUS("Cannot change mode: "); + CHECK_ARGS(2, 2); - tmp = silc_argument_get_arg_type(cmd->args, 2, NULL); - if (!tmp) { - COMMAND_REPLY_ERROR; + tmp = silc_argument_get_arg_type(args, 2, &len); + if (!tmp || len != 4) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } SILC_GET32_MSB(mode, tmp); + silc_rwlock_wrlock(conn->local_entry->internal.lock); conn->local_entry->mode = mode; + silc_rwlock_unlock(conn->local_entry->internal.lock); /* Notify application */ - COMMAND_REPLY((ARGS, mode)); + silc_client_command_callback(cmd, mode); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE); - silc_client_command_reply_free(cmd); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } +/********************************** CMODE ***********************************/ + /* Received reply for CMODE command. */ -SILC_CLIENT_CMD_REPLY_FUNC(cmode) +SILC_FSM_STATE(silc_client_command_reply_cmode) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); unsigned char *tmp; SilcUInt32 mode; - SilcChannelID *channel_id; - SilcChannelEntry channel; + SilcChannelEntry channel = NULL; SilcUInt32 len; + SilcPublicKey public_key = NULL; + SilcID id; - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } + /* Sanity checks */ + CHECK_STATUS("Cannot change mode: "); + CHECK_ARGS(3, 6); /* Take Channel ID */ - tmp = silc_argument_get_arg_type(cmd->args, 2, &len); - if (!tmp) - goto out; - channel_id = silc_id_payload_parse_id(tmp, len, NULL); - if (!channel_id) + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; + } /* Get the channel entry */ - channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id); if (!channel) { - silc_free(channel_id); - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - + + /* Get founder public key */ + tmp = silc_argument_get_arg_type(args, 4, &len); + if (tmp) + silc_public_key_payload_decode(tmp, len, &public_key); + /* Get channel mode */ - tmp = silc_argument_get_arg_type(cmd->args, 3, NULL); - if (!tmp) { - silc_free(channel_id); - COMMAND_REPLY_ERROR; + tmp = silc_argument_get_arg_type(args, 3, &len); + if (!tmp || len != 4) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } + SILC_GET32_MSB(mode, tmp); + + silc_rwlock_wrlock(channel->internal.lock); + + /* Get user limit */ + tmp = silc_argument_get_arg_type(args, 6, &len); + if (tmp && len == 4) + SILC_GET32_MSB(channel->user_limit, tmp); + if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT)) + channel->user_limit = 0; + + /* Get channel public key(s) */ + tmp = silc_argument_get_arg_type(args, 5, &len); + if (tmp) + silc_client_channel_save_public_keys(channel, tmp, len, FALSE); + else if (channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH) + silc_client_channel_save_public_keys(channel, NULL, 0, TRUE); /* Save the mode */ - SILC_GET32_MSB(mode, tmp); channel->mode = mode; + silc_rwlock_unlock(channel->internal.lock); + /* Notify application */ - COMMAND_REPLY((ARGS, channel, mode)); + silc_client_command_callback(cmd, channel, mode, public_key, + channel->channel_pubkeys, channel->user_limit); + + silc_rwlock_wrlock(channel->internal.lock); + + /* If founder key changed, update it */ + if (public_key && + (!channel->founder_key || + !silc_pkcs_public_key_compare(public_key, channel->founder_key))) { + if (channel->founder_key) + silc_pkcs_public_key_free(channel->founder_key); + channel->founder_key = public_key; + public_key = NULL; + } - silc_free(channel_id); + silc_rwlock_unlock(channel->internal.lock); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE); - silc_client_command_reply_free(cmd); + silc_client_unref_channel(client, conn, channel); + if (public_key) + silc_pkcs_public_key_free(public_key); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } +/********************************** CUMODE **********************************/ + /* Received reply for CUMODE command */ -SILC_CLIENT_CMD_REPLY_FUNC(cumode) +SILC_FSM_STATE(silc_client_command_reply_cumode) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcClientID *client_id; - SilcChannelID *channel_id; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); SilcClientEntry client_entry; - SilcChannelEntry channel; + SilcChannelEntry channel = NULL; SilcChannelUser chu; - unsigned char *modev, *tmp, *id; + unsigned char *modev; SilcUInt32 len, mode; - - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } - + SilcID id; + + /* Sanity checks */ + CHECK_STATUS("Cannot change mode: "); + CHECK_ARGS(4, 4); + /* Get channel mode */ - modev = silc_argument_get_arg_type(cmd->args, 2, NULL); - if (!modev) { - COMMAND_REPLY_ERROR; + modev = silc_argument_get_arg_type(args, 2, &len); + if (!modev || len != 4) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } + SILC_GET32_MSB(mode, modev); /* Take Channel ID */ - tmp = silc_argument_get_arg_type(cmd->args, 3, &len); - if (!tmp) - goto out; - channel_id = silc_id_payload_parse_id(tmp, len, NULL); - if (!channel_id) + if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; + } /* Get the channel entry */ - channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id); if (!channel) { - silc_free(channel_id); - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - + /* Get Client ID */ - id = silc_argument_get_arg_type(cmd->args, 4, &len); - if (!id) { - silc_free(channel_id); - COMMAND_REPLY_ERROR; - goto out; - } - client_id = silc_id_payload_parse_id(id, len, NULL); - if (!client_id) { - silc_free(channel_id); - COMMAND_REPLY_ERROR; + if (!silc_argument_get_decoded(args, 4, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - + /* Get client entry */ - client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); + client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); if (!client_entry) { - silc_free(channel_id); - silc_free(client_id); - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } /* Save the mode */ - SILC_GET32_MSB(mode, modev); + silc_rwlock_wrlock(channel->internal.lock); chu = silc_client_on_channel(channel, client_entry); if (chu) chu->mode = mode; + silc_rwlock_unlock(channel->internal.lock); /* Notify application */ - COMMAND_REPLY((ARGS, mode, channel, client_entry)); - silc_free(client_id); - silc_free(channel_id); - + silc_client_command_callback(cmd, mode, channel, client_entry); + + silc_client_unref_client(client, conn, client_entry); + out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE); - silc_client_command_reply_free(cmd); + silc_client_unref_channel(client, conn, channel); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } -SILC_CLIENT_CMD_REPLY_FUNC(kick) +/********************************** KICK ************************************/ + +SILC_FSM_STATE(silc_client_command_reply_kick) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); + SilcClientEntry client_entry; + SilcChannelEntry channel = NULL; + SilcID id; + + /* Sanity checks */ + CHECK_STATUS("Cannot kick: "); + CHECK_ARGS(3, 3); - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; + /* Take Channel ID */ + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + /* Get the channel entry */ + channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id); + if (!channel) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + /* Get Client ID */ + if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; + } + + /* Get client entry */ + client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); + if (!client_entry) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } /* Notify application */ - COMMAND_REPLY((ARGS)); + silc_client_command_callback(cmd, channel, client_entry); + + silc_client_unref_client(client, conn, client_entry); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK); - silc_client_command_reply_free(cmd); + silc_client_unref_channel(client, conn, channel); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } -SILC_CLIENT_CMD_REPLY_FUNC(silcoper) +/******************************** SILCOPER **********************************/ + +SILC_FSM_STATE(silc_client_command_reply_silcoper) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcClientCommandContext cmd = fsm_context; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } + /* Sanity checks */ + CHECK_STATUS("Cannot change mode: "); + CHECK_ARGS(1, 1); + + /* Set user mode */ + cmd->conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR; /* Notify application */ - COMMAND_REPLY((ARGS)); + silc_client_command_callback(cmd); - out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER); - silc_client_command_reply_free(cmd); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } -SILC_CLIENT_CMD_REPLY_FUNC(oper) +/********************************** OPER ************************************/ + +SILC_FSM_STATE(silc_client_command_reply_oper) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcClientCommandContext cmd = fsm_context; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } + /* Sanity checks */ + CHECK_STATUS("Cannot change mode: "); + CHECK_ARGS(1, 1); + + /* Set user mode */ + cmd->conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR; /* Notify application */ - COMMAND_REPLY((ARGS)); + silc_client_command_callback(cmd); - out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER); - silc_client_command_reply_free(cmd); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } -SILC_CLIENT_CMD_REPLY_FUNC(detach) +/********************************* DETACH ***********************************/ + +SILC_FSM_STATE(silc_client_command_reply_detach) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); SilcBuffer detach; - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; + /* Sanity checks */ + CHECK_STATUS("Cannot detach: "); + CHECK_ARGS(1, 1); + + /* Get detachment data */ + detach = silc_client_get_detach_data(client, conn); + if (!detach) { + ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT); goto out; } /* Notify application */ - COMMAND_REPLY((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); - } + silc_client_command_callback(cmd, detach); + silc_buffer_free(detach); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_DETACH); - silc_client_command_reply_free(cmd); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } -SILC_CLIENT_CMD_REPLY_FUNC(ban) +/********************************** WATCH ***********************************/ + +SILC_FSM_STATE(silc_client_command_reply_watch) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcChannelEntry channel; - SilcChannelID *channel_id; + SilcClientCommandContext cmd = fsm_context; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); + + /* Sanity checks */ + CHECK_STATUS("Cannot set watch: "); + CHECK_ARGS(1, 1); + + /* Notify application */ + silc_client_command_callback(cmd); + + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; +} + +/*********************************** BAN ************************************/ + +SILC_FSM_STATE(silc_client_command_reply_ban) +{ + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); + SilcChannelEntry channel = NULL; unsigned char *tmp; SilcUInt32 len; + SilcArgumentPayload invite_args = NULL; + SilcID id; - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } + /* Sanity checks */ + CHECK_STATUS("Cannot set ban: "); + CHECK_ARGS(2, 3); /* Take Channel ID */ - tmp = silc_argument_get_arg_type(cmd->args, 2, &len); - if (!tmp) - goto out; - - channel_id = silc_id_payload_parse_id(tmp, len, NULL); - if (!channel_id) + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; + } /* Get the channel entry */ - channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); + channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id); if (!channel) { - silc_free(channel_id); - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - - /* Get the ban list */ - tmp = silc_argument_get_arg_type(cmd->args, 3, &len); + + /* Get the invite list */ + tmp = silc_argument_get_arg_type(args, 3, &len); + if (tmp) + invite_args = silc_argument_list_parse(tmp, len); /* Notify application */ - COMMAND_REPLY((ARGS, channel, tmp)); + silc_client_command_callback(cmd, channel, invite_args); + + if (invite_args) + silc_argument_payload_free(invite_args); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN); - silc_client_command_reply_free(cmd); + silc_client_unref_channel(client, conn, channel); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } +/********************************** LEAVE ***********************************/ + /* Reply to LEAVE command. */ -SILC_CLIENT_CMD_REPLY_FUNC(leave) +SILC_FSM_STATE(silc_client_command_reply_leave) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcChannelID *channel_id; - SilcChannelEntry channel = NULL; - unsigned char *tmp; - SilcUInt32 len; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); + SilcChannelEntry channel; + SilcCipher key; + SilcHmac hmac; + SilcID id; + + /* Sanity checks */ + CHECK_STATUS("Cannot set leave: "); + CHECK_ARGS(2, 2); - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; + /* Get Channel ID */ + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - /* 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; - - /* 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; - goto out; - } - - silc_free(channel_id); + /* Get the channel entry */ + channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id); + if (!channel) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); + goto out; } + /* Remove us from this channel. */ + silc_client_remove_from_channel(client, conn, channel, conn->local_entry); + /* Notify application */ - COMMAND_REPLY((ARGS, channel)); + silc_client_command_callback(cmd, channel); + + /* Remove old keys and stuff. The channel may remain even after leaving + but we want to remove these always. */ + if (channel->internal.send_key) + silc_cipher_free(channel->internal.send_key); + channel->internal.send_key = NULL; + if (channel->internal.receive_key) + silc_cipher_free(channel->internal.receive_key); + channel->internal.receive_key = NULL; + if (channel->internal.hmac) + silc_hmac_free(channel->internal.hmac); + channel->internal.hmac = NULL; + if (channel->internal.old_channel_keys) { + silc_dlist_start(channel->internal.old_channel_keys); + while ((key = silc_dlist_get(channel->internal.old_channel_keys))) + silc_cipher_free(key); + silc_dlist_uninit(channel->internal.old_channel_keys); + } + channel->internal.old_channel_keys = NULL; + if (channel->internal.old_hmacs) { + silc_dlist_start(channel->internal.old_hmacs); + while ((hmac = silc_dlist_get(channel->internal.old_hmacs))) + silc_hmac_free(hmac); + silc_dlist_uninit(channel->internal.old_hmacs); + } + channel->internal.old_hmacs = NULL; + + /* Now delete the channel. */ + silc_client_empty_channel(client, conn, channel); + silc_client_del_channel(client, conn, channel); out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE); - silc_client_command_reply_free(cmd); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } -/* Channel resolving callback for USERS command reply. */ +/********************************* USERS ************************************/ + +/* Continue USERS command reply processing after resolving unknown users */ -static void silc_client_command_reply_users_cb(SilcClient client, - SilcClientConnection conn, - SilcChannelEntry *channels, - SilcUInt32 channels_count, - void *context) +static void +silc_client_command_reply_users_resolved(SilcClient client, + SilcClientConnection conn, + SilcStatus status, + SilcDList clients, + 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_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS); - silc_client_command_reply_free(cmd); - return; + SilcClientCommandContext cmd = context; + SILC_FSM_CALL_CONTINUE(&cmd->thread); +} + + +/* Continue USERS command after resolving unknown channel */ + +static void +silc_client_command_reply_users_continue(SilcClient client, + SilcClientConnection conn, + SilcStatus status, + SilcDList channels, + void *context) +{ + SilcClientCommandContext cmd = context; + + if (!channels) { + SilcCommandPayload payload = silc_fsm_get_state_context(&cmd->thread); + SilcArgumentPayload args = silc_command_get_args(payload); + + cmd->status = SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID; + ERROR_CALLBACK(cmd->status); + silc_fsm_next(&cmd->thread, silc_client_command_reply_processed); } - silc_client_command_reply_users(context, NULL); + SILC_FSM_CALL_CONTINUE(&cmd->thread); } -static int -silc_client_command_reply_users_save(SilcClientCommandReplyContext cmd, - SilcCommandStatus status, - bool notify, - SilcGetChannelCallback get_channel, - SilcCommandCb get_clients) +/* Reply to USERS command. Received list of client ID's and theirs modes + on the channel we requested. */ + +SILC_FSM_STATE(silc_client_command_reply_users) { - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcChannelEntry channel; - SilcClientEntry client_entry; - SilcChannelUser chu; - SilcChannelID *channel_id = NULL; - SilcBufferStruct client_id_list, client_mode_list; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); unsigned char *tmp; - SilcUInt32 tmp_len, list_count; + SilcUInt32 tmp_len, list_count, mode; + SilcUInt16 idp_len; + SilcHashTableList htl; + SilcBufferStruct client_id_list, client_mode_list; + SilcChannelEntry channel = NULL; + SilcClientEntry client_entry; + SilcID id; int i; - unsigned char **res_argv = NULL; - SilcUInt32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0; - SILC_LOG_DEBUG(("Start")); + /* Sanity checks */ + CHECK_STATUS("Cannot get users: "); + CHECK_ARGS(5, 5); /* Get channel ID */ - tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len); - if (!tmp) { - COMMAND_REPLY_ERROR; + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL); - if (!channel_id) { - COMMAND_REPLY_ERROR; - goto out; + + /* Get channel entry */ + channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id); + if (!channel) { + /* Resolve the channel from server */ + SILC_FSM_CALL(silc_client_get_channel_by_id_resolve( + client, conn, &id.u.channel_id, + silc_client_command_reply_users_continue, cmd)); + /* NOT REACHED */ } - + /* Get the list count */ - tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len); - if (!tmp) { - COMMAND_REPLY_ERROR; + tmp = silc_argument_get_arg_type(args, 3, &tmp_len); + if (!tmp || tmp_len != 4) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } SILC_GET32_MSB(list_count, tmp); /* Get Client ID list */ - tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len); + tmp = silc_argument_get_arg_type(args, 4, &tmp_len); if (!tmp) { - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } silc_buffer_set(&client_id_list, tmp, tmp_len); + /* Resolve users we do not know about */ + if (!cmd->resolved) { + cmd->resolved = TRUE; + silc_client_unref_channel(client, conn, channel); + SILC_FSM_CALL(silc_client_get_clients_by_list( + client, conn, list_count, &client_id_list, + silc_client_command_reply_users_resolved, cmd)); + /* NOT REACHED */ + } + /* Get client mode list */ - tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len); + tmp = silc_argument_get_arg_type(args, 5, &tmp_len); if (!tmp) { - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } silc_buffer_set(&client_mode_list, tmp, tmp_len); - /* Get channel entry */ - channel = silc_client_get_channel_by_id(cmd->client, conn, channel_id); - if (!channel) { - /* Resolve the channel from server */ - silc_client_get_channel_by_id_resolve(cmd->client, conn, channel_id, - get_channel, cmd); - silc_free(channel_id); - return 1; - } + SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count)); + + silc_rwlock_wrlock(channel->internal.lock); /* Cache the received Client ID's and modes. */ for (i = 0; i < list_count; i++) { - SilcUInt16 idp_len; - SilcUInt32 mode; - SilcClientID *client_id; - - /* 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, NULL); - if (!client_id) - continue; + if (!silc_id_payload_parse_id(client_id_list.data, idp_len, &id)) + goto out; /* Mode */ SILC_GET32_MSB(mode, client_mode_list.data); - /* Check if we have this client cached already. */ - client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); - if (!client_entry || !client_entry->username || !client_entry->realname) { - if (client_entry) { - if (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; - } + /* Save the client on this channel. Unknown clients are ignored as they + clearly do not exist since the resolving didn't find them. */ + client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); + if (client_entry && client_entry->internal.valid) { + silc_rwlock_wrlock(client_entry->internal.lock); + silc_client_add_to_channel(client, conn, channel, client_entry, mode); + silc_rwlock_unlock(client_entry->internal.lock); + } + silc_client_unref_client(client, conn, client_entry); - /* 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 { - 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); - } + if (!silc_buffer_pull(&client_id_list, idp_len)) { + silc_rwlock_unlock(channel->internal.lock); + goto out; } + if (!silc_buffer_pull(&client_mode_list, 4)) { + silc_rwlock_unlock(channel->internal.lock); + goto out; + } + } - 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 - that we don't know about. */ - if (res_argc) { - 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, - NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len, - TRUE); - - /* Register pending command callback. After we've received the WHOIS - 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, - get_clients, cmd); - - silc_buffer_free(res_cmd); - silc_free(channel_id); - silc_free(res_argv); - silc_free(res_argv_lens); - silc_free(res_argv_types); - 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)); + silc_rwlock_unlock(channel->internal.lock); /* Notify application */ - if (notify) - COMMAND_REPLY((ARGS, channel, list_count, &client_id_list, - &client_mode_list)); + silc_hash_table_list(channel->user_list, &htl); + silc_client_command_callback(cmd, channel, &htl); + silc_hash_table_list_reset(&htl); out: - silc_free(channel_id); - return 0; + silc_client_unref_channel(client, conn, channel); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } -/* 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; - - SILC_LOG_DEBUG(("Start")); - - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } - - if (silc_client_command_reply_users_save(cmd, cmd->status, TRUE, - silc_client_command_reply_users_cb, - silc_client_command_reply_users)) - return; - - out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS); - silc_client_command_reply_free(cmd); -} +/********************************** GETKEY **********************************/ /* Received command reply to GETKEY command. WE've received the remote client's public key. */ -SILC_CLIENT_CMD_REPLY_FUNC(getkey) +SILC_FSM_STATE(silc_client_command_reply_getkey) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - SilcIDPayload idp = NULL; - SilcClientID *client_id = NULL; + SilcClientCommandContext cmd = fsm_context; + SilcClientConnection conn = cmd->conn; + SilcClient client = conn->client; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); SilcClientEntry client_entry; - SilcServerID *server_id = NULL; SilcServerEntry server_entry; - SilcSKEPKType type; - unsigned char *tmp, *pk; + unsigned char *tmp; SilcUInt32 len; - SilcUInt16 pk_len; - SilcIdType id_type; - SilcPublicKey public_key = NULL; + SilcPublicKey public_key; + SilcID id; - SILC_LOG_DEBUG(("Start")); + /* Sanity checks */ + CHECK_STATUS("Cannot get key: "); + CHECK_ARGS(2, 3); - if (cmd->error != SILC_STATUS_OK) { - SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, - "%s", silc_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; + /* Get the ID */ + if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) { + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - tmp = silc_argument_get_arg_type(cmd->args, 2, &len); + /* Get the public key */ + tmp = silc_argument_get_arg_type(args, 3, &len); if (!tmp) { - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - idp = silc_id_payload_parse(tmp, len); - if (!idp) { - COMMAND_REPLY_ERROR; + if (!silc_public_key_payload_decode(tmp, len, &public_key)) { + SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR, + "Cannot decode public key: malformed/unsupported public key"); + ERROR_CALLBACK(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) - if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key)) - public_key = NULL; - } - - id_type = silc_id_payload_get_type(idp); - if (id_type == SILC_ID_CLIENT) { + if (id.type == SILC_ID_CLIENT) { /* Received client's public key */ - client_id = silc_id_payload_get_id(idp); - client_entry = silc_client_get_client_by_id(cmd->client, conn, client_id); + client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); if (!client_entry) { - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } + silc_rwlock_wrlock(client_entry->internal.lock); + + /* Save fingerprint */ + if (!client_entry->fingerprint) + silc_hash_make(conn->internal->sha1hash, tmp + 4, len - 4, + client_entry->fingerprint); + if (!client_entry->public_key) { + client_entry->public_key = public_key; + public_key = NULL; + } + + silc_rwlock_unlock(client_entry->internal.lock); + /* Notify application */ - COMMAND_REPLY((ARGS, id_type, client_entry, public_key)); - } else if (id_type == SILC_ID_SERVER) { + silc_client_command_callback(cmd, SILC_ID_CLIENT, client_entry, + client_entry->public_key); + silc_client_unref_client(client, conn, client_entry); + } else if (id.type == SILC_ID_SERVER) { /* Received server's public key */ - server_id = silc_id_payload_get_id(idp); - server_entry = silc_client_get_server_by_id(cmd->client, conn, server_id); + server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id); if (!server_entry) { - COMMAND_REPLY_ERROR; + ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - /* Notify application */ - COMMAND_REPLY((ARGS, id_type, server_entry, public_key)); - } - - out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY); - if (idp) - silc_id_payload_free(idp); - if (public_key) - silc_pkcs_public_key_free(public_key); - silc_free(client_id); - silc_free(server_id); - silc_client_command_reply_free(cmd); -} - -SILC_CLIENT_CMD_REPLY_FUNC(quit) -{ - silc_client_command_reply_free(context); -} + silc_rwlock_wrlock(server_entry->internal.lock); - -/****************************************************************************** - - Internal command reply functions - -******************************************************************************/ - -SILC_CLIENT_CMD_REPLY_FUNC(whois_i) -{ - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data; - - SILC_LOG_DEBUG(("Start")); - - if (cmd->error != SILC_STATUS_OK) - goto out; - - /* 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); - - /* 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); - } + if (!server_entry->public_key) { + server_entry->public_key = public_key; + public_key = NULL; } - } - - /* 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; - SILC_LOG_DEBUG(("Start")); + silc_rwlock_unlock(server_entry->internal.lock); - if (cmd->error != SILC_STATUS_OK) - goto out; - - /* 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; + /* Notify application */ + silc_client_command_callback(cmd, SILC_ID_SERVER, server_entry, + server_entry->public_key); + silc_client_unref_server(client, conn, server_entry); } out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY); - - /* 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; - - SILC_LOG_DEBUG(("Start")); - - if (cmd->error != SILC_STATUS_OK) - goto out; - - /* 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); - silc_client_command_reply_free(cmd); + if (public_key) + silc_pkcs_public_key_free(public_key); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } -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_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS); - silc_client_command_reply_free(cmd); - return; - } +/********************************** SERVICE *********************************/ - silc_client_command_reply_users_i(context, NULL); -} +/* Reply to SERVICE command. */ +/* XXX incomplete */ -SILC_CLIENT_CMD_REPLY_FUNC(users_i) +SILC_FSM_STATE(silc_client_command_reply_service) { - SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context; - - SILC_LOG_DEBUG(("Start")); + SilcClientCommandContext cmd = fsm_context; + SilcCommandPayload payload = state_context; + SilcArgumentPayload args = silc_command_get_args(payload); + SilcUInt32 tmp_len; + unsigned char *service_list, *name; - if (cmd->error != SILC_STATUS_OK) - goto out; + /* Sanity checks */ + CHECK_STATUS("Cannot get service: "); - /* Save USERS info */ - if (silc_client_command_reply_users_save( - cmd, cmd->status, FALSE, - silc_client_command_reply_users_i_cb, - silc_client_command_reply_users_i)) - return; + /* Get service list */ + service_list = silc_argument_get_arg_type(args, 2, &tmp_len); - out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS); - - /* 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_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } + /* Get requested service name */ + name = silc_argument_get_arg_type(args, 3, &tmp_len); /* Notify application */ - COMMAND_REPLY((ARGS)); + silc_client_command_callback(cmd, service_list, name); - out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CONNECT); - silc_client_command_reply_free(cmd); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; } -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_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } +/*********************************** QUIT ***********************************/ - /* Notify application */ - COMMAND_REPLY((ARGS)); +/* QUIT command reply stub */ - out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_CLOSE); - silc_client_command_reply_free(cmd); -} - -SILC_CLIENT_CMD_REPLY_FUNC(shutdown) +SILC_FSM_STATE(silc_client_command_reply_quit) { - 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_client_command_status_message(cmd->error)); - COMMAND_REPLY_ERROR; - goto out; - } - - /* Notify application */ - COMMAND_REPLY((ARGS)); - - out: - SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PRIV_SHUTDOWN); - silc_client_command_reply_free(cmd); + silc_fsm_next(fsm, silc_client_command_reply_processed); + return SILC_FSM_CONTINUE; }