mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-16 17:10:20 +08:00
[fix] restore password prompts and handle join authentication failures
This commit is contained in:
@@ -72,8 +72,6 @@ struct ServerWindowState {
|
||||
};
|
||||
|
||||
struct UiState {
|
||||
bool password_validating_ = false;
|
||||
uint32_t password_validating_time_ = 0;
|
||||
bool rejoin_ = false;
|
||||
bool show_password_ = true;
|
||||
bool show_connection_status_window_ = false;
|
||||
|
||||
@@ -1451,14 +1451,8 @@ void GuiApplication::BindMainCallbacks() {
|
||||
show_connection_status_window_ = false;
|
||||
return;
|
||||
}
|
||||
const ConnectionStatus status = props->connection_status_.load();
|
||||
if (status == ConnectionStatus::IncorrectPassword) {
|
||||
std::memset(props->remote_password_, 0, sizeof(props->remote_password_));
|
||||
password_validating_ = false;
|
||||
} else {
|
||||
CloseStreamTab(props->remote_id_);
|
||||
re_enter_remote_id_ = true;
|
||||
}
|
||||
CloseStreamTab(props->remote_id_);
|
||||
re_enter_remote_id_ = true;
|
||||
show_connection_status_window_ = false;
|
||||
});
|
||||
main->on_connection_acknowledge([this] {
|
||||
@@ -1480,11 +1474,11 @@ void GuiApplication::BindMainCallbacks() {
|
||||
if (!props || password.empty() || password.size() > 6) {
|
||||
return;
|
||||
}
|
||||
if (!props->connection_auth_.Begin(password)) return;
|
||||
std::memset(props->remote_password_, 0, sizeof(props->remote_password_));
|
||||
std::memcpy(props->remote_password_, password.data(), password.size());
|
||||
props->remember_password_ = remember;
|
||||
props->rejoin_ = true;
|
||||
password_validating_ = true;
|
||||
need_to_rejoin_ = true;
|
||||
});
|
||||
main->on_request_screen_recording_permission([this] {
|
||||
@@ -2580,26 +2574,41 @@ void GuiApplication::SyncConnectionDialog() {
|
||||
}
|
||||
ui_->connection_dialog_remote_id = props->remote_id_;
|
||||
const ConnectionStatus status = props->connection_status_.load();
|
||||
const bool password_required = status == ConnectionStatus::IncorrectPassword;
|
||||
const bool pending = status == ConnectionStatus::Connecting ||
|
||||
status == ConnectionStatus::Gathering;
|
||||
const auto auth = props->connection_auth_.Get();
|
||||
const bool password_required = auth.password_required ||
|
||||
(status == ConnectionStatus::IncorrectPassword && auth.validating);
|
||||
const bool pending = !password_required &&
|
||||
(status == ConnectionStatus::Connecting || status == ConnectionStatus::Gathering);
|
||||
std::string text;
|
||||
if (password_required) {
|
||||
text = password_validating_
|
||||
text = auth.validating
|
||||
? localization::validate_password[localization_language_index_]
|
||||
: password_validating_time_ <= 1
|
||||
? localization::input_password[localization_language_index_]
|
||||
: localization::reinput_password[localization_language_index_];
|
||||
: auth.incorrect_password
|
||||
? localization::reinput_password[localization_language_index_]
|
||||
: localization::input_password[localization_language_index_];
|
||||
} else if (auth.error == minirtc::JoinFailureKind::Throttled) {
|
||||
text = localization::connection_auth_throttled[localization_language_index_];
|
||||
} else if (auth.error == minirtc::JoinFailureKind::Busy) {
|
||||
text = localization::connection_service_busy[localization_language_index_];
|
||||
} else if (auth.error == minirtc::JoinFailureKind::NotAuthenticated) {
|
||||
text = localization::connection_not_authenticated[localization_language_index_];
|
||||
} else {
|
||||
text = ConnectionStatusText(status, localization_language_index_);
|
||||
}
|
||||
if (auth.retry_after > 0) {
|
||||
char retry_text[256];
|
||||
std::snprintf(retry_text, sizeof(retry_text),
|
||||
localization::connection_retry_after[localization_language_index_].c_str(),
|
||||
auth.retry_after);
|
||||
text += "\n" + std::string(retry_text);
|
||||
}
|
||||
|
||||
ui_->main->set_connection_dialog_open(true);
|
||||
ui_->main->set_connection_status_text(UiText(text));
|
||||
ui_->main->set_connection_pending(pending);
|
||||
ui_->main->set_connection_password_required(password_required);
|
||||
ui_->main->set_connection_validating(password_validating_);
|
||||
if (password_required && !password_validating_ &&
|
||||
ui_->main->set_connection_validating(auth.validating);
|
||||
if (password_required && !auth.validating &&
|
||||
!ui_->connection_password_initialized) {
|
||||
ui_->main->set_connection_password(props->remote_password_);
|
||||
ui_->main->set_connection_remember_password(props->remember_password_);
|
||||
|
||||
@@ -272,6 +272,10 @@ struct TranslationRow {
|
||||
X(receiving_screen, u8"画面接收中...", "Receiving screen...", \
|
||||
u8"Получение изображения...") \
|
||||
X(p2p_failed, u8"对等连接失败", "P2P Failed", u8"Сбой P2P") \
|
||||
X(connection_auth_throttled, u8"验证尝试过于频繁,请稍后重试。", "Too many authentication attempts. Try again later.", u8"Слишком много попыток. Повторите позже.") \
|
||||
X(connection_service_busy, u8"认证服务繁忙,请稍后重试。", "Authentication service busy. Try again later.", u8"Сервис проверки занят. Повторите позже.") \
|
||||
X(connection_not_authenticated, u8"登录状态已失效,请重新连接。", "Signaling login expired. Please reconnect.", u8"Сеанс входа истёк. Подключитесь заново.") \
|
||||
X(connection_retry_after, u8"请在 %d 秒后重试。", "Retry in %d seconds.", u8"Повторите через %d сек.") \
|
||||
X(p2p_closed, u8"对等连接已关闭", "P2P closed", u8"P2P закрыто") \
|
||||
X(no_such_id, u8"无此ID", "No such ID", u8"ID не найден") \
|
||||
X(about, u8"关于", "About", u8"О программе") \
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef CROSSDESK_CONNECTION_AUTH_STATE_H_
|
||||
#define CROSSDESK_CONNECTION_AUTH_STATE_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <string_view>
|
||||
|
||||
#include "join_failure.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
// One remote session's password prompt and server-requested retry delay.
|
||||
// Signaling callbacks and the UI access a single synchronized snapshot.
|
||||
class ConnectionAuthState {
|
||||
public:
|
||||
using Clock = std::chrono::steady_clock;
|
||||
struct Snapshot {
|
||||
bool password_required = false;
|
||||
bool validating = false;
|
||||
bool incorrect_password = false;
|
||||
minirtc::JoinFailureKind error = minirtc::JoinFailureKind::Unknown;
|
||||
int retry_after = 0;
|
||||
};
|
||||
|
||||
bool Begin(std::string_view password, Clock::time_point now = Clock::now()) {
|
||||
std::lock_guard lock(mutex_);
|
||||
if (retry_at_ > now) return false;
|
||||
state_ = {};
|
||||
retry_at_ = {};
|
||||
state_.password_required = password.empty();
|
||||
state_.validating = !password.empty();
|
||||
return state_.validating;
|
||||
}
|
||||
|
||||
void Rejected(const nlohmann::json& reply,
|
||||
Clock::time_point now = Clock::now()) {
|
||||
const auto failure = minirtc::ParseJoinFailure(reply);
|
||||
std::lock_guard lock(mutex_);
|
||||
state_ = {};
|
||||
state_.error = failure.kind;
|
||||
state_.incorrect_password =
|
||||
failure.kind == minirtc::JoinFailureKind::AuthenticationFailed;
|
||||
state_.password_required =
|
||||
state_.incorrect_password ||
|
||||
failure.kind == minirtc::JoinFailureKind::PasswordRequired;
|
||||
retry_at_ = now + std::chrono::seconds(failure.retry_after);
|
||||
}
|
||||
|
||||
void Complete() {
|
||||
std::lock_guard lock(mutex_);
|
||||
state_.validating = false;
|
||||
}
|
||||
|
||||
Snapshot Get(Clock::time_point now = Clock::now()) const {
|
||||
std::lock_guard lock(mutex_);
|
||||
auto snapshot = state_;
|
||||
if (retry_at_ > now) {
|
||||
snapshot.retry_after = static_cast<int>(
|
||||
std::chrono::ceil<std::chrono::seconds>(retry_at_ - now).count());
|
||||
}
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::mutex mutex_;
|
||||
Snapshot state_;
|
||||
Clock::time_point retry_at_{};
|
||||
};
|
||||
|
||||
} // namespace crossdesk
|
||||
|
||||
#endif
|
||||
@@ -479,6 +479,14 @@ std::shared_ptr<GuiRuntime::RemoteSession> GuiRuntime::FindRemoteSession(
|
||||
|
||||
int GuiRuntime::ConnectTo(const std::string& remote_id, const char* password,
|
||||
bool remember_password, bool bypass_presence_check) {
|
||||
// Do not recreate a failed peer or send another probe during a server cooldown.
|
||||
if (const auto current = FindRemoteSession(remote_id);
|
||||
current && current->connection_auth_.Get().retry_after > 0) {
|
||||
focused_remote_id_ = remote_id;
|
||||
show_connection_status_window_ = true;
|
||||
current->rejoin_ = false;
|
||||
return 0;
|
||||
}
|
||||
if (session_cleanup_tasks_.count(remote_id)) {
|
||||
pending_reconnects_[remote_id] = {password ? password : "", remember_password};
|
||||
LOG_INFO("[{}] Reconnect queued until previous peer is destroyed", remote_id);
|
||||
@@ -595,18 +603,23 @@ int GuiRuntime::ConnectTo(const std::string& remote_id, const char* password,
|
||||
auto props = FindRemoteSession(remote_id);
|
||||
if (!props || props->closing_) return -1;
|
||||
if (!props->connection_established_) {
|
||||
props->connection_status_.store(ConnectionStatus::Connecting);
|
||||
show_connection_status_window_ = true;
|
||||
|
||||
props->remember_password_ = remember_password;
|
||||
if (strcmp(password, "") != 0 &&
|
||||
if (password && *password &&
|
||||
strcmp(password, props->remote_password_) != 0) {
|
||||
strncpy(props->remote_password_, password,
|
||||
sizeof(props->remote_password_) - 1);
|
||||
props->remote_password_[sizeof(props->remote_password_) - 1] = '\0';
|
||||
}
|
||||
|
||||
std::string remote_id_with_pwd = remote_id + "@" + password;
|
||||
// Ask locally before Join; an empty-password round trip is no longer needed.
|
||||
if (!props->connection_auth_.Begin(props->remote_password_)) {
|
||||
props->rejoin_ = false;
|
||||
return 0;
|
||||
}
|
||||
props->connection_status_.store(ConnectionStatus::Connecting);
|
||||
std::string remote_id_with_pwd = remote_id + "@" + props->remote_password_;
|
||||
if (props->peer_) {
|
||||
ret = JoinConnection(props->peer_, remote_id_with_pwd.c_str());
|
||||
if (0 == ret) {
|
||||
|
||||
@@ -79,7 +79,12 @@ void PeerEventHandler::OnSignalMessage(const char* message, size_t size,
|
||||
return;
|
||||
}
|
||||
std::string type = j["type"].get<std::string>();
|
||||
if (type == "announcements_changed") {
|
||||
if (type == "user_join_transmission" && j.contains("status") &&
|
||||
j["status"] == "failed" && j.contains("transmission_id") &&
|
||||
j["transmission_id"].is_string()) {
|
||||
const auto props = runtime->FindRemoteSession(j["transmission_id"].get<std::string>());
|
||||
if (props && !props->closing_) props->connection_auth_.Rejected(j);
|
||||
} else if (type == "announcements_changed") {
|
||||
runtime->announcements_.Refresh();
|
||||
} else if (type == "announcements") {
|
||||
runtime->announcements_.Receive(j);
|
||||
@@ -265,6 +270,8 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
|
||||
runtime->is_client_mode_ = true;
|
||||
runtime->show_connection_status_window_ = true;
|
||||
props->connection_status_.store(status);
|
||||
if (status != ConnectionStatus::Connecting && status != ConnectionStatus::Gathering)
|
||||
props->connection_auth_.Complete();
|
||||
|
||||
switch (status) {
|
||||
case ConnectionStatus::Connected: {
|
||||
@@ -385,11 +392,6 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
|
||||
|
||||
break;
|
||||
}
|
||||
case ConnectionStatus::IncorrectPassword: {
|
||||
runtime->password_validating_ = false;
|
||||
runtime->password_validating_time_++;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "minirtc.h"
|
||||
#include "rendering/video_latency.h"
|
||||
#include "runtime/net_traffic_stats_cache.h"
|
||||
#include "runtime/connection_auth_state.h"
|
||||
|
||||
namespace crossdesk { class PeerEventHandler; }
|
||||
|
||||
@@ -100,6 +101,7 @@ struct RemoteSession {
|
||||
bool p2p_mode_ = true;
|
||||
bool remember_password_ = false;
|
||||
char remote_password_[7] = "";
|
||||
ConnectionAuthState connection_auth_;
|
||||
// Reused across frames by the decode callback thread when a CPU NV12
|
||||
// native frame has to be materialized for a renderer that rejects it.
|
||||
std::vector<unsigned char> native_cpu_fallback_;
|
||||
|
||||
@@ -83,9 +83,7 @@ int main() {
|
||||
ok &= ExpectContains("minirtc.h", minirtc_api, "RemoteUnavailable");
|
||||
ok &= ExpectNotContains("minirtc.h", minirtc_api, "DeviceOffline");
|
||||
ok &= ExpectContains("peer_connection.cpp", peer_connection,
|
||||
"\"Remote unavailable\"");
|
||||
ok &= ExpectContains("peer_connection.cpp", peer_connection,
|
||||
"ConnectionStatus::RemoteUnavailable");
|
||||
"on_connection_status_(ParseJoinFailure(j).status,");
|
||||
ok &= ExpectContains("peer_connection.cpp", peer_connection,
|
||||
"IsCurrentPeerConnection");
|
||||
ok &= ExpectContains(
|
||||
|
||||
Vendored
+1
-1
Submodule deps/submodules/minirtc updated: a017ce97f2...c44807de57
Reference in New Issue
Block a user