stuff that relates to the
channel. Not used for the
channel resolving itself. */
- SilcAtomic8 refcnt; /* Reference counter */
+ SilcAtomic16 refcnt; /* Reference counter */
} SilcChannelEntryInternal;
/* Internal server entry context */
SILC_LOG_DEBUG(("Sending channel message"));
- if (!client || !conn || !channel)
+ if (silc_unlikely(!client || !conn || !channel))
return FALSE;
- if (flags & SILC_MESSAGE_FLAG_SIGNED && !hash)
+ if (silc_unlikely(flags & SILC_MESSAGE_FLAG_SIGNED && !hash))
return FALSE;
- if (conn->internal->disconnected)
+ if (silc_unlikely(conn->internal->disconnected))
return FALSE;
chu = silc_client_on_channel(channel, conn->local_entry);
- if (!chu) {
+ if (silc_unlikely(!chu)) {
client->internal->ops->say(conn->client, conn,
SILC_CLIENT_MESSAGE_AUDIT,
"Cannot talk to channel: not joined");
}
/* Check if it is allowed to send messages to this channel by us. */
- if (channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS && !chu->mode)
+ if (silc_unlikely(channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS &&
+ !chu->mode))
return FALSE;
- if (channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS &&
- chu->mode & SILC_CHANNEL_UMODE_CHANOP &&
- !(chu->mode & SILC_CHANNEL_UMODE_CHANFO))
+ if (silc_unlikely(channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS &&
+ chu->mode & SILC_CHANNEL_UMODE_CHANOP &&
+ !(chu->mode & SILC_CHANNEL_UMODE_CHANFO)))
return FALSE;
- if (chu->mode & SILC_CHANNEL_UMODE_QUIET)
+ if (silc_unlikely(chu->mode & SILC_CHANNEL_UMODE_QUIET))
return FALSE;
/* Take the key to be used */
hmac = channel->internal.hmac;
}
- if (!cipher || !hmac) {
+ if (silc_unlikely(!cipher || !hmac)) {
SILC_LOG_ERROR(("No cipher and HMAC for channel"));
return FALSE;
}
buffer = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE,
cipher, hmac, client->rng, NULL,
conn->private_key, hash, NULL);
- if (!buffer) {
+ if (silc_unlikely(!buffer)) {
SILC_LOG_ERROR(("Error encoding channel message"));
return FALSE;
}
SILC_LOG_DEBUG(("Received channel message"));
- if (packet->dst_id_type != SILC_ID_CHANNEL) {
+ if (silc_unlikely(packet->dst_id_type != SILC_ID_CHANNEL)) {
/** Invalid packet */
silc_fsm_next(fsm, silc_client_channel_message_error);
return SILC_FSM_CONTINUE;
}
- if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
- &remote_id, sizeof(remote_id))) {
+ if (silc_unlikely(!silc_id_str2id(packet->src_id,
+ packet->src_id_len, SILC_ID_CLIENT,
+ &remote_id, sizeof(remote_id)))) {
/** Invalid source ID */
silc_fsm_next(fsm, silc_client_channel_message_error);
return SILC_FSM_CONTINUE;
/* NOT REACHED */
}
- if (!silc_id_str2id(packet->dst_id, packet->dst_id_len, SILC_ID_CHANNEL,
- &channel_id, sizeof(channel_id))) {
+ if (silc_unlikely(!silc_id_str2id(packet->dst_id, packet->dst_id_len,
+ SILC_ID_CHANNEL, &channel_id,
+ sizeof(channel_id)))) {
/** Invalid destination ID */
silc_fsm_next(fsm, silc_client_channel_message_error);
return SILC_FSM_CONTINUE;
/* Find the channel */
channel = silc_client_get_channel_by_id(client, conn, &channel_id);
- if (!channel) {
+ if (silc_unlikely(!channel)) {
/** Unknown channel */
silc_fsm_next(fsm, silc_client_channel_message_error);
return SILC_FSM_CONTINUE;
}
/* Check that user is on channel */
- if (!silc_client_on_channel(channel, client_entry)) {
+ if (silc_unlikely(!silc_client_on_channel(channel, client_entry))) {
/** User not on channel */
+ SILC_LOG_WARNING(("Message from user not on channel, client or "
+ "server bug"));
silc_fsm_next(fsm, silc_client_channel_message_error);
return SILC_FSM_CONTINUE;
}
/* If decryption failed and we have just performed channel key rekey
we will use the old key in decryption. If that fails too then we
cannot do more and will drop the packet. */
- if (!payload) {
+ if (silc_unlikely(!payload)) {
SilcCipher cipher;
SilcHmac hmac;
SilcChannelID id;
SilcChannelKeyPayload payload;
+ SILC_LOG_DEBUG(("New channel key"));
+
payload = silc_channel_key_payload_parse(silc_buffer_data(key_payload),
silc_buffer_len(key_payload));
if (!payload)
if (!channel) {
channel = silc_client_get_channel_by_id(client, conn, &id);
if (!channel) {
+ SILC_LOG_DEBUG(("Key for unknown channel"));
silc_channel_key_payload_free(payload);
return FALSE;
}
/* Set the cipher key */
key = silc_channel_key_get_key(payload, &tmp_len);
- silc_cipher_set_key(channel->internal.channel_key, key, tmp_len * 8);
+ silc_cipher_set_key(channel->internal.channel_key, key, tmp_len * 8, TRUE);
/* Get channel HMAC */
hmac = (channel->internal.hmac ?
}
entry->name = name ? strdup(name) : NULL;
- /* Allocate the cipher and set the key*/
+ /* Allocate the cipher and set the key */
if (!silc_cipher_alloc(cipher, &entry->cipher)) {
silc_free(entry);
silc_free(entry->name);
return FALSE;
}
silc_cipher_set_key(entry->cipher, keymat->send_enc_key,
- keymat->enc_key_len);
+ keymat->enc_key_len, TRUE);
/* Generate HMAC key from the channel key data and set it */
if (!silc_hmac_alloc(hmac, NULL, &entry->hmac)) {
client_entry->mode = mode;
}
+/* Change a client's nickname */
+
+SilcBool silc_client_change_nickname(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ const char *new_nick,
+ SilcClientID *new_id,
+ const unsigned char *idp,
+ SilcUInt32 idp_len)
+{
+ char *tmp;
+
+ SILC_LOG_DEBUG(("Change nickname %s to %s", client_entry->nickname,
+ new_nick));
+
+ /* Normalize nickname */
+ tmp = silc_identifier_check(new_nick, strlen(new_nick),
+ SILC_STRING_UTF8, 128, NULL);
+ if (!tmp)
+ return FALSE;
+
+ /* Update the client entry */
+ silc_mutex_lock(conn->internal->lock);
+ if (!silc_idcache_update_by_context(conn->internal->client_cache,
+ client_entry, new_id, tmp, TRUE)) {
+ silc_free(tmp);
+ silc_mutex_unlock(conn->internal->lock);
+ return FALSE;
+ }
+ silc_mutex_unlock(conn->internal->lock);
+
+ memset(client_entry->nickname, 0, sizeof(client_entry->nickname));
+ memcpy(client_entry->nickname, new_nick, strlen(new_nick));
+ client_entry->nickname_normalized = tmp;
+ silc_client_nickname_format(client, conn, client_entry);
+
+ /* For my client entry, update ID and set new ID to packet stream */
+ if (client_entry == conn->local_entry) {
+ if (idp && idp_len) {
+ silc_buffer_enlarge(conn->internal->local_idp, idp_len);
+ silc_buffer_put(conn->internal->local_idp, idp, idp_len);
+ }
+ if (new_id)
+ silc_packet_set_ids(conn->stream, SILC_ID_CLIENT, conn->local_id,
+ 0, NULL);
+ }
+
+ return TRUE;
+}
+
/* Deletes the client entry and frees all memory. */
void silc_client_del_client_entry(SilcClient client,
if (!channel)
return NULL;
- silc_atomic_init8(&channel->internal.refcnt, 0);
+ silc_atomic_init16(&channel->internal.refcnt, 0);
channel->id = *channel_id;
channel->mode = mode;
if (!channel)
return FALSE;
- if (silc_atomic_sub_int8(&channel->internal.refcnt, 1) > 0)
+ if (silc_atomic_sub_int16(&channel->internal.refcnt, 1) > 0)
return FALSE;
SILC_LOG_DEBUG(("Deleting channel %p", channel));
silc_hmac_free(hmac);
silc_dlist_uninit(channel->internal.old_hmacs);
}
+ if (channel->channel_pubkeys)
+ silc_argument_list_free(channel->channel_pubkeys,
+ SILC_ARGUMENT_PUBLIC_KEY);
silc_client_del_channel_private_keys(client, conn, channel);
- silc_atomic_uninit8(&channel->internal.refcnt);
+ silc_atomic_uninit16(&channel->internal.refcnt);
silc_schedule_task_del_by_context(conn->client->schedule, channel);
silc_free(channel);
void silc_client_ref_channel(SilcClient client, SilcClientConnection conn,
SilcChannelEntry channel_entry)
{
- silc_atomic_add_int8(&channel_entry->internal.refcnt, 1);
+ silc_atomic_add_int16(&channel_entry->internal.refcnt, 1);
SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
- silc_atomic_get_int8(&channel_entry->internal.refcnt) - 1,
- silc_atomic_get_int8(&channel_entry->internal.refcnt)));
+ silc_atomic_get_int16(&channel_entry->internal.refcnt) - 1,
+ silc_atomic_get_int16(&channel_entry->internal.refcnt)));
}
/* Release reference of channel entry */
{
if (channel_entry) {
SILC_LOG_DEBUG(("Channel %p refcnt %d->%d", channel_entry,
- silc_atomic_get_int8(&channel_entry->internal.refcnt),
- silc_atomic_get_int8(&channel_entry->internal.refcnt)
+ silc_atomic_get_int16(&channel_entry->internal.refcnt),
+ silc_atomic_get_int16(&channel_entry->internal.refcnt)
- 1));
silc_client_del_channel(client, conn, channel_entry);
}
const char *username,
const char *userinfo,
SilcUInt32 mode);
+SilcBool silc_client_change_nickname(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientEntry client_entry,
+ const char *new_nick,
+ SilcClientID *new_id,
+ const unsigned char *idp,
+ SilcUInt32 idp_len);
void silc_client_del_client_entry(SilcClient client,
SilcClientConnection conn,
SilcClientEntry client_entry);
/* Internal context for conn->internal in SilcClientConnection. */
struct SilcClientConnectionInternalStruct {
- SilcIDCacheEntry local_entry; /* Local client cache entry */
SilcClientConnectionParams params; /* Connection parameters */
-
SilcFSMStruct fsm; /* Connection FSM */
SilcFSMThreadStruct event_thread; /* FSM thread for events */
SilcFSMSemaStruct wait_event; /* Event signaller */
}
if (!silc_notify_get_args(payload)) {
- SILC_LOG_DEBUG(("Malformed notify"));
+ SILC_LOG_DEBUG(("Malformed notify %d", silc_notify_get_type(payload)));
silc_notify_payload_free(payload);
silc_packet_free(packet);
return SILC_FSM_FINISH;
/* Get signoff message */
tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
- if (tmp_len > 128)
+ if (tmp && tmp_len > 128)
tmp[128] = '\0';
/* Notify application */
SilcNotifyType type = silc_notify_get_type(payload);
SilcArgumentPayload args = silc_notify_get_args(payload);
SilcClientEntry client_entry = NULL;
- unsigned char *tmp, *nick, oldnick[128 + 1];
+ unsigned char *tmp, oldnick[128 + 1];
SilcUInt32 tmp_len;
SilcID id, id2;
goto out;
}
- /* Normalize nickname */
- nick = silc_identifier_check(tmp, tmp_len, SILC_STRING_UTF8, 128, NULL);
- if (!nick)
- goto out;
-
- /* Update nickname */
- silc_mutex_lock(conn->internal->lock);
- if (!silc_idcache_update_by_context(conn->internal->client_cache,
- client_entry, NULL, nick, TRUE)) {
- silc_free(nick);
- silc_mutex_unlock(conn->internal->lock);
- goto out;
- }
- silc_mutex_unlock(conn->internal->lock);
+ /* Change the nickname */
memcpy(oldnick, client_entry->nickname, sizeof(client_entry->nickname));
- memcpy(client_entry->nickname, tmp, tmp_len);
- client_entry->nickname_normalized = nick;
- silc_client_nickname_format(client, conn, client_entry);
+ if (!silc_client_change_nickname(client, conn, client_entry, tmp,
+ &id2.u.client_id, NULL, 0))
+ goto out;
/* Notify application */
NOTIFY(client, conn, type, client_entry, client_entry->nickname, oldnick);
chpks = silc_argument_list_parse_decoded(tmp, tmp_len,
SILC_ARGUMENT_PUBLIC_KEY);
+ /* XXX add to/remove from channel pubkeys channel->channel_pubkeys */
+
/* Notify application. */
NOTIFY(client, conn, type, id.type, entry, mode, cipher, hmac,
passphrase, channel->founder_key, chpks, channel);
SilcBuffer buffer;
SilcBool ret;
- if (!client || !conn || !client_entry)
+ if (silc_unlikely(!client || !conn || !client_entry))
return FALSE;
- if (flags & SILC_MESSAGE_FLAG_SIGNED && !hash)
+ if (silc_unlikely(flags & SILC_MESSAGE_FLAG_SIGNED && !hash))
return FALSE;
- if (conn->internal->disconnected)
+ if (silc_unlikely(conn->internal->disconnected))
return FALSE;
SILC_LOG_DEBUG(("Sending private message"));
client_entry->internal.hmac_send,
client->rng, NULL, conn->private_key,
hash, NULL);
- if (!buffer) {
+ if (silc_unlikely(!buffer)) {
SILC_LOG_ERROR(("Error encoding private message"));
return FALSE;
}
SILC_LOG_DEBUG(("Received private message"));
- if (packet->src_id_type != SILC_ID_CLIENT) {
+ if (silc_unlikely(packet->src_id_type != SILC_ID_CLIENT)) {
/** Invalid packet */
silc_fsm_next(fsm, silc_client_private_message_error);
return SILC_FSM_CONTINUE;
}
- if (!silc_id_str2id(packet->src_id, packet->src_id_len, SILC_ID_CLIENT,
- &remote_id, sizeof(remote_id))) {
+ if (silc_unlikely(!silc_id_str2id(packet->src_id, packet->src_id_len,
+ SILC_ID_CLIENT, &remote_id,
+ sizeof(remote_id)))) {
/** Invalid source ID */
silc_fsm_next(fsm, silc_client_private_message_error);
return SILC_FSM_CONTINUE;
/* NOT REACHED */
}
- if (packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
- !remote_client->internal.receive_key &&
- !remote_client->internal.hmac_receive)
+ if (silc_unlikely(packet->flags & SILC_PACKET_FLAG_PRIVMSG_KEY &&
+ !remote_client->internal.receive_key &&
+ !remote_client->internal.hmac_receive))
goto out;
/* Parse the payload and decrypt it also if private message key is set */
remote_client->internal.receive_key,
remote_client->internal.hmac_receive,
NULL, FALSE, NULL);
- if (!payload)
+ if (silc_unlikely(!payload))
goto out;
#if 0 /* We need to rethink this. This doesn't work with multiple
if (client_entry->internal.prv_resp) {
silc_cipher_set_key(client_entry->internal.send_key,
keymat->receive_enc_key,
- keymat->enc_key_len);
+ keymat->enc_key_len, TRUE);
silc_cipher_set_iv(client_entry->internal.send_key,
keymat->receive_iv);
silc_cipher_set_key(client_entry->internal.receive_key,
keymat->send_enc_key,
- keymat->enc_key_len);
+ keymat->enc_key_len, FALSE);
silc_cipher_set_iv(client_entry->internal.receive_key, keymat->send_iv);
silc_hmac_set_key(client_entry->internal.hmac_send,
keymat->receive_hmac_key,
} else {
silc_cipher_set_key(client_entry->internal.send_key,
keymat->send_enc_key,
- keymat->enc_key_len);
+ keymat->enc_key_len, TRUE);
silc_cipher_set_iv(client_entry->internal.send_key,
keymat->send_iv);
silc_cipher_set_key(client_entry->internal.receive_key,
keymat->receive_enc_key,
- keymat->enc_key_len);
+ keymat->enc_key_len, FALSE);
silc_cipher_set_iv(client_entry->internal.receive_key, keymat->receive_iv);
silc_hmac_set_key(client_entry->internal.hmac_send,
keymat->send_hmac_key,
SilcClientConnection conn;
SilcBufferStruct detach;
char *nickname;
- SilcClientID client_id;
SilcUInt32 channel_count;
- SilcUInt32 *cmd_idents;
- SilcUInt32 cmd_idents_count;
- SilcBool success;
} *SilcClientResumeSession;
/************************ Static utility functions **************************/
return TRUE;
}
+/* Function used to call command replies back to application in resuming. */
+
+static void
+silc_client_resume_command_callback(SilcClient client,
+ SilcClientConnection conn,
+ SilcCommand command, ...)
+{
+ va_list ap;
+ va_start(ap, command);
+ client->internal->ops->command_reply(client, conn, command,
+ SILC_STATUS_OK, SILC_STATUS_OK, ap);
+ va_end(ap);
+}
+
+
/****************************** NEW_ID packet *******************************/
/* Received new ID packet from server during registering to SILC network */
if (!conn->local_entry)
goto out;
- /* Save the ID */
+ /* Save the ID. Take reference to conn->local_id. */
conn->local_id = &conn->local_entry->id;
conn->internal->local_idp = silc_buffer_copy(&packet->buffer);
- /* Save cache entry */
- silc_mutex_lock(conn->internal->lock);
- if (!silc_idcache_find_by_id_one(conn->internal->client_cache,
- conn->local_id,
- &conn->internal->local_entry)) {
- silc_mutex_unlock(conn->internal->lock);
- goto out;
- }
- silc_mutex_unlock(conn->internal->lock);
-
/* Save remote ID */
if (packet->src_id_len) {
conn->internal->remote_idp =
SilcBuffer auth;
unsigned char *id;
SilcUInt16 id_len;
+ SilcClientID client_id;
int ret;
SILC_LOG_DEBUG(("Resuming detached session"));
SILC_STR_END);
if (ret < 0) {
/** Malformed detach data */
+ SILC_LOG_DEBUG(("Malformed detachment data"));
silc_fsm_next(fsm, silc_client_st_resume_error);
return SILC_FSM_CONTINUE;
}
- if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &resume->client_id,
- sizeof(resume->client_id))) {
+ if (!silc_id_str2id(id, id_len, SILC_ID_CLIENT, &client_id,
+ sizeof(client_id))) {
/** Malformed ID */
+ SILC_LOG_DEBUG(("Malformed ID"));
silc_fsm_next(fsm, silc_client_st_resume_error);
return SILC_FSM_CONTINUE;
}
conn->private_key,
client->rng,
conn->internal->hash,
- &resume->client_id,
- SILC_ID_CLIENT);
+ &client_id, SILC_ID_CLIENT);
if (!auth) {
/** Out of memory */
silc_fsm_next(fsm, silc_client_st_resume_error);
silc_buffer_len(auth)),
SILC_STR_END)) {
/** Error sending packet */
+ SILC_LOG_DEBUG(("Error sending packet"));
silc_fsm_next(fsm, silc_client_st_resume_error);
return SILC_FSM_CONTINUE;
}
unsigned char **res_argv = NULL;
int i;
+ if (conn->internal->disconnected) {
+ /** Disconnected */
+ silc_fsm_next(fsm, silc_client_st_resume_error);
+ return SILC_FSM_CONTINUE;
+ }
+
if (!conn->local_id) {
/** Timeout, ID not received */
conn->internal->registering = FALSE;
return SILC_FSM_CONTINUE;
}
- /* First, send UMODE command to get our own user mode in the network */
+ /* Change our nickname */
+ silc_client_change_nickname(client, conn, conn->local_entry,
+ resume->nickname, NULL, NULL, 0);
+
+ /* Send UMODE command to get our own user mode in the network */
SILC_LOG_DEBUG(("Resolving user mode"));
silc_client_command_send(client, conn, SILC_COMMAND_UMODE,
silc_client_register_command_called, NULL,
1, 1, silc_buffer_data(conn->internal->local_idp),
silc_buffer_len(conn->internal->local_idp));
- /* Second, send IDENTIFY command for all channels we know about. These
- are the channels we've joined to according our detachment data. */
+ /* Send IDENTIFY command for all channels we know about. These are the
+ channels we've joined to according our detachment data. */
for (i = 0; i < resume->channel_count; i++) {
- SilcChannelID channel_id;
+ SilcChannelEntry channel;
unsigned char *chid;
SilcUInt16 chid_len;
SilcBuffer idp;
+ SilcChannelID channel_id;
+ char *name;
if (silc_buffer_unformat(&resume->detach,
SILC_STR_ADVANCE,
- SILC_STR_UI16_NSTRING(NULL, NULL),
+ SILC_STR_UI16_NSTRING(&name, NULL),
SILC_STR_UI16_NSTRING(&chid, &chid_len),
SILC_STR_UI_INT(NULL),
SILC_STR_END) < 0)
continue;
+ if (!silc_id_str2id(chid, chid_len, SILC_ID_CHANNEL, &channel_id,
+ sizeof(channel_id)))
+ continue;
idp = silc_id_payload_encode_data(chid, chid_len, SILC_ID_CHANNEL);
if (!idp)
continue;
+
+ /* Add the channel to cache */
+ channel = silc_client_get_channel_by_id(client, conn, &channel_id);
+ if (!channel)
+ silc_client_add_channel(client, conn, name, 0, &channel_id);
+
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));
return SILC_FSM_WAIT;
}
-/* Resolve joined channel modes. */
+/* Resolve joined channel modes, users and topics. */
SILC_FSM_STATE(silc_client_st_resume_resolve_cmodes)
{
SilcClientConnection conn = fsm_context;
+ SilcClient client = conn->client;
SilcClientResumeSession resume = state_context;
- SilcHashTableList htl;
- SilcChannelUser chu;
+ SilcIDCacheEntry entry;
+ SilcChannelEntry channel;
+ SilcList channels;
+ SilcBuffer idp;
+
+ if (conn->internal->disconnected) {
+ /** Disconnected */
+ silc_fsm_next(fsm, silc_client_st_resume_error);
+ return SILC_FSM_CONTINUE;
+ }
- SILC_LOG_DEBUG(("Resolving joined channel modes"));
+ SILC_LOG_DEBUG(("Resolving channel details"));
- silc_hash_table_list(conn->local_entry->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chu)) {
+ /** Wait for channel modes */
+ silc_fsm_next(fsm, silc_client_st_resume_completed);
+ if (!silc_idcache_get_all(conn->internal->channel_cache, &channels))
+ return SILC_FSM_CONTINUE;
+
+ /* Resolve channels' mode, users and topic */
+ resume->channel_count = silc_list_count(channels) * 3;
+ silc_list_start(channels);
+ while ((entry = silc_list_get(channels))) {
+ channel = entry->context;
+ idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
+ if (!idp)
+ continue;
+
+ silc_client_command_send(client, conn, SILC_COMMAND_CMODE,
+ silc_client_resume_continue, conn, 1,
+ 1, silc_buffer_data(idp),
+ silc_buffer_len(idp));
+ silc_client_command_send(client, conn, SILC_COMMAND_USERS,
+ silc_client_resume_continue, conn, 1,
+ 1, silc_buffer_data(idp),
+ silc_buffer_len(idp));
+ silc_client_command_send(client, conn, SILC_COMMAND_TOPIC,
+ silc_client_resume_continue, conn, 1,
+ 1, silc_buffer_data(idp),
+ silc_buffer_len(idp));
+ silc_buffer_free(idp);
}
- silc_hash_table_list_reset(&htl);
+
+ return SILC_FSM_WAIT;
+}
+
+/* Resuming completed */
+
+SILC_FSM_STATE(silc_client_st_resume_completed)
+{
+ SilcClientConnection conn = fsm_context;
+ SilcClient client = conn->client;
+ SilcClientResumeSession resume = state_context;
+ SilcIDCacheEntry entry;
+ SilcChannelEntry channel;
+ SilcList channels;
+
+ if (conn->internal->disconnected) {
+ /** Disconnected */
+ silc_fsm_next(fsm, silc_client_st_resume_error);
+ return SILC_FSM_CONTINUE;
+ }
+
+ if (resume->channel_count > 0) {
+ resume->channel_count--;
+ if (resume->channel_count)
+ return SILC_FSM_WAIT;
+ }
+
+ SILC_LOG_DEBUG(("Resuming completed"));
+
+ /* Issue IDENTIFY command for itself to get resolved hostname
+ correctly from server. */
+ silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
+ silc_client_register_command_called, NULL,
+ 1, 5, silc_buffer_data(conn->internal->local_idp),
+ silc_buffer_len(conn->internal->local_idp));
+
+ /* Issue INFO command to fetch the real server name and server
+ information and other stuff. */
+ silc_client_command_send(client, conn, SILC_COMMAND_INFO,
+ silc_client_register_command_called, NULL,
+ 1, 2, silc_buffer_data(conn->internal->remote_idp),
+ silc_buffer_len(conn->internal->remote_idp));
+
+ /* Call connection callback. We have now resumed to SILC network. */
+ conn->callback(client, conn, SILC_CLIENT_CONN_SUCCESS_RESUME, 0, NULL,
+ conn->callback_context);
+
+ /* Call NICK command reply. */
+ silc_client_resume_command_callback(client, conn, SILC_COMMAND_NICK,
+ conn->local_entry,
+ conn->local_entry->nickname,
+ &conn->local_entry->id);
+
+ /* Call JOIN command replies for all joined channel */
+ silc_idcache_get_all(conn->internal->channel_cache, &channels);
+ silc_list_start(channels);
+ while ((entry = silc_list_get(channels))) {
+ SilcHashTableList htl;
+ const char *cipher, *hmac;
+
+ channel = entry->context;
+ cipher = (channel->internal.channel_key ?
+ silc_cipher_get_name(channel->internal.channel_key) : NULL);
+ hmac = (channel->internal.hmac ?
+ silc_hmac_get_name(channel->internal.hmac) : NULL);
+ silc_hash_table_list(channel->user_list, &htl);
+ silc_client_resume_command_callback(client, conn, SILC_COMMAND_JOIN,
+ channel->channel_name, channel,
+ channel->mode, &htl, channel->topic,
+ cipher, hmac, channel->founder_key,
+ channel->channel_pubkeys,
+ channel->user_limit);
+ silc_hash_table_list_reset(&htl);
+ }
+
+ conn->internal->registering = FALSE;
+ silc_schedule_task_del_by_all(conn->internal->schedule, 0,
+ silc_client_connect_timeout, conn);
+ silc_free(resume->nickname);
+ silc_free(resume);
return SILC_FSM_FINISH;
}
+/* Error resuming to network */
+
SILC_FSM_STATE(silc_client_st_resume_error)
{
- /* XXX */
- /* Close connection */
+ SilcClientConnection conn = fsm_context;
+ SilcClient client = conn->client;
+ SilcClientResumeSession resume = state_context;
+
+ if (conn->internal->disconnected) {
+ if (resume) {
+ silc_free(resume->nickname);
+ silc_free(resume);
+ }
+ return SILC_FSM_FINISH;
+ }
+
+ SILC_LOG_DEBUG(("Error resuming to network"));
+
+ /* Signal to close connection */
+ if (!conn->internal->disconnected) {
+ conn->internal->disconnected = TRUE;
+ SILC_FSM_SEMA_POST(&conn->internal->wait_event);
+ }
+
+ /* Call connect callback */
+ if (conn->internal->callback_called)
+ conn->callback(client, conn, SILC_CLIENT_CONN_ERROR, 0, NULL,
+ conn->callback_context);
+ conn->internal->callback_called = TRUE;
+
+ silc_schedule_task_del_by_all(conn->internal->schedule, 0,
+ silc_client_connect_timeout, conn);
+
+ if (resume) {
+ silc_free(resume->nickname);
+ silc_free(resume);
+ }
return SILC_FSM_FINISH;
}
SilcBuffer detach;
SilcHashTableList htl;
SilcChannelUser chu;
+ unsigned char id[64];
+ SilcUInt32 id_len;
int ret, ch_count;
SILC_LOG_DEBUG(("Creating detachment data"));
ch_count = silc_hash_table_count(conn->local_entry->channels);
+ silc_id_id2str(conn->local_id, SILC_ID_CLIENT, id, sizeof(id), &id_len);
/* Save the nickname, Client ID and user mode in SILC network */
detach = silc_buffer_alloc(0);
SILC_STR_UI_SHORT(strlen(conn->local_entry->nickname)),
SILC_STR_DATA(conn->local_entry->nickname,
strlen(conn->local_entry->nickname)),
- SILC_STR_UI_SHORT(silc_buffer_len(conn->internal->
- local_idp)),
- SILC_STR_DATA(silc_buffer_data(conn->internal->
- local_idp),
- silc_buffer_len(conn->internal->
- local_idp)),
+ SILC_STR_UI_SHORT(id_len),
+ SILC_STR_DATA(id, id_len),
SILC_STR_UI_INT(conn->local_entry->mode),
SILC_STR_UI_INT(ch_count),
SILC_STR_END);
SILC_STR_DATA(chid, chid_len),
SILC_STR_UI_INT(chu->channel->mode),
SILC_STR_END);
- silc_free(chid);
}
silc_hash_table_list_reset(&htl);
SILC_FSM_STATE(silc_client_st_resume);
SILC_FSM_STATE(silc_client_st_resume_resolve_channels);
SILC_FSM_STATE(silc_client_st_resume_resolve_cmodes);
+SILC_FSM_STATE(silc_client_st_resume_completed);
SILC_FSM_STATE(silc_client_st_resume_error);
SilcBuffer silc_client_get_detach_data(SilcClient client,
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;
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;
}
}
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);
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) {
+ /* 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;
}
- /* Update the client entry */
- silc_mutex_lock(conn->internal->lock);
- if (!silc_idcache_update(conn->internal->client_cache,
- conn->internal->local_entry,
- &id.u.client_id, tmp, TRUE)) {
- silc_free(tmp);
- silc_mutex_unlock(conn->internal->lock);
- 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,
conn->local_entry->nickname, &old_client_id);
const char *cipher;
SilcBufferStruct client_id_list, client_mode_list, keyp;
SilcHashTableList htl;
- SilcDList chpks = NULL;
SilcID id;
int i;
/* 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;
/* 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;
}
void (*ftp)(SilcClient client, SilcClientConnection conn,
SilcClientEntry client_entry, SilcUInt32 session_id,
const char *hostname, SilcUInt16 port);
-
- /* Delivers SILC session detachment data indicated by `detach_data' to the
- application. If application has issued SILC_COMMAND_DETACH command
- the client session in the SILC network is not quit. The client remains
- in the network but is detached. The detachment data may be used later
- to resume the session in the SILC Network. The appliation is
- responsible of saving the `detach_data', to for example in a file.
-
- The detachment data can be given as argument to the functions
- silc_client_connect_to_server or silc_client_key_exchange when creating
- connection to remote host, inside SilcClientConnectionParams structure.
- If it is provided the client library will attempt to resume the session
- in the network. After the connection is created successfully, the
- application is responsible of setting the user interface for user into
- the same state it was before detaching (showing same channels, channel
- modes, etc). It can do this by fetching the information (like joined
- channels) from the client library. */
- void (*detach)(SilcClient client, SilcClientConnection conn,
- const unsigned char *detach_data,
- SilcUInt32 detach_data_len);
} SilcClientOperations;
/***/
client, but must be FALSE with server connections. */
SilcBool no_authentication;
- /* The SILC session detachment data that was returned by `detach' client
- operation when the application detached from the network. Application
- is responsible of saving the data and giving it as argument here
- for resuming the session in the SILC network.
-
- If this is provided here the client library will attempt to resume
- the session in the network. After the connection is created
- successfully, the application is responsible of setting the user
- interface for user into the same state it was before detaching (showing
- same channels, channel modes, etc). It can do this by fetching the
- information (like joined channels) from the client library. */
+ /* The SILC session detachment data that was returned in the `command_reply'
+ client operation for SILC_COMMAND_DETACH command. If this is provided
+ here the client library will attempt to resume the session in the network.
+ After the connection is created and the session has been resumed the
+ client will receive SILC_COMMAND_NICK command_reply for the client's
+ nickname in the network and SILC_COMMAND_JOIN command reply for all the
+ channels that the client has joined in the network. It may also receive
+ SILC_COMMAND_UMODE command reply to set user's mode on the network. */
unsigned char *detach_data;
SilcUInt32 detach_data_len;
char *channel_name; /* Channel name */
char *topic; /* Current topic, may be NULL */
SilcPublicKey founder_key; /* Founder key, may be NULL */
+ SilcDList channel_pubkeys; /* Channel public keys, may be NULL */
SilcChannelID id; /* Channel ID */
SilcUInt32 mode; /* Channel mode, ChannelModes. */
SilcUInt32 user_limit; /* User limit on channel */