SilcClientKeyAgreement ke; /* Current key agreement context or NULL */
/* Flags */
- unsigned int valid : 1; /* FALSE if this entry is not valid */
- unsigned int resolving : 1; /* TRUE when entry is being resolved */
+ unsigned int valid : 1; /* FALSE if this entry is not valid. Entry
+ without nickname is not valid. */
unsigned int generated : 1; /* TRUE if library generated `key' */
unsigned int prv_resp : 1; /* TRUE if we are responder when using
private message keys. */
SilcCipher cipher;
SilcHmac hmac;
SilcBool ret;
+ SilcID sid, rid;
SILC_LOG_DEBUG(("Sending channel message"));
}
/* Encode the message payload. This also encrypts the message payload. */
+ sid.type = SILC_ID_CLIENT;
+ sid.u.client_id = chu->client->id;
+ rid.type = SILC_ID_CHANNEL;
+ rid.u.channel_id = chu->channel->id;
buffer = silc_message_payload_encode(flags, data, data_len, TRUE, FALSE,
cipher, hmac, client->rng, NULL,
- conn->private_key, hash, NULL);
+ conn->private_key, hash, &sid, &rid,
+ NULL);
if (silc_unlikely(!buffer)) {
SILC_LOG_ERROR(("Error encoding channel message"));
return FALSE;
/* Get sender client entry */
client_entry = silc_client_get_client_by_id(client, conn, &remote_id);
- if (!client_entry || !client_entry->nickname[0]) {
+ if (!client_entry || !client_entry->internal.valid) {
/** Resolve client info */
silc_client_unref_client(client, conn, client_entry);
SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
payload = silc_message_payload_parse(silc_buffer_data(buffer),
silc_buffer_len(buffer), FALSE,
FALSE, channel->internal.receive_key,
- channel->internal.hmac, NULL,
- FALSE, NULL);
+ channel->internal.hmac,
+ packet->src_id, packet->src_id_len,
+ packet->dst_id, packet->dst_id_len,
+ NULL, FALSE, NULL);
/* 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
payload = silc_message_payload_parse(silc_buffer_data(buffer),
silc_buffer_len(buffer),
FALSE, FALSE, cipher, hmac,
+ packet->src_id,
+ packet->src_id_len,
+ packet->dst_id,
+ packet->dst_id_len,
NULL, FALSE, NULL);
if (payload)
break;
silc_buffer_len(buffer),
FALSE, FALSE,
channel->internal.receive_key,
- channel->internal.hmac, NULL,
- FALSE, NULL);
+ channel->internal.hmac,
+ packet->src_id,
+ packet->src_id_len,
+ packet->dst_id,
+ packet->dst_id_len,
+ NULL, FALSE, NULL);
if (!payload) {
silc_dlist_start(channel->internal.private_keys);
payload = silc_message_payload_parse(silc_buffer_data(buffer),
silc_buffer_len(buffer),
FALSE, FALSE, key->cipher,
- key->hmac, NULL, FALSE, NULL);
+ key->hmac, packet->src_id,
+ packet->src_id_len,
+ packet->dst_id,
+ packet->dst_id_len,
+ NULL, FALSE, NULL);
if (payload)
break;
}
return TRUE;
}
-/* Removes client from a channel. This handles entry locking internally. */
+/* Removes client from a channel. Returns FALSE if user is not on channel.
+ This handles entry locking internally. */
SilcBool silc_client_remove_from_channel(SilcClient client,
SilcClientConnection conn,
SilcClientConnection conn = fsm_context;
SilcClient client = conn->client;
+ /* Get SILC protocol version remote supports */
+ silc_ske_parse_version(conn->internal->ske, &conn->internal->remote_version,
+ NULL, NULL, NULL, NULL);
+
silc_ske_free(conn->internal->ske);
conn->internal->ske = NULL;
SilcClientConnection conn = context;
/* Signal to start rekey */
- conn->internal->rekey_responder = FALSE;
- conn->internal->rekeying = TRUE;
- SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
+ if (!silc_fsm_is_started(&conn->internal->event_thread)) {
+ conn->internal->rekey_responder = FALSE;
+ conn->internal->rekeying = TRUE;
+ SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
+ }
/* Reinstall rekey timer */
silc_schedule_task_add_timeout(conn->internal->schedule,
SilcDList clients;
SilcGetClientCallback completion;
void *context;
+ SilcClientEntry client_entry;
} *SilcClientGetClientInternal;
/* Resolving command callback */
if (error != SILC_STATUS_OK) {
SILC_LOG_DEBUG(("Resolving failed: %s", silc_get_status_message(error)));
+
+ if (i->client_entry) {
+ i->client_entry->internal.resolve_cmd_ident = 0;
+ silc_client_unref_client(client, conn, i->client_entry);
+ }
+
if (i->completion)
i->completion(client, conn, error, NULL, i->context);
goto out;
/* Deliver the clients to the caller */
if (i->completion) {
SILC_LOG_DEBUG(("Resolved %d clients", silc_dlist_count(i->clients)));
+
+ if (i->client_entry) {
+ i->client_entry->internal.resolve_cmd_ident = 0;
+ silc_client_unref_client(client, conn, i->client_entry);
+ }
+
silc_dlist_start(i->clients);
i->completion(client, conn, SILC_STATUS_OK, i->clients, i->context);
}
if (!cmd_ident && completion)
completion(client, conn, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL, context);
- if (client_entry && cmd_ident)
+ if (client_entry && cmd_ident) {
client_entry->internal.resolve_cmd_ident = cmd_ident;
+ i->client_entry = client_entry;
+ } else {
+ silc_client_unref_client(client, conn, client_entry);
+ }
- silc_client_unref_client(client, conn, client_entry);
silc_buffer_free(idp);
return cmd_ident;
silc_rwlock_alloc(&client_entry->internal.lock);
silc_atomic_init8(&client_entry->internal.refcnt, 0);
client_entry->id = *id;
- client_entry->internal.valid = TRUE;
client_entry->mode = mode;
client_entry->realname = userinfo ? strdup(userinfo) : NULL;
silc_parse_userfqdn(nickname, client_entry->nickname,
/* Format the nickname */
silc_client_nickname_format(client, conn, client_entry, FALSE);
+ if (client_entry->nickname[0])
+ client_entry->internal.valid = TRUE;
+
SILC_LOG_DEBUG(("Added %p", client_entry));
return client_entry;
client_entry, NULL, nick, TRUE);
silc_mutex_unlock(conn->internal->lock);
client_entry->nickname_normalized = nick;
+ client_entry->internal.valid = TRUE;
}
client_entry->mode = mode;
0, NULL);
}
+ client_entry->internal.valid = TRUE;
return TRUE;
}
return ret;
}
+/* Internal routine used to find client by ID and if not found this creates
+ new client entry and returns it. */
+
+SilcClientEntry silc_client_get_client(SilcClient client,
+ SilcClientConnection conn,
+ SilcClientID *client_id)
+{
+ SilcClientEntry client_entry;
+
+ client_entry = silc_client_get_client_by_id(client, conn, client_id);
+ if (!client_entry) {
+ client_entry = silc_client_add_client(client, conn, NULL, NULL, NULL,
+ client_id, 0);
+ if (!client_entry)
+ return NULL;
+ silc_client_ref_client(client, conn, client_entry);
+ }
+
+ return client_entry;
+}
+
/* Lock client */
void silc_client_lock_client(SilcClientEntry client_entry)
SilcDList clients;
SilcClientEntry entry, unformatted = NULL;
- SILC_LOG_DEBUG(("Format nickname"));
-
if (!client->internal->params->nickname_format[0])
return client_entry;
if (!client_entry->nickname[0])
return NULL;
+ SILC_LOG_DEBUG(("Format nickname"));
+
/* Get all clients with same nickname. Do not perform the formatting
if there aren't any clients with same nickname unless the application
is forcing us to do so. */
SilcClientEntry client_entry);
SilcBool silc_client_del_client(SilcClient client, SilcClientConnection conn,
SilcClientEntry client_entry);
-SilcClientEntry silc_idlist_get_client(SilcClient client,
+SilcClientEntry silc_client_get_client(SilcClient client,
SilcClientConnection conn,
- const char *nickname,
- const char *format,
- bool query);
+ SilcClientID *client_id);
SilcChannelEntry silc_client_add_channel(SilcClient client,
SilcClientConnection conn,
const char *channel_name,
SilcIDCache channel_cache; /* Channel entry cache */
SilcIDCache server_cache; /* Server entry cache */
+ SilcUInt32 remote_version; /* Remote SILC protocol version */
SilcAtomic16 cmd_ident; /* Current command identifier */
SilcUInt8 retry_count; /* Packet retry counter */
SilcUInt8 retry_timer; /* Packet retry timer */
/* Check whether we know this client already */
remote_client = silc_client_get_client_by_id(client, conn, &remote_id);
- if (!remote_client || !remote_client->nickname[0]) {
+ if (!remote_client || !remote_client->internal.valid) {
/** Resolve client info */
silc_client_unref_client(client, conn, remote_client);
SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
/* Notify processing context */
typedef struct {
- SilcPacket packet;
- SilcNotifyPayload payload;
- SilcFSMThread fsm;
- SilcChannelEntry channel;
+ SilcPacket packet; /* Notify packet */
+ SilcNotifyPayload payload; /* Parsed notify payload */
+ SilcFSMThread fsm; /* Notify FSM thread */
+ SilcChannelEntry channel; /* Channel entry being resolved */
+ SilcClientEntry client_entry; /* Client entry being resolved */
} *SilcClientNotify;
/************************ Static utility functions **************************/
-/* Entry resolving callback. This will continue processing the notify. */
+/* The client entires in notify processing are resolved if they do not exist,
+ or they are not valid. We go to this callback after resolving where we
+ check if the client entry has become valid. If resolving succeeded the
+ entry is valid but remains invalid if resolving failed. This callback
+ will continue processing the notify. We use this callback also with other
+ entry resolving. */
static void silc_client_notify_resolved(SilcClient client,
SilcClientConnection conn,
{
SilcClientNotify notify = context;
- /* If no entries found, just finish the notify processing, a silent error */
- if (!entries)
+ /* If entry is still invalid, resolving failed. Finish notify processing. */
+ if (notify->client_entry && !notify->client_entry->internal.valid) {
+ silc_fsm_next(notify->fsm, silc_client_notify_processed);
+ silc_client_unref_client(client, conn, notify->client_entry);
+ }
+
+ /* If no entries found, just finish the notify processing */
+ if (!entries && !notify->client_entry)
silc_fsm_next(notify->fsm, silc_client_notify_processed);
if (notify->channel) {
break;
}
- return SILC_FSM_YIELD;
+ return SILC_FSM_CONTINUE;
}
/* Notify processed, finish the packet processing thread */
/* Find Client entry and if not found query it */
client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
- if (!client_entry || !client_entry->nickname[0]) {
+ if (!client_entry || !client_entry->internal.valid) {
/** Resolve client */
silc_client_unref_client(client, conn, client_entry);
notify->channel = channel;
if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
goto out;
- /* Find Client entry and if not found query it */
- client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
- if (!client_entry || !client_entry->nickname[0] ||
+ /* Find client entry and if not found query it. If we just queried it
+ don't do it again, unless some data (like username) is missing. */
+ client_entry = notify->client_entry;
+ if (!client_entry)
+ client_entry = silc_client_get_client(client, conn, &id.u.client_id);
+ if (!client_entry || !client_entry->internal.valid ||
!client_entry->username[0]) {
/** Resolve client */
- silc_client_unref_client(client, conn, client_entry);
notify->channel = channel;
+ notify->client_entry = client_entry;
SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
silc_client_get_client_by_id_resolve(
- client, conn, &id.u.client_id, NULL,
- silc_client_notify_resolved,
+ client, conn, client_entry ?
+ &client_entry->id : &id.u.client_id,
+ NULL, silc_client_notify_resolved,
notify));
/* NOT REACHED */
}
goto out;
/* Remove client from channel */
- silc_client_remove_from_channel(client, conn, channel, client_entry);
+ if (!silc_client_remove_from_channel(client, conn, channel, client_entry))
+ goto out;
/* Notify application. */
NOTIFY(client, conn, type, client_entry, channel);
tmp[128] = '\0';
/* Notify application */
- NOTIFY(client, conn, type, client_entry, tmp);
+ if (client_entry->internal.valid)
+ NOTIFY(client, conn, type, client_entry, tmp);
/* Remove from channel */
if (packet->dst_id_type == SILC_ID_CHANNEL) {
}
/* Delete client */
+ client_entry->internal.valid = FALSE;
silc_client_del_client(client, conn, client_entry);
silc_client_unref_client(client, conn, client_entry);
/* NOT REACHED */
}
- /* Get ID */
+ /* Get ID of topic changer */
if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
goto out;
if (id.type == SILC_ID_CLIENT) {
/* Find Client entry */
- client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
- if (!client_entry || !client_entry->nickname[0]) {
- /** Resolve client */
- silc_client_unref_client(client, conn, client_entry);
- notify->channel = channel;
- SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
- silc_client_get_client_by_id_resolve(
+ client_entry = notify->client_entry;
+ if (!client_entry) {
+ client_entry = silc_client_get_client(client, conn, &id.u.client_id);
+ if (!client_entry || !client_entry->internal.valid) {
+ /** Resolve client */
+ notify->channel = channel;
+ notify->client_entry = client_entry;
+ SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+ silc_client_get_client_by_id_resolve(
client, conn, &id.u.client_id, NULL,
silc_client_notify_resolved,
notify));
- /* NOT REACHED */
+ /* NOT REACHED */
+ }
}
+
+ /* If client is not on channel, ignore this notify */
+ if (!silc_client_on_channel(channel, client_entry))
+ goto out;
+
entry = client_entry;
} else if (id.type == SILC_ID_SERVER) {
/* Find Server entry */
unsigned char *tmp, oldnick[128 + 1];
SilcUInt32 tmp_len;
SilcID id, id2;
+ SilcBool valid;
SILC_LOG_DEBUG(("Notify: NICK_CHANGE"));
SILC_ID_CLIENT_COMPARE(&id2.u.client_id, conn->local_id))
goto out;
- /* Find old Client entry */
+ /* Find old client entry. If we don't have the entry, we ignore this
+ notify. */
client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
- if (!client_entry || !client_entry->nickname[0]) {
- /** Resolve client */
- silc_client_unref_client(client, conn, client_entry);
- SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
- client, conn, &id.u.client_id, NULL,
- silc_client_notify_resolved,
- notify));
- /* NOT REACHED */
- }
+ if (!client_entry)
+ goto out;
+ valid = client_entry->internal.valid;
/* Take the new nickname */
tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
silc_rwlock_unlock(client_entry->internal.lock);
- /* Notify application */
- NOTIFY(client, conn, type, client_entry, client_entry->nickname, oldnick);
+ /* Notify application, if client entry is valid. We do not send nick change
+ notify for entries that were invalid (application doesn't know them). */
+ if (valid)
+ NOTIFY(client, conn, type, client_entry, oldnick, client_entry->nickname);
out:
/** Notify processed */
goto out;
SILC_GET32_MSB(mode, tmp);
- /* Get ID */
+ /* Get ID of who changed the mode */
if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
goto out;
if (id.type == SILC_ID_CLIENT) {
/* Find Client entry */
- client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
- if (!client_entry || !client_entry->nickname[0]) {
- /** Resolve client */
- silc_client_unref_client(client, conn, client_entry);
- notify->channel = channel;
- SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
- silc_client_get_client_by_id_resolve(
+ client_entry = notify->client_entry;
+ if (!client_entry) {
+ client_entry = silc_client_get_client(client, conn, &id.u.client_id);
+ if (!client_entry || !client_entry->internal.valid) {
+ /** Resolve client */
+ notify->channel = channel;
+ notify->client_entry = client_entry;
+ SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+ silc_client_get_client_by_id_resolve(
client, conn, &id.u.client_id, NULL,
silc_client_notify_resolved,
notify));
- /* NOT REACHED */
+ /* NOT REACHED */
+ }
}
+
+ /* If client is not on channel, ignore this notify */
+ if (!silc_client_on_channel(channel, client_entry))
+ goto out;
+
entry = client_entry;
} else if (id.type == SILC_ID_SERVER) {
/* Find Server entry */
/* Find target Client entry */
client_entry2 = silc_client_get_client_by_id(client, conn, &id2.u.client_id);
- if (!client_entry2 || !client_entry2->nickname[0]) {
+ if (!client_entry2 || !client_entry2->internal.valid) {
/** Resolve client */
silc_client_unref_client(client, conn, client_entry2);
SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
/* NOT REACHED */
}
+ /* If target client is not on channel, ignore this notify */
+ if (!silc_client_on_channel(channel, client_entry2))
+ goto out;
+
/* Get the mode */
tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
if (!tmp)
goto out;
SILC_GET32_MSB(mode, tmp);
- /* Get ID */
+ /* Get ID of mode changer */
if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
goto out;
if (id.type == SILC_ID_CLIENT) {
/* Find Client entry */
- client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
- if (!client_entry || !client_entry->nickname[0]) {
- /** Resolve client */
- silc_client_unref_client(client, conn, client_entry);
- notify->channel = channel;
- SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
- silc_client_get_client_by_id_resolve(
+ client_entry = notify->client_entry;
+ if (!client_entry) {
+ client_entry = silc_client_get_client(client, conn, &id.u.client_id);
+ if (!client_entry || !client_entry->internal.valid) {
+ /** Resolve client */
+ notify->channel = channel;
+ notify->client_entry = client_entry;
+ SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
+ silc_client_get_client_by_id_resolve(
client, conn, &id.u.client_id, NULL,
silc_client_notify_resolved,
notify));
- /* NOT REACHED */
+ /* NOT REACHED */
+ }
}
+
+ /* If client is not on channel, ignore this notify */
+ if (!silc_client_on_channel(channel, client_entry))
+ goto out;
+
entry = client_entry;
} else if (id.type == SILC_ID_SERVER) {
/* Find Server entry */
/* NOT REACHED */
}
- /* Get Client ID */
+ /* Get the kicked Client ID */
if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
goto out;
- /* Find Client entry */
+ /* Find client entry */
client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
if (!client_entry)
goto out;
/* Find kicker's client entry and if not found resolve it */
client_entry2 = silc_client_get_client_by_id(client, conn, &id.u.client_id);
- if (!client_entry2 || !client_entry2->nickname[0]) {
+ if (!client_entry2 || !client_entry2->internal.valid) {
/** Resolve client */
silc_client_unref_client(client, conn, client_entry);
silc_client_unref_client(client, conn, client_entry2);
tmp = silc_argument_get_arg_type(args, 2, &tmp_len);
/* Remove kicked client from channel */
- if (client_entry != conn->local_entry)
- silc_client_remove_from_channel(client, conn, channel, client_entry);
+ if (client_entry != conn->local_entry) {
+ if (!silc_client_remove_from_channel(client, conn, channel, client_entry))
+ goto out;
+ }
/* Notify application. */
NOTIFY(client, conn, type, client_entry, tmp, client_entry2, channel);
/* Find Client entry */
client_entry2 = silc_client_get_client_by_id(client, conn,
&id.u.client_id);
- if (!client_entry2 || !client_entry2->nickname[0]) {
+ if (!client_entry2 || !client_entry2->internal.valid) {
/** Resolve client */
silc_client_unref_client(client, conn, client_entry);
silc_client_unref_client(client, conn, client_entry2);
/* Delete the killed client */
if (client_entry != conn->local_entry) {
silc_client_remove_from_channels(client, conn, client_entry);
+ client_entry->internal.valid = FALSE;
silc_client_del_client(client, conn, client_entry);
}
/* Get the client entry */
client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
- if (client_entry)
+ if (client_entry && client_entry->internal.valid)
silc_dlist_add(clients, client_entry);
}
silc_dlist_start(clients);
while ((client_entry = silc_dlist_get(clients))) {
silc_client_remove_from_channels(client, conn, client_entry);
+ client_entry->internal.valid = FALSE;
silc_client_del_client(client, conn, client_entry);
}
if (!silc_argument_get_decoded(args, 1, SILC_ARGUMENT_ID, &id, NULL))
goto out;
- /* Find Client entry and if not found resolve it */
+ /* Find client entry and if not found resolve it */
client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
- if (!client_entry || !client_entry->nickname[0]) {
+ if (!client_entry || !client_entry->internal.valid) {
/** Resolve client */
silc_client_unref_client(client, conn, client_entry);
SILC_FSM_CALL(silc_client_get_client_by_id_resolve(
{
SilcBuffer buffer;
SilcBool ret;
+ SilcID sid, rid;
if (silc_unlikely(!client || !conn || !client_entry))
return FALSE;
SILC_LOG_DEBUG(("Sending private message"));
+ sid.type = SILC_ID_CLIENT;
+ sid.u.client_id = conn->local_entry->id;
+ rid.type = SILC_ID_CLIENT;
+ rid.u.client_id = client_entry->id;
+
/* Encode private message payload */
buffer =
silc_message_payload_encode(flags, data, data_len,
TRUE, client_entry->internal.send_key,
client_entry->internal.hmac_send,
client->rng, NULL, conn->private_key,
- hash, NULL);
+ hash, &sid, &rid, NULL);
if (silc_unlikely(!buffer)) {
SILC_LOG_ERROR(("Error encoding private message"));
return FALSE;
TRUE, !remote_client->internal.generated,
remote_client->internal.receive_key,
remote_client->internal.hmac_receive,
+ packet->src_id, packet->src_id_len,
+ packet->dst_id, packet->dst_id_len,
NULL, FALSE, NULL);
if (silc_unlikely(!payload))
goto out;
TRUE, !client_entry->internal.generated,
client_entry->internal.receive_key,
client_entry->internal.hmac_receive,
+ packet->src_id, packet->src_id_len,
+ packet->dst_id, packet->dst_id_len,
NULL, FALSE, NULL);
if (!(*payload)) {
silc_packet_free(packet);
SilcDList clients,
void *context)
{
- SilcPacket packet = context;
+ SilcFSMThread thread = context;
+ SilcPacket packet = silc_fsm_get_state_context(thread);
unsigned char *cipher = NULL, *hmac = NULL;
SilcClientEntry client_entry;
int ret;
if (!clients) {
silc_packet_free(packet);
+ silc_fsm_finish(thread);
return;
}
silc_free(cipher);
silc_free(hmac);
silc_packet_free(packet);
+ silc_fsm_finish(thread);
}
/* Processes incoming Private Message Key payload to indicate that the
SILC_FSM_STATE(silc_client_command_nick)
{
- SilcClientCommandContext cmd = fsm_context;
+ SilcClientCommandContext cmd2, cmd = fsm_context;
SilcClientConnection conn = cmd->conn;
if (cmd->argc < 2) {
goto out;
}
+ /* If JOIN command is active, wait for it to finish before sending NICK.
+ To avoid problems locally with changing IDs while joining, we do this. */
+ silc_mutex_lock(conn->internal->lock);
+ silc_list_start(conn->internal->pending_commands);
+ while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
+ if (cmd2->cmd == SILC_COMMAND_JOIN) {
+ silc_mutex_unlock(conn->internal->lock);
+ silc_fsm_next_later(fsm, silc_client_command_nick, 0, 300000);
+ return SILC_FSM_WAIT;
+ }
+ }
+ silc_mutex_unlock(conn->internal->lock);
+
if (cmd->argv_lens[1] > 128)
cmd->argv_lens[1] = 128;
SILC_FSM_STATE(silc_client_command_join)
{
- SilcClientCommandContext cmd = fsm_context;
+ SilcClientCommandContext cmd2, cmd = fsm_context;
SilcClientConnection conn = cmd->conn;
SilcClient client = conn->client;
SilcChannelEntry channel = NULL;
if (channel && silc_client_on_channel(channel, conn->local_entry))
goto out;
+ /* If NICK command is active, wait for it to finish before sending JOIN.
+ To avoid problems locally with changing IDs while joining, we do this. */
+ silc_mutex_lock(conn->internal->lock);
+ silc_list_start(conn->internal->pending_commands);
+ while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
+ if (cmd2->cmd == SILC_COMMAND_NICK) {
+ silc_mutex_unlock(conn->internal->lock);
+ silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
+ return SILC_FSM_WAIT;
+ }
+ }
+ silc_mutex_unlock(conn->internal->lock);
+
if (cmd->argv_lens[1] > 256)
cmd->argv_lens[1] = 256;
/* Get client entry */
client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
- if (!client_entry)
+ if (!client_entry || !client_entry->internal.valid)
continue;
/* Join client to the channel */
/* 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) {
+ 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);
Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 2006 Pekka Riikonen
+ Copyright (C) 2006 - 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
* before reading any data from the `client_entry'. The lock must be
* unlocked with silc_client_unlock_client.
*
+ * NOTES
+ *
* The entry must be unlocked before calling any Client Library API
- * functions where the entry is given as argument.
+ * functions where the entry is given as argument, unless otherwise stated.
+ *
+ * The entry should not be locked for long periods of time. For example,
+ * it is not appropriate to hold the lock while waiting user interface to
+ * be drawn. The appropriate way is to read the data and duplicate it if
+ * necessary, unlock the entry, then draw on the user interface.
*
- * This function is not needed if application is not multithreaded
+ * This function is not needed if application is not multithreaded.
*
***/
void silc_client_lock_client(SilcClientEntry client_entry);
* after it is not needed anymore. Returns `client_entry'.
*
***/
-SilcClientEntry silc_client_ref_client(SilcClient client,
+SilcClientEntry silc_client_ref_client(SilcClient client,
SilcClientConnection conn,
SilcClientEntry client_entry);
* before reading any data from the `channel_entry'. The lock must be
* unlocked with silc_client_unlock_channel.
*
+ * NOTES
+ *
* The entry must be unlocked before calling any Client Library API
- * functions where the entry is given as argument.
+ * functions where the entry is given as argument, unless otherwise stated.
*
- * This function is not needed if application is not multithreaded
+ * The entry should not be locked for long periods of time. For example,
+ * it is not appropriate to hold the lock while waiting user interface to
+ * be drawn. The appropriate way is to read the data and duplicate it if
+ * necessary, unlock the entry, then draw on the user interface.
+ *
+ * This function is not needed if application is not multithreaded.
*
***/
void silc_client_lock_channel(SilcChannelEntry channel_entry);
* after it is not needed anymore. Returns `channel_entry'.
*
***/
-SilcChannelEntry silc_client_ref_channel(SilcClient client,
+SilcChannelEntry silc_client_ref_channel(SilcClient client,
SilcClientConnection conn,
SilcChannelEntry channel_entry);
* before reading any data from the `server_entry'. The lock must be
* unlocked with silc_client_unlock_server.
*
+ * NOTES
+ *
* The entry must be unlocked before calling any Client Library API
- * functions where the entry is given as argument.
+ * functions where the entry is given as argument, unless otherwise stated.
+ *
+ * The entry should not be locked for long periods of time. For example,
+ * it is not appropriate to hold the lock while waiting user interface to
+ * be drawn. The appropriate way is to read the data and duplicate it if
+ * necessary, unlock the entry, then draw on the user interface.
*
- * This function is not needed if application is not multithreaded
+ * This function is not needed if application is not multithreaded.
*
***/
void silc_client_lock_server(SilcServerEntry server_entry);
* after it is not needed anymore. Returns `server_entry'.
*
***/
-SilcServerEntry silc_client_ref_server(SilcClient client,
+SilcServerEntry silc_client_ref_server(SilcClient client,
SilcClientConnection conn,
SilcServerEntry server_entry);