From 587c4b43c1aeb9e863a13d9f01725bff211f5609 Mon Sep 17 00:00:00 2001 From: AI-scor <354880559+AI-scor@users.noreply.github.com> Date: Wed, 11 Nov 2026 10:05:01 +0100 Subject: [PATCH] Read the missing-feature list once in loginResponse (#7894) --- .../network/client/remote/remote_client.cpp | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp b/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp index 636c603c6..fac9eecec 100644 --- a/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp +++ b/libcockatrice_network/libcockatrice/network/client/remote/remote_client.cpp @@ -468,16 +468,28 @@ void RemoteClient::passwordSaltResponse(const Response &response) } } +namespace +{ +// The features the server reports it could not use for this login, in the order it sent them. +QList missingFeatureList(const Response_Login &resp) +{ + QList missingFeatures; + for (int i = 0; i < resp.missing_features_size(); ++i) { + missingFeatures << QString::fromStdString(resp.missing_features(i)); + } + return missingFeatures; +} +} // namespace + void RemoteClient::loginResponse(const Response &response) { const Response_Login &resp = response.GetExtension(Response_Login::ext); - QString possibleMissingFeatures; - if (resp.missing_features_size() > 0) { - for (int i = 0; i < resp.missing_features_size(); ++i) { - possibleMissingFeatures.append("," + QString::fromStdString(resp.missing_features(i))); - } - } + const QList missingFeatures = missingFeatureList(resp); + // The settings store remembers the known features as a comma-prefixed list, so keep + // that shape when comparing this server's set against it. + const QString possibleMissingFeatures = + missingFeatures.isEmpty() ? QString() : QString(",") + missingFeatures.join(","); if (response.response_code() == Response::RespOk) { setStatus(StatusLoggedIn); @@ -503,7 +515,7 @@ void RemoteClient::loginResponse(const Response &response) } emit ignoreListReceived(ignoreList); - if (newMissingFeatureFound(possibleMissingFeatures) && resp.missing_features_size() > 0 && + if (newMissingFeatureFound(possibleMissingFeatures) && !missingFeatures.isEmpty() && networkSettingsProvider->getNotifyAboutUpdates()) { networkSettingsProvider->setKnownMissingFeatures(possibleMissingFeatures); emit notifyUserAboutUpdate(); @@ -517,35 +529,24 @@ void RemoteClient::loginResponse(const Response &response) // the command would sit in pendingCommands forever, because the ping timer that // times it out stops with the connection. Keep the session and let the controller // report the outcome through accountPasswordResponse(). - QList missingFeatures; - if (resp.missing_features_size() > 0) { - for (int i = 0; i < resp.missing_features_size(); ++i) { - missingFeatures << QString::fromStdString(resp.missing_features(i)); - } - } setStatus(StatusPasswordChangeRequired); emit loginError(response.response_code(), QString::fromStdString(resp.denied_reason_str()), static_cast(resp.denied_end_time()), missingFeatures); } else if (response.response_code() != Response::RespNotConnected) { - QList missingFeatures; - if (resp.missing_features_size() > 0) { - for (int i = 0; i < resp.missing_features_size(); ++i) { - missingFeatures << QString::fromStdString(resp.missing_features(i)); - } - } + QList reportedFeatures = missingFeatures; QString reasonStr = QString::fromStdString(resp.denied_reason_str()); // A strict server reports a login that carried no credential as a missing feature, // because challenge-response needs one. No client update can supply a password, so // report it like the other local aborts instead of advising an update. - if (response.response_code() == Response::RespClientUpdateRequired && missingFeatures.size() == 1 && - missingFeatures.first() == "challenge_response_auth" && password.isEmpty() && storedVerifier.isEmpty()) { - missingFeatures.clear(); + if (response.response_code() == Response::RespClientUpdateRequired && reportedFeatures.size() == 1 && + reportedFeatures.first() == "challenge_response_auth" && password.isEmpty() && storedVerifier.isEmpty()) { + reportedFeatures.clear(); reasonStr = tr(LOGIN_ABORT_PASSWORD_MISSING); } emit loginError(response.response_code(), reasonStr, static_cast(resp.denied_end_time()), - missingFeatures); + reportedFeatures); setStatus(StatusDisconnecting); } }