X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=lib%2Fsilcclient%2Fcommand_reply.c;h=ede887535fdcdc97b6219d0c8d73f11b45867021;hp=9eea6bf1a53177cbb896377b39304d77b5896524;hb=805fddcf6431e784f9f77114782a90c9d12f9cbe;hpb=d4938a564e358e8072c30290305a68cef665eaa0 diff --git a/lib/silcclient/command_reply.c b/lib/silcclient/command_reply.c index 9eea6bf1..ede88753 100644 --- a/lib/silcclient/command_reply.c +++ b/lib/silcclient/command_reply.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - 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 @@ -25,27 +25,29 @@ /************************** 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_client_command_callback(cmd, arg1, arg2); \ + 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, \ + 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_process); \ - return SILC_FSM_YIELD; \ + silc_fsm_next(fsm, silc_client_command_reply_processed); \ + return SILC_FSM_CONTINUE; \ } /* Check for correct arguments */ @@ -53,8 +55,8 @@ do { \ 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 @@ -67,6 +69,7 @@ static inline void silc_client_command_callback(SilcClientCommandContext cmd, ...) { SilcClientCommandReplyCallback cb; + SilcList list; va_list ap, cp; va_start(ap, cmd); @@ -81,12 +84,13 @@ silc_client_command_callback(SilcClientCommandContext cmd, ...) } /* 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_va_copy(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); } @@ -102,19 +106,54 @@ static void silc_client_command_process_error(SilcClientCommandContext cmd, 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); + 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; + + /* 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; } } @@ -135,10 +174,8 @@ SILC_FSM_STATE(silc_client_command_reply) payload = silc_command_payload_parse(silc_buffer_datalen(&packet->buffer)); silc_packet_free(packet); if (!payload) { - /** Bad reply payload */ SILC_LOG_DEBUG(("Bad command reply packet")); - silc_fsm_next(fsm, silc_client_connection_st_packet); - return SILC_FSM_CONTINUE; + return SILC_FSM_FINISH; } cmd_ident = silc_command_get_ident(payload); @@ -147,27 +184,31 @@ SILC_FSM_STATE(silc_client_command_reply) /* 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_ident == cmd_ident) + 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) { - /** Unknown command reply */ - 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); - silc_fsm_next(fsm, silc_client_connection_st_packet); - return SILC_FSM_CONTINUE; + return SILC_FSM_FINISH; } - /* Signal command thread that command reply has arrived */ + /* 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); - /** Packet processed */ - silc_fsm_next(fsm, silc_client_connection_st_packet); - return SILC_FSM_CONTINUE; + return SILC_FSM_FINISH; } /* Wait here for command reply to arrive from remote host */ @@ -179,42 +220,44 @@ SILC_FSM_STATE(silc_client_command_reply_wait) SILC_LOG_DEBUG(("Wait for command reply")); /** Wait for command reply */ - cmd->processed = FALSE; silc_fsm_set_state_context(fsm, NULL); - silc_fsm_next_later(fsm, silc_client_command_reply_process, 20, 0); + silc_fsm_next_later(fsm, silc_client_command_reply_timeout, + cmd->cmd != SILC_COMMAND_PING ? 40 : 60, 0); return SILC_FSM_WAIT; } -/* Process received command reply payload */ +/* Timeout occurred while waiting command reply */ -SILC_FSM_STATE(silc_client_command_reply_process) +SILC_FSM_STATE(silc_client_command_reply_timeout) { SilcClientCommandContext cmd = fsm_context; - SilcCommandPayload payload = state_context; + SilcClientConnection conn = cmd->conn; + SilcArgumentPayload args = NULL; - if (!payload) { - /* Timeout, reply not received in timely fashion */ - SilcArgumentPayload args = NULL; - ERROR_CALLBACK(SILC_STATUS_ERR_TIMEDOUT); + 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 (cmd->processed) { - /* Command reply processed */ - silc_command_payload_free(payload); + SILC_LOG_DEBUG(("Command %s timeout", silc_get_command_name(cmd->cmd))); - if (cmd->status == SILC_STATUS_OK || cmd->status == SILC_STATUS_LIST_END || - SILC_STATUS_IS_ERROR(cmd->status)) - return SILC_FSM_FINISH; + /* 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; +} - /** Wait more command payloads */ - silc_fsm_next(fsm, silc_client_command_reply_wait); - return SILC_FSM_CONTINUE; - } +/* Process received command reply payload */ + +SILC_FSM_STATE(silc_client_command_reply_process) +{ + SilcClientCommandContext cmd = fsm_context; + SilcCommandPayload payload = state_context; silc_command_get_status(payload, &cmd->status, &cmd->error); - silc_fsm_set_state_context(fsm, payload); - cmd->processed = TRUE; switch (cmd->cmd) { case SILC_COMMAND_WHOIS: @@ -290,7 +333,7 @@ SILC_FSM_STATE(silc_client_command_reply_process) silc_fsm_next(fsm, silc_client_command_reply_cumode); break; case SILC_COMMAND_KICK: - /** kick */ + /** KICK */ silc_fsm_next(fsm, silc_client_command_reply_kick); break; case SILC_COMMAND_BAN: @@ -332,6 +375,31 @@ SILC_FSM_STATE(silc_client_command_reply_process) 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; +} + /******************************** WHOIS *************************************/ /* Received reply for WHOIS command. */ @@ -407,14 +475,17 @@ SILC_FSM_STATE(silc_client_command_reply_whois) 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_rwlock_wrlock(client_entry->internal.lock); + if (fingerprint && fingerprint_len == sizeof(client_entry->fingerprint)) memcpy(client_entry->fingerprint, fingerprint, fingerprint_len); @@ -426,6 +497,8 @@ SILC_FSM_STATE(silc_client_command_reply_whois) client_entry->attrs = silc_attribute_payload_parse(tmp, len); } + silc_rwlock_unlock(client_entry->internal.lock); + /* Parse channel and channel user mode list */ if (has_channels) { channel_list = silc_channel_payload_parse_list(silc_buffer_data(&channels), @@ -442,12 +515,12 @@ SILC_FSM_STATE(silc_client_command_reply_whois) silc_client_unref_client(client, conn, client_entry); if (has_channels) { - silc_dlist_uninit(channel_list); + silc_channel_payload_list_free(channel_list); silc_free(umodes); } out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -495,7 +568,7 @@ SILC_FSM_STATE(silc_client_command_reply_whowas) out: silc_client_unref_client(client, conn, client_entry); - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -542,9 +615,10 @@ SILC_FSM_STATE(silc_client_command_reply_identify) 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); @@ -568,13 +642,15 @@ SILC_FSM_STATE(silc_client_command_reply_identify) 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->resolve_cmd_ident = 0; + server_entry->internal.resolve_cmd_ident = 0; /* Notify application */ silc_client_command_callback(cmd, server_entry, name, info); + silc_client_unref_server(client, conn, server_entry); break; case SILC_ID_CHANNEL: @@ -584,7 +660,7 @@ SILC_FSM_STATE(silc_client_command_reply_identify) 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); @@ -598,15 +674,18 @@ SILC_FSM_STATE(silc_client_command_reply_identify) 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_command_callback(cmd, channel_entry, + channel_entry->channel_name, info); + silc_client_unref_channel(client, conn, channel_entry); break; } out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -621,7 +700,7 @@ SILC_FSM_STATE(silc_client_command_reply_nick) 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; @@ -630,8 +709,6 @@ SILC_FSM_STATE(silc_client_command_reply_nick) 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) { @@ -650,36 +727,25 @@ SILC_FSM_STATE(silc_client_command_reply_nick) 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; - } + silc_rwlock_wrlock(conn->local_entry->internal.lock); - /* Update the client entry */ - 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); + /* 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; } - memcpy(conn->local_entry->nickname, nick, strlen(nick)); - conn->local_entry->nickname_normalized = tmp; - silc_buffer_enlarge(conn->local_idp, idp_len); - silc_buffer_put(conn->local_idp, idp, idp_len); - silc_client_nickname_format(client, conn, conn->local_entry); + + silc_rwlock_unlock(conn->local_entry->internal.lock); /* Notify application */ silc_client_command_callback(cmd, conn->local_entry, conn->local_entry->nickname, &old_client_id); out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -696,7 +762,7 @@ SILC_FSM_STATE(silc_client_command_reply_list) 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 */ @@ -705,7 +771,7 @@ SILC_FSM_STATE(silc_client_command_reply_list) 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_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -733,13 +799,16 @@ SILC_FSM_STATE(silc_client_command_reply_list) 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); + silc_client_command_callback(cmd, channel_entry, channel_entry->channel_name, + topic, usercount); out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_client_unref_channel(client, conn, channel_entry); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -754,7 +823,7 @@ SILC_FSM_STATE(silc_client_command_reply_topic) SilcClient client = conn->client; SilcCommandPayload payload = state_context; SilcArgumentPayload args = silc_command_get_args(payload); - SilcChannelEntry channel; + SilcChannelEntry channel = NULL; char *topic; SilcUInt32 len; SilcID id; @@ -776,6 +845,8 @@ SILC_FSM_STATE(silc_client_command_reply_topic) goto out; } + silc_rwlock_wrlock(channel->internal.lock); + /* Take topic */ topic = silc_argument_get_arg_type(args, 3, &len); if (topic) { @@ -783,11 +854,14 @@ SILC_FSM_STATE(silc_client_command_reply_topic) channel->topic = silc_memdup(topic, len); } + silc_rwlock_unlock(channel->internal.lock); + /* Notify application */ silc_client_command_callback(cmd, channel, channel->topic); out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_client_unref_channel(client, conn, channel); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -802,10 +876,10 @@ SILC_FSM_STATE(silc_client_command_reply_invite) SilcClient client = conn->client; SilcCommandPayload payload = state_context; SilcArgumentPayload args = silc_command_get_args(payload); - SilcChannelEntry channel; + SilcChannelEntry channel = NULL; unsigned char *tmp; SilcUInt32 len; - SilcBufferStruct buf; + SilcArgumentPayload invite_args = NULL; SilcID id; /* Sanity checks */ @@ -828,13 +902,17 @@ SILC_FSM_STATE(silc_client_command_reply_invite) /* Get the invite list */ tmp = silc_argument_get_arg_type(args, 3, &len); if (tmp) - silc_buffer_set(&buf, tmp, len); + invite_args = silc_argument_list_parse(tmp, len); /* Notify application */ - silc_client_command_callback(cmd, channel, tmp ? &buf : NULL); + silc_client_command_callback(cmd, channel, invite_args); + + if (invite_args) + silc_argument_payload_free(invite_args); out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_client_unref_channel(client, conn, channel); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -867,14 +945,16 @@ SILC_FSM_STATE(silc_client_command_reply_kill) /* 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); + client_entry->internal.valid = FALSE; silc_client_del_client(client, conn, client_entry); silc_client_unref_client(client, conn, client_entry); } out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -921,19 +1001,21 @@ SILC_FSM_STATE(silc_client_command_reply_info) /* 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_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -990,7 +1072,7 @@ SILC_FSM_STATE(silc_client_command_reply_stats) silc_client_command_callback(cmd, &stats); out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -1006,19 +1088,62 @@ SILC_FSM_STATE(silc_client_command_reply_ping) SilcInt64 diff; diff = silc_time() - SILC_PTR_TO_64(cmd->context); - SAY(client, conn, SILC_CLIENT_MESSAGE_INFO, - "Ping reply from %s: %d second%s", conn->remote_host, - (int)diff, diff == 1 ? "" : "s"); + 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 */ silc_client_command_callback(cmd); - silc_fsm_next(fsm, silc_client_command_reply_process); + 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_FSM_STATE(silc_client_command_reply_join) @@ -1029,11 +1154,11 @@ SILC_FSM_STATE(silc_client_command_reply_join) SilcCommandPayload payload = state_context; SilcArgumentPayload args = silc_command_get_args(payload); SilcChannelEntry channel; - SilcChannelUser chu; SilcUInt32 mode = 0, len, list_count; char *topic, *tmp, *channel_name = NULL, *hmac; - SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL; - SilcBufferStruct chpklist; + const char *cipher; + SilcBufferStruct client_id_list, client_mode_list, keyp; + SilcHashTableList htl; SilcID id; int i; @@ -1054,26 +1179,10 @@ SILC_FSM_STATE(silc_client_command_reply_join) goto out; } - /* Get channel mode */ - tmp = silc_argument_get_arg_type(args, 5, NULL); - if (tmp) - SILC_GET32_MSB(mode, tmp); - - /* Get channel key */ - tmp = silc_argument_get_arg_type(args, 7, &len); - if (tmp) { - keyp = silc_buffer_alloc_size(len); - if (keyp) - silc_buffer_put(keyp, tmp, len); - } - - /* Get topic */ - topic = silc_argument_get_arg_type(args, 10, NULL); - /* Check whether we have this channel entry already. */ channel = silc_client_get_channel(client, conn, channel_name); if (channel) { - if (!SILC_ID_CHANNEL_COMPARE(channel->id, &id.u.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 */ @@ -1083,26 +1192,12 @@ SILC_FSM_STATE(silc_client_command_reply_join) ERROR_CALLBACK(SILC_STATUS_ERR_BAD_CHANNEL); goto out; } - } - - conn->current_channel = channel; - channel->mode = mode; - - /* Get hmac */ - hmac = silc_argument_get_arg_type(args, 11, NULL); - if (hmac) { - if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) { - if (cmd->verbose) - SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR, - "Cannot join channel: Unsupported HMAC `%s'", hmac); - ERROR_CALLBACK(SILC_STATUS_ERR_UNKNOWN_ALGORITHM); - goto out; - } + silc_client_ref_channel(client, conn, channel); } /* Get the list count */ tmp = silc_argument_get_arg_type(args, 12, &len); - if (!tmp) { + if (!tmp || len != 4) { ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } @@ -1114,10 +1209,18 @@ SILC_FSM_STATE(silc_client_command_reply_join) ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } + silc_buffer_set(&client_id_list, tmp, len); - client_id_list = silc_buffer_alloc_size(len); - if (client_id_list) - 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(args, 14, &len); @@ -1125,66 +1228,82 @@ SILC_FSM_STATE(silc_client_command_reply_join) ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } + silc_buffer_set(&client_mode_list, tmp, len); - client_mode_list = silc_buffer_alloc_size(len); - if (client_mode_list) - 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; 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; - if (!silc_id_payload_parse_id(client_id_list->data, idp_len, &id)) - goto out; + 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. */ + /* Get client entry */ client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id); - if (!client_entry) { - /* No, we don't have it, add entry for it. */ - client_entry = - silc_client_add_client(client, conn, NULL, NULL, NULL, - &id.u.client_id, 0); - if (!client_entry) - goto out; - } - - /* Join client to the channel */ - if (!silc_client_on_channel(channel, client_entry)) { - chu = silc_calloc(1, sizeof(*chu)); - if (!chu) { - silc_client_unref_client(client, conn, client_entry); - goto out; - } - 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); + 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)) + 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; - if (!silc_buffer_pull(client_mode_list, 4)) + } + } + + /* 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; + } } - silc_buffer_start(client_id_list); - silc_buffer_start(client_mode_list); - /* Save channel key */ -#if 0 - if (keyp) - silc_client_save_channel_key(client, conn, keyp, channel); -#endif /* 0 */ + /* 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); + } + } + + /* 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); @@ -1205,25 +1324,27 @@ SILC_FSM_STATE(silc_client_command_reply_join) /* Get channel public key list */ tmp = silc_argument_get_arg_type(args, 16, &len); if (tmp) - silc_buffer_set(&chpklist, tmp, len); + silc_client_channel_save_public_keys(channel, tmp, len, FALSE); - if (topic) { - silc_free(channel->topic); - channel->topic = silc_memdup(topic, strlen(topic)); - } + /* 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 */ - silc_client_command_callback(cmd, channel_name, channel, mode, 0, - keyp ? keyp->head : NULL, NULL, - NULL, topic, hmac, list_count, client_id_list, - client_mode_list, channel->founder_key, - tmp ? &chpklist : NULL, channel->user_limit); + 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_buffer_free(keyp); - silc_buffer_free(client_id_list); - silc_buffer_free(client_mode_list); - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -1277,13 +1398,13 @@ SILC_FSM_STATE(silc_client_command_reply_motd) silc_client_command_callback(cmd, motd); out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } /********************************** UMODE ***********************************/ -/* Received reply tot he UMODE command. Save the current user mode */ +/* Received reply to the UMODE command. Save the current user mode */ SILC_FSM_STATE(silc_client_command_reply_umode) { @@ -1305,13 +1426,15 @@ SILC_FSM_STATE(silc_client_command_reply_umode) } 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 */ silc_client_command_callback(cmd, mode); out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -1328,10 +1451,9 @@ SILC_FSM_STATE(silc_client_command_reply_cmode) SilcArgumentPayload args = silc_command_get_args(payload); unsigned char *tmp; SilcUInt32 mode; - SilcChannelEntry channel; + SilcChannelEntry channel = NULL; SilcUInt32 len; SilcPublicKey public_key = NULL; - SilcBufferStruct channel_pubkeys; SilcID id; /* Sanity checks */ @@ -1351,21 +1473,20 @@ SILC_FSM_STATE(silc_client_command_reply_cmode) 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(args, 3, &len); if (!tmp || len != 4) { ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS); goto out; } - - /* Save the mode */ SILC_GET32_MSB(mode, tmp); - channel->mode = mode; - /* Get founder public key */ - tmp = silc_argument_get_arg_type(args, 4, &len); - if (tmp) - silc_public_key_payload_decode(tmp, len, &public_key); + silc_rwlock_wrlock(channel->internal.lock); /* Get user limit */ tmp = silc_argument_get_arg_type(args, 6, &len); @@ -1377,17 +1498,38 @@ SILC_FSM_STATE(silc_client_command_reply_cmode) /* Get channel public key(s) */ tmp = silc_argument_get_arg_type(args, 5, &len); if (tmp) - silc_buffer_set(&channel_pubkeys, tmp, len); + 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 */ + channel->mode = mode; + + silc_rwlock_unlock(channel->internal.lock); /* Notify application */ silc_client_command_callback(cmd, channel, mode, public_key, - tmp ? &channel_pubkeys : NULL, - channel->user_limit); + 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_rwlock_unlock(channel->internal.lock); out: + 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_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -1403,7 +1545,7 @@ SILC_FSM_STATE(silc_client_command_reply_cumode) SilcCommandPayload payload = state_context; SilcArgumentPayload args = silc_command_get_args(payload); SilcClientEntry client_entry; - SilcChannelEntry channel; + SilcChannelEntry channel = NULL; SilcChannelUser chu; unsigned char *modev; SilcUInt32 len, mode; @@ -1448,9 +1590,11 @@ SILC_FSM_STATE(silc_client_command_reply_cumode) } /* Save the mode */ + 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 */ silc_client_command_callback(cmd, mode, channel, client_entry); @@ -1458,7 +1602,8 @@ SILC_FSM_STATE(silc_client_command_reply_cumode) silc_client_unref_client(client, conn, client_entry); out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_client_unref_channel(client, conn, channel); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -1472,7 +1617,7 @@ SILC_FSM_STATE(silc_client_command_reply_kick) SilcCommandPayload payload = state_context; SilcArgumentPayload args = silc_command_get_args(payload); SilcClientEntry client_entry; - SilcChannelEntry channel; + SilcChannelEntry channel = NULL; SilcID id; /* Sanity checks */ @@ -1511,7 +1656,8 @@ SILC_FSM_STATE(silc_client_command_reply_kick) silc_client_unref_client(client, conn, client_entry); out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_client_unref_channel(client, conn, channel); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -1527,10 +1673,13 @@ SILC_FSM_STATE(silc_client_command_reply_silcoper) CHECK_STATUS("Cannot change mode: "); CHECK_ARGS(1, 1); + /* Set user mode */ + cmd->conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR; + /* Notify application */ silc_client_command_callback(cmd); - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -1546,10 +1695,13 @@ SILC_FSM_STATE(silc_client_command_reply_oper) CHECK_STATUS("Cannot change mode: "); CHECK_ARGS(1, 1); + /* Set user mode */ + cmd->conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR; + /* Notify application */ silc_client_command_callback(cmd); - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -1568,21 +1720,19 @@ SILC_FSM_STATE(silc_client_command_reply_detach) 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 */ - silc_fsm_next(fsm, silc_client_command_reply_process); + /* 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; } @@ -1601,7 +1751,7 @@ SILC_FSM_STATE(silc_client_command_reply_watch) /* Notify application */ silc_client_command_callback(cmd); - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -1614,10 +1764,10 @@ SILC_FSM_STATE(silc_client_command_reply_ban) SilcClient client = conn->client; SilcCommandPayload payload = state_context; SilcArgumentPayload args = silc_command_get_args(payload); - SilcChannelEntry channel; + SilcChannelEntry channel = NULL; unsigned char *tmp; SilcUInt32 len; - SilcBufferStruct buf; + SilcArgumentPayload invite_args = NULL; SilcID id; /* Sanity checks */ @@ -1637,16 +1787,20 @@ SILC_FSM_STATE(silc_client_command_reply_ban) goto out; } - /* Get the ban list */ + /* Get the invite list */ tmp = silc_argument_get_arg_type(args, 3, &len); if (tmp) - silc_buffer_set(&buf, tmp, len); + invite_args = silc_argument_list_parse(tmp, len); /* Notify application */ - silc_client_command_callback(cmd, channel, tmp ? &buf : NULL); + silc_client_command_callback(cmd, channel, invite_args); + + if (invite_args) + silc_argument_payload_free(invite_args); out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_client_unref_channel(client, conn, channel); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -1662,7 +1816,8 @@ SILC_FSM_STATE(silc_client_command_reply_leave) SilcCommandPayload payload = state_context; SilcArgumentPayload args = silc_command_get_args(payload); SilcChannelEntry channel; - SilcChannelUser chu; + SilcCipher key; + SilcHmac hmac; SilcID id; /* Sanity checks */ @@ -1683,51 +1838,83 @@ SILC_FSM_STATE(silc_client_command_reply_leave) } /* Remove us from this channel. */ - chu = silc_client_on_channel(channel, conn->local_entry); - if (chu) { - silc_hash_table_del(chu->client->channels, chu->channel); - silc_hash_table_del(chu->channel->user_list, chu->client); - silc_free(chu); - } + silc_client_remove_from_channel(client, conn, channel, conn->local_entry); /* Notify application */ 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_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } /********************************* USERS ************************************/ -static SilcBool -silc_client_command_reply_users_continue(SilcClient client, +/* Continue USERS command reply processing after resolving unknown users */ + +static void +silc_client_command_reply_users_resolved(SilcClient client, SilcClientConnection conn, - SilcCommand command, SilcStatus status, - SilcStatus error, - void *context, - va_list ap) + SilcDList clients, + void *context) { SilcClientCommandContext cmd = context; - - return TRUE; + SILC_FSM_CALL_CONTINUE(&cmd->thread); } -/* Continue USERS command after resolving unknown users */ + +/* Continue USERS command after resolving unknown channel */ static void -silc_client_command_reply_users_resolved(SilcClient client, +silc_client_command_reply_users_continue(SilcClient client, SilcClientConnection conn, SilcStatus status, - SilcDList clients, + SilcDList channels, void *context) { SilcClientCommandContext cmd = context; - SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread); + + 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_FSM_CALL_CONTINUE(&cmd->thread); } /* Reply to USERS command. Received list of client ID's and theirs modes @@ -1741,13 +1928,12 @@ SILC_FSM_STATE(silc_client_command_reply_users) SilcCommandPayload payload = state_context; SilcArgumentPayload args = silc_command_get_args(payload); unsigned char *tmp; - SilcUInt32 tmp_len, list_count; - SilcUInt16 idp_len, mode; + SilcUInt32 tmp_len, list_count, mode; + SilcUInt16 idp_len; SilcHashTableList htl; SilcBufferStruct client_id_list, client_mode_list; - SilcChannelEntry channel; + SilcChannelEntry channel = NULL; SilcClientEntry client_entry; - SilcChannelUser chu; SilcID id; int i; @@ -1765,11 +1951,9 @@ SILC_FSM_STATE(silc_client_command_reply_users) channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id); if (!channel) { /* Resolve the channel from server */ -#if 0 SILC_FSM_CALL(silc_client_get_channel_by_id_resolve( client, conn, &id.u.channel_id, silc_client_command_reply_users_continue, cmd)); -#endif /* 0 */ /* NOT REACHED */ } @@ -1792,6 +1976,7 @@ SILC_FSM_STATE(silc_client_command_reply_users) /* 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)); @@ -1808,6 +1993,8 @@ SILC_FSM_STATE(silc_client_command_reply_users) 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++) { SILC_GET16_MSB(idp_len, client_id_list.data + 2); @@ -1821,33 +2008,33 @@ SILC_FSM_STATE(silc_client_command_reply_users) /* 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 && !silc_client_on_channel(channel, client_entry)) { - chu = silc_calloc(1, sizeof(*chu)); - if (!chu) { - silc_client_unref_client(client, conn, client_entry); - goto out; - } - 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 (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); - if (!silc_buffer_pull(&client_id_list, idp_len)) + 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)) + } + if (!silc_buffer_pull(&client_mode_list, 4)) { + silc_rwlock_unlock(channel->internal.lock); goto out; + } } + silc_rwlock_unlock(channel->internal.lock); + /* Notify application */ silc_hash_table_list(channel->user_list, &htl); silc_client_command_callback(cmd, channel, &htl); silc_hash_table_list_reset(&htl); out: - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_client_unref_channel(client, conn, channel); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -1887,6 +2074,8 @@ SILC_FSM_STATE(silc_client_command_reply_getkey) goto out; } 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; } @@ -1899,15 +2088,19 @@ SILC_FSM_STATE(silc_client_command_reply_getkey) goto out; } + silc_rwlock_wrlock(client_entry->internal.lock); + /* Save fingerprint */ if (!client_entry->fingerprint) - silc_hash_make(client->sha1hash, tmp + 4, len - 4, + 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 */ silc_client_command_callback(cmd, SILC_ID_CLIENT, client_entry, client_entry->public_key); @@ -1920,15 +2113,25 @@ SILC_FSM_STATE(silc_client_command_reply_getkey) goto out; } + silc_rwlock_wrlock(server_entry->internal.lock); + + if (!server_entry->public_key) { + server_entry->public_key = public_key; + public_key = NULL; + } + + silc_rwlock_unlock(server_entry->internal.lock); + /* Notify application */ silc_client_command_callback(cmd, SILC_ID_SERVER, server_entry, - public_key); + server_entry->public_key); + silc_client_unref_server(client, conn, server_entry); } out: if (public_key) silc_pkcs_public_key_free(public_key); - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -1957,7 +2160,7 @@ SILC_FSM_STATE(silc_client_command_reply_service) /* Notify application */ silc_client_command_callback(cmd, service_list, name); - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; } @@ -1967,6 +2170,6 @@ SILC_FSM_STATE(silc_client_command_reply_service) SILC_FSM_STATE(silc_client_command_reply_quit) { - silc_fsm_next(fsm, silc_client_command_reply_process); + silc_fsm_next(fsm, silc_client_command_reply_processed); return SILC_FSM_CONTINUE; }