diff --git a/src/gui/application/gui_application.cpp b/src/gui/application/gui_application.cpp index aa914a4..e79c9b5 100644 --- a/src/gui/application/gui_application.cpp +++ b/src/gui/application/gui_application.cpp @@ -1342,33 +1342,42 @@ void GuiApplication::BindMainCallbacks() { if (password.size() != 6) { return false; } - std::memset(password_saved_, 0, sizeof(password_saved_)); - std::strncpy(password_saved_, password.c_str(), - sizeof(password_saved_) - 1); - if (config_center_->IsSelfHosted()) { - std::string identity = self_hosted_id_; - if (const auto at = identity.find('@'); at != std::string::npos) { - identity.resize(at); - } - if (identity.empty()) { - identity = client_id_; - } - identity += "@" + password; - std::memset(self_hosted_id_, 0, sizeof(self_hosted_id_)); - std::strncpy(self_hosted_id_, identity.c_str(), - sizeof(self_hosted_id_) - 1); - } else { - const std::string identity = std::string(client_id_) + "@" + password; - std::memset(client_id_with_password_, 0, - sizeof(client_id_with_password_)); - std::strncpy(client_id_with_password_, identity.c_str(), - sizeof(client_id_with_password_) - 1); + if (!peer_ || !signal_connected_) { + offline_warning_text_ = + localization::signal_disconnected[localization_language_index_]; + show_offline_warning_window_ = true; + return true; } - settings_.Save(); - if (peer_) { - LeaveConnection(peer_, client_id_); - DestroyPeer(&peer_); + + std::string request_id; + { + std::lock_guard lock(password_change_mutex_); + if (password_change_pending_) { + return true; + } + request_id = std::to_string(++next_password_change_request_id_); + password_change_pending_ = true; + password_change_result_ready_ = false; + password_change_succeeded_ = false; + password_change_requested_at_ = std::chrono::steady_clock::now(); + pending_password_change_request_id_ = request_id; + pending_local_password_ = password; + password_change_error_.clear(); + } + + const nlohmann::json request = {{"type", "change_password"}, + {"request_id", request_id}, + {"new_password", password}}; + const std::string message = request.dump(); + if (SendSignalMessage(peer_, message.data(), message.size()) != 0) { + std::lock_guard lock(password_change_mutex_); + password_change_pending_ = false; + pending_password_change_request_id_.clear(); + pending_local_password_.clear(); + offline_warning_text_ = + localization::signal_disconnected[localization_language_index_]; + show_offline_warning_window_ = true; } return true; }); @@ -1778,6 +1787,7 @@ void GuiApplication::Tick() { slint::quit_event_loop(); return; } + HandlePasswordChangeResult(); if (!peer_) { CreateConnectionPeer(); } @@ -1852,6 +1862,76 @@ void GuiApplication::Tick() { SyncServerWindow(); } +void GuiApplication::HandlePasswordChangeResult() { + bool succeeded = false; + std::string new_password; + std::string error; + + { + std::lock_guard lock(password_change_mutex_); + if (password_change_pending_ && !password_change_result_ready_ && + std::chrono::steady_clock::now() - password_change_requested_at_ >= + std::chrono::seconds(10)) { + password_change_result_ready_ = true; + password_change_succeeded_ = false; + password_change_error_ = "Server did not respond"; + } + + if (!password_change_result_ready_) { + return; + } + + succeeded = password_change_succeeded_; + new_password = pending_local_password_; + error = password_change_error_; + password_change_pending_ = false; + password_change_result_ready_ = false; + password_change_succeeded_ = false; + pending_password_change_request_id_.clear(); + pending_local_password_.clear(); + password_change_error_.clear(); + } + + if (!succeeded) { + LOG_WARN("Password change failed: {}", error); + offline_warning_text_ = localization::failed[localization_language_index_]; + if (!error.empty()) { + offline_warning_text_ += ": " + error; + } + show_offline_warning_window_ = true; + return; + } + + std::memset(password_saved_, 0, sizeof(password_saved_)); + std::strncpy(password_saved_, new_password.c_str(), + sizeof(password_saved_) - 1); + + const std::string identity = std::string(client_id_) + "@" + new_password; + if (config_center_->IsSelfHosted()) { + std::memset(self_hosted_id_, 0, sizeof(self_hosted_id_)); + std::strncpy(self_hosted_id_, identity.c_str(), + sizeof(self_hosted_id_) - 1); + } else { + std::memset(client_id_with_password_, 0, + sizeof(client_id_with_password_)); + std::strncpy(client_id_with_password_, identity.c_str(), + sizeof(client_id_with_password_) - 1); + } + + if (settings_.Save() != 0) { + LOG_ERROR("Password changed on server but could not be saved locally"); + offline_warning_text_ = localization::failed[localization_language_index_]; + show_offline_warning_window_ = true; + return; + } + + LOG_INFO("Password changed successfully for [{}]", client_id_); + if (peer_) { + LeaveConnection(peer_, client_id_); + DestroyPeer(&peer_); + } +} + void GuiApplication::UpdateLocalization() { const int language = localization::detail::ClampLanguageIndex(localization_language_index_); diff --git a/src/gui/application/gui_application.h b/src/gui/application/gui_application.h index 072e974..ff4fadf 100644 --- a/src/gui/application/gui_application.h +++ b/src/gui/application/gui_application.h @@ -31,6 +31,7 @@ private: void BindStreamCallbacks(); void BindServerCallbacks(); void Tick(); + void HandlePasswordChangeResult(); void SyncMainWindow(); void SyncConnectionDialog(); void SyncPlatformDialogs(); diff --git a/src/gui/runtime/peer_event_handler.cpp b/src/gui/runtime/peer_event_handler.cpp index 1fa30e4..d07b9cb 100644 --- a/src/gui/runtime/peer_event_handler.cpp +++ b/src/gui/runtime/peer_event_handler.cpp @@ -85,6 +85,38 @@ void PeerEventHandler::OnSignalMessage(const char* message, size_t size, } } } + } else if (type == "change_password") { + std::lock_guard lock(runtime->password_change_mutex_); + if (!runtime->password_change_pending_) { + LOG_WARN("Ignore unexpected password change response"); + return; + } + + if (!j.contains("request_id") || !j["request_id"].is_string() || + j["request_id"].get() != + runtime->pending_password_change_request_id_) { + LOG_WARN("Ignore password change response with unexpected request id"); + return; + } + + if (j.contains("user_id") && j["user_id"].is_string()) { + const std::string response_user_id = j["user_id"].get(); + if (!response_user_id.empty() && + response_user_id != runtime->client_id_) { + LOG_WARN("Ignore password change response for unexpected id [{}]", + response_user_id); + return; + } + } + + runtime->password_change_succeeded_ = + j.contains("status") && j["status"].is_string() && + j["status"].get() == "success"; + runtime->password_change_error_ = + j.contains("reason") && j["reason"].is_string() + ? j["reason"].get() + : "Password change failed"; + runtime->password_change_result_ready_ = true; } } diff --git a/src/gui/runtime/runtime_state.h b/src/gui/runtime/runtime_state.h index c5f68e3..751b19b 100644 --- a/src/gui/runtime/runtime_state.h +++ b/src/gui/runtime/runtime_state.h @@ -152,6 +152,18 @@ struct UserSettingsState { bool show_file_browser_ = true; }; +struct PasswordChangeState { + std::mutex password_change_mutex_; + uint64_t next_password_change_request_id_ = 0; + bool password_change_pending_ = false; + bool password_change_result_ready_ = false; + bool password_change_succeeded_ = false; + std::chrono::steady_clock::time_point password_change_requested_at_; + std::string pending_password_change_request_id_; + std::string pending_local_password_; + std::string password_change_error_; +}; + struct ConnectionState { using RemoteSessionMap = std::unordered_map; @@ -180,6 +192,7 @@ struct RuntimeState : InfrastructureState, PeerState, PlatformIntegrationState, UserSettingsState, + PasswordChangeState, ConnectionState {}; } // namespace crossdesk::gui_detail diff --git a/submodules/minirtc b/submodules/minirtc index 2f68819..eb3b296 160000 --- a/submodules/minirtc +++ b/submodules/minirtc @@ -1 +1 @@ -Subproject commit 2f6881917fb5757c33f4ee9b6f5a80f9b4c5ae90 +Subproject commit eb3b296f34ec5a7bc71491de3e9fcf257008a18d