Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2006 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
/************************** Types and definitions ***************************/
/* Calls error command reply callback back to command sender. */
-#define ERROR_CALLBACK(error) \
+#define ERROR_CALLBACK(err) \
do { \
void *arg1 = NULL, *arg2 = NULL; \
if (cmd->status != SILC_STATUS_OK) \
silc_status_get_args(cmd->status, args, &arg1, &arg2); \
else \
- cmd->status = error; \
- SILC_LOG_DEBUG(("Error in command reply")); \
+ 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)
/* Check for error */
#define CHECK_STATUS(msg) \
- SILC_LOG_DEBUG(("Start")); \
+ 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_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_process); \
- return SILC_FSM_YIELD; \
+ silc_fsm_next(fsm, silc_client_command_reply_processed); \
+ return SILC_FSM_CONTINUE; \
}
/* Check for correct arguments */
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_process); \
- return SILC_FSM_YIELD; \
+ silc_fsm_next(fsm, silc_client_command_reply_processed); \
+ return SILC_FSM_CONTINUE; \
}
#define SAY cmd->conn->client->internal->ops->say
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_LOG_DEBUG(("cp %p, ap %p", cp, ap));
silc_va_copy(cp, ap);
- SILC_LOG_DEBUG(("cp %p, ap %p", cp, ap));
cmd->conn->client->internal->ops->command_reply(
cmd->conn->client, cmd->conn, cmd->cmd, cmd->status,
cmd->error, cp);
}
/* Reply callback */
- silc_list_start(cmd->reply_callbacks);
- while ((cb = silc_list_get(cmd->reply_callbacks)))
+ list = cmd->reply_callbacks;
+ silc_list_start(list);
+ while ((cb = silc_list_get(list)))
if (!cb->do_not_call) {
- SILC_LOG_DEBUG(("cp %p, ap %p", cp, ap));
silc_va_copy(cp, ap);
- SILC_LOG_DEBUG(("cp %p, ap %p", cp, ap));
- cb->do_not_call = cb->reply(cmd->conn->client, cmd->conn, cmd->cmd,
- cmd->status, cmd->error, cb->context, cp);
+ cb->do_not_call = !cb->reply(cmd->conn->client, cmd->conn, cmd->cmd,
+ cmd->status, cmd->error, cb->context, cp);
va_end(cp);
}
SilcClient client = cmd->conn->client;
SilcClientConnection conn = cmd->conn;
SilcArgumentPayload args = silc_command_get_args(payload);
- SilcClientEntry client_entry;
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_unref_client(client, conn, client_entry);
+ silc_client_remove_from_channels(client, conn, client_entry);
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 client 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;
+
+ /* Remove unknown client 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;
}
}
silc_mutex_lock(conn->internal->lock);
silc_list_start(conn->internal->pending_commands);
while ((cmd = silc_list_get(conn->internal->pending_commands))) {
- SILC_LOG_DEBUG(("cmd %p, command %d, ident %d", cmd, cmd->cmd,
- cmd->cmd_ident));
if ((cmd->cmd == command || cmd->cmd == SILC_COMMAND_NONE)
&& cmd->cmd_ident == cmd_ident) {
silc_list_del(conn->internal->pending_commands, cmd);
silc_mutex_unlock(conn->internal->lock);
if (!cmd) {
- SILC_LOG_DEBUG(("Unknown command reply"));
+ 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;
}
- SILC_LOG_DEBUG(("cmd %p, command %d", cmd, cmd->cmd));
-
/* Signal command thread that command reply has arrived */
silc_fsm_set_state_context(&cmd->thread, payload);
silc_fsm_next(&cmd->thread, silc_client_command_reply_process);
SILC_FSM_STATE(silc_client_command_reply_wait)
{
+ SilcClientCommandContext cmd = fsm_context;
+
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, 20, 0);
+ silc_fsm_next_later(fsm, silc_client_command_reply_timeout,
+ cmd->cmd != SILC_COMMAND_PING ? 25 : 60, 0);
return SILC_FSM_WAIT;
}
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 && cmd->cmd != SILC_COMMAND_PING)
+ ERROR_CALLBACK(SILC_STATUS_ERR_TIMEDOUT);
+ return SILC_FSM_FINISH;
+ }
+
+ SILC_LOG_DEBUG(("Command %s timeout", silc_get_command_name(cmd->cmd)));
+
/* 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;
}
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);
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;
silc_client_add_client(client, conn, nickname, username, realname,
&id.u.client_id, mode);
if (!client_entry) {
- ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT);
goto out;
}
+ silc_client_ref_client(client, conn, client_entry);
} else {
silc_client_update_client(client, conn, client_entry,
nickname, username, realname, mode);
silc_client_add_client(client, conn, name, info, NULL,
&id.u.client_id, 0);
if (!client_entry) {
- ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
+ ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT);
goto out;
}
+ silc_client_ref_client(client, conn, client_entry);
} else {
silc_client_update_client(client, conn, client_entry,
name, info, NULL, 0);
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);
}
/* Notify application */
silc_client_command_callback(cmd, server_entry, name, info);
+ silc_client_unref_server(client, conn, server_entry);
break;
case SILC_ID_CHANNEL:
channel_entry = silc_client_get_channel_by_id(client, conn,
&id.u.channel_id);
if (!channel_entry) {
- SILC_LOG_DEBUG(("Adding new channel entry (IDENTIFY"));
+ SILC_LOG_DEBUG(("Adding new channel entry (IDENTIFY)"));
if (!name) {
ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
goto out;
}
+ silc_client_ref_channel(client, conn, channel_entry);
}
/* Notify application */
silc_client_command_callback(cmd, channel_entry, name, info);
+ silc_client_unref_channel(client, conn, channel_entry);
break;
}
SilcClient client = conn->client;
SilcCommandPayload payload = state_context;
SilcArgumentPayload args = silc_command_get_args(payload);
- unsigned char *tmp, *nick, *idp;
+ unsigned char *nick, *idp;
SilcUInt32 len, idp_len;
SilcClientID old_client_id;
SilcID id;
CHECK_STATUS("Cannot set nickname: ");
CHECK_ARGS(2, 3);
- old_client_id = *conn->local_id;
-
/* Take received Client ID */
idp = silc_argument_get_arg_type(args, 2, &idp_len);
if (!idp) {
goto out;
}
- /* Normalize nickname */
- tmp = silc_identifier_check(nick, len, SILC_STRING_UTF8, 128, NULL);
- if (!tmp) {
- ERROR_CALLBACK(SILC_STATUS_ERR_BAD_NICKNAME);
- goto out;
- }
-
- /* Update the client entry */
- silc_mutex_lock(conn->internal->lock);
- if (!silc_idcache_update(conn->internal->client_cache,
- conn->internal->local_entry,
- &conn->local_entry->id,
- &id.u.client_id,
- conn->local_entry->nickname_normalized,
- tmp, TRUE)) {
- silc_free(tmp);
- silc_mutex_unlock(conn->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);
goto out;
}
- silc_mutex_unlock(conn->internal->lock);
- memset(conn->local_entry->nickname, 0, sizeof(conn->local_entry->nickname));
- memcpy(conn->local_entry->nickname, nick, len);
- conn->local_entry->nickname_normalized = tmp;
- silc_buffer_enlarge(conn->internal->local_idp, idp_len);
- silc_buffer_put(conn->internal->local_idp, idp, idp_len);
- silc_client_nickname_format(client, conn, conn->local_entry);
- silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id, 0, NULL);
/* Notify application */
silc_client_command_callback(cmd, conn->local_entry,
SilcArgumentPayload args = silc_command_get_args(payload);
unsigned char *tmp, *name, *topic;
SilcUInt32 usercount = 0;
- SilcChannelEntry channel_entry;
+ SilcChannelEntry channel_entry = NULL;
SilcID id;
/* Sanity checks */
ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
goto out;
}
+ silc_client_ref_channel(client, conn, channel_entry);
}
/* Notify application */
silc_client_command_callback(cmd, channel_entry, name, topic, usercount);
out:
+ silc_client_unref_channel(client, conn, channel_entry);
silc_fsm_next(fsm, silc_client_command_reply_processed);
return SILC_FSM_CONTINUE;
}
/* Notify application */
silc_client_command_callback(cmd, client_entry);
- /* Remove the client from all channels and free it */
+ /* Remove the client */
if (client_entry) {
+ silc_client_remove_from_channels(client, conn, client_entry);
silc_client_del_client(client, conn, client_entry);
silc_client_unref_client(client, conn, client_entry);
}
/* See whether we have this server cached. If not create it. */
server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
if (!server) {
- SILC_LOG_DEBUG(("New server entry"));
+ 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 */
silc_client_command_callback(cmd, server, server->server_name,
server->server_info);
+ silc_client_unref_server(client, conn, server);
out:
silc_fsm_next(fsm, silc_client_command_reply_processed);
const char *cipher;
SilcBufferStruct client_id_list, client_mode_list, keyp;
SilcHashTableList htl;
- SilcDList chpks = NULL;
SilcID id;
int i;
/* Mode */
SILC_GET32_MSB(mode, client_mode_list.data);
- SILC_LOG_DEBUG(("id %s", silc_id_render(&id.u.client_id, SILC_ID_CLIENT)));
-
/* Get client entry */
client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
if (!client_entry)
continue;
/* Join client to the channel */
- silc_client_add_to_channel(channel, client_entry, mode);
+ silc_client_add_to_channel(client, conn, channel, client_entry, mode);
silc_client_unref_client(client, conn, client_entry);
if (!silc_buffer_pull(&client_id_list, idp_len))
/* Get channel public key list */
tmp = silc_argument_get_arg_type(args, 16, &len);
if (tmp)
- chpks = silc_argument_list_parse_decoded(tmp, len,
- SILC_ARGUMENT_PUBLIC_KEY);
+ channel->channel_pubkeys =
+ silc_argument_list_parse_decoded(tmp, len, SILC_ARGUMENT_PUBLIC_KEY);
/* Set current channel */
conn->current_channel = channel;
- cipher = (channel->internal.channel_key ?
- silc_cipher_get_name(channel->internal.channel_key) : NULL);
+ cipher = (channel->internal.send_key ?
+ silc_cipher_get_name(channel->internal.send_key) : NULL);
silc_hash_table_list(channel->user_list, &htl);
/* Notify application */
silc_client_command_callback(cmd, channel_name, channel, mode, &htl,
topic, cipher, hmac, channel->founder_key,
- chpks, channel->user_limit);
+ channel->channel_pubkeys, channel->user_limit);
- if (chpks)
- silc_argument_list_free(chpks, SILC_ARGUMENT_PUBLIC_KEY);
silc_hash_table_list_reset(&htl);
silc_client_unref_channel(client, conn, channel);
CHECK_STATUS("Cannot detach: ");
CHECK_ARGS(1, 1);
- /* Notify application */
- silc_client_command_callback(cmd);
-
-#if 0
- /* Generate the detachment data and deliver it to the client in the
- detach client operation */
+ /* Get detachment data */
detach = silc_client_get_detach_data(client, conn);
- if (detach) {
- client->internal->ops->detach(client, conn, silc_buffer_data(detach),
- silc_buffer_len(detach));
- silc_buffer_free(detach);
+ if (!detach) {
+ ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT);
+ goto out;
}
-#endif /* 0 */
+ /* Notify application */
+ silc_client_command_callback(cmd, detach);
+ silc_buffer_free(detach);
+
+ out:
silc_fsm_next(fsm, silc_client_command_reply_processed);
return SILC_FSM_CONTINUE;
}
}
/* Remove us from this channel. */
- silc_client_remove_from_channel(channel, conn->local_entry);
+ silc_client_remove_from_channel(client, conn, channel, conn->local_entry);
/* Notify application */
silc_client_command_callback(cmd, channel);
/* Now delete the channel. */
+ silc_client_empty_channel(client, conn, channel);
silc_client_del_channel(client, conn, channel);
out:
SilcUInt16 idp_len, mode;
SilcHashTableList htl;
SilcBufferStruct client_id_list, client_mode_list;
- SilcChannelEntry channel;
+ SilcChannelEntry channel = NULL;
SilcClientEntry client_entry;
SilcID id;
int i;
/* 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));
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)
- silc_client_add_to_channel(channel, client_entry, mode);
+ silc_client_add_to_channel(client, conn, channel, client_entry, mode);
silc_client_unref_client(client, conn, client_entry);
if (!silc_buffer_pull(&client_id_list, idp_len))
silc_hash_table_list_reset(&htl);
out:
+ silc_client_unref_channel(client, conn, channel);
silc_fsm_next(fsm, silc_client_command_reply_processed);
return SILC_FSM_CONTINUE;
}
/* 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: