From 1229a95b7fae7930a89625f8ad94d09403e95a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Wed, 4 Nov 2026 06:21:51 +0100 Subject: [PATCH] Share deck analysis URL validation and drop the unused request latch Both analysis interfaces carried a copy of normalizeAnalysisUrl; move it to one header next to them. They are constructed per analysis and analyzeDeck is called once, so the analysisStarted latch was unreachable; remove it from both. --- .../network/interfaces/deck_analysis_url.h | 42 +++++++++++++++++++ .../interfaces/deck_stats_interface.cpp | 27 +----------- .../network/interfaces/deck_stats_interface.h | 1 - .../interfaces/tapped_out_interface.cpp | 27 +----------- .../network/interfaces/tapped_out_interface.h | 1 - 5 files changed, 44 insertions(+), 54 deletions(-) create mode 100644 cockatrice/src/client/network/interfaces/deck_analysis_url.h diff --git a/cockatrice/src/client/network/interfaces/deck_analysis_url.h b/cockatrice/src/client/network/interfaces/deck_analysis_url.h new file mode 100644 index 000000000..425056270 --- /dev/null +++ b/cockatrice/src/client/network/interfaces/deck_analysis_url.h @@ -0,0 +1,42 @@ +/** + * @file deck_analysis_url.h + * @ingroup ApiInterfaces + */ + +#ifndef DECK_ANALYSIS_URL_H +#define DECK_ANALYSIS_URL_H + +#include +#include + +/** + * Validates a deck address returned by a deck analysis service. + * + * Only the expected host (optionally "www."-prefixed), HTTPS, default ports and + * URLs without user information are accepted. Legacy HTTP addresses for the same + * service are upgraded to HTTPS in place instead of being rejected. + * + * Relative addresses must be resolved against the response URL by the caller. + * + * @return true when @p url names a deck on @p serviceHost; false otherwise. + */ +inline bool normalizeAnalysisUrl(QUrl &url, const QString &serviceHost) +{ + const QString host = url.host().toLower(); + const bool legacyHttp = url.scheme() == QStringLiteral("http"); + const int defaultPort = legacyHttp ? 80 : 443; + if (!url.isValid() || (!legacyHttp && url.scheme() != QStringLiteral("https")) || !url.userInfo().isEmpty() || + !url.path().startsWith('/') || (url.port() != -1 && url.port() != defaultPort) || + (host != serviceHost && host != QStringLiteral("www.") + serviceHost)) { + return false; + } + // Both services accept HTTPS. Older public metadata may still identify the + // same deck with HTTP; retain its host/path/query while using HTTPS to open it. + if (legacyHttp) { + url.setScheme(QStringLiteral("https")); + url.setPort(-1); + } + return true; +} + +#endif diff --git a/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp b/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp index 5be4c8a9a..22be120c4 100644 --- a/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp +++ b/cockatrice/src/client/network/interfaces/deck_stats_interface.cpp @@ -1,5 +1,6 @@ #include "deck_stats_interface.h" +#include "deck_analysis_url.h" #include "libcockatrice/card/card_info.h" #include "libcockatrice/card/database/card_database_querier.h" #include "libcockatrice/deck_list/tree/inner_deck_list_node.h" @@ -25,28 +26,6 @@ #include #include -namespace -{ -bool normalizeAnalysisUrl(QUrl &url, const QString &serviceHost) -{ - const QString host = url.host().toLower(); - const bool legacyHttp = url.scheme() == QStringLiteral("http"); - const int defaultPort = legacyHttp ? 80 : 443; - if (!url.isValid() || (!legacyHttp && url.scheme() != QStringLiteral("https")) || !url.userInfo().isEmpty() || - !url.path().startsWith('/') || (url.port() != -1 && url.port() != defaultPort) || - (host != serviceHost && host != QStringLiteral("www.") + serviceHost)) { - return false; - } - // Both services accept HTTPS. Older public metadata may still identify the - // same deck with HTTP; retain its host/path/query while using HTTPS to open it. - if (legacyHttp) { - url.setScheme(QStringLiteral("https")); - url.setPort(-1); - } - return true; -} -} // namespace - DeckStatsInterface::DeckStatsInterface(QObject *parent) : QObject(parent) { manager = NetworkManagerFactory::create(this); @@ -116,10 +95,6 @@ void DeckStatsInterface::getAnalyzeRequestData(const DeckList &deck, QByteArray void DeckStatsInterface::analyzeDeck(const DeckList &deck) { - if (analysisStarted) { - return; - } - analysisStarted = true; QByteArray data; getAnalyzeRequestData(deck, data); diff --git a/cockatrice/src/client/network/interfaces/deck_stats_interface.h b/cockatrice/src/client/network/interfaces/deck_stats_interface.h index 05759ab9e..b70646403 100644 --- a/cockatrice/src/client/network/interfaces/deck_stats_interface.h +++ b/cockatrice/src/client/network/interfaces/deck_stats_interface.h @@ -21,7 +21,6 @@ class DeckStatsInterface : public QObject Q_OBJECT private: QNetworkAccessManager *manager; - bool analysisStarted = false; bool replyHandled = false; void showError(const QString &message); diff --git a/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp b/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp index 41d387557..a57877b6a 100644 --- a/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp +++ b/cockatrice/src/client/network/interfaces/tapped_out_interface.cpp @@ -1,5 +1,6 @@ #include "tapped_out_interface.h" +#include "deck_analysis_url.h" #include "libcockatrice/card/card_info.h" #include "libcockatrice/card/database/card_database_querier.h" #include "libcockatrice/deck_list/tree/inner_deck_list_node.h" @@ -31,28 +32,6 @@ #include #include -namespace -{ -bool normalizeAnalysisUrl(QUrl &url, const QString &serviceHost) -{ - const QString host = url.host().toLower(); - const bool legacyHttp = url.scheme() == QStringLiteral("http"); - const int defaultPort = legacyHttp ? 80 : 443; - if (!url.isValid() || (!legacyHttp && url.scheme() != QStringLiteral("https")) || !url.userInfo().isEmpty() || - !url.path().startsWith('/') || (url.port() != -1 && url.port() != defaultPort) || - (host != serviceHost && host != QStringLiteral("www.") + serviceHost)) { - return false; - } - // Both services accept HTTPS. Older public metadata may still identify the - // same deck with HTTP; retain its host/path/query while using HTTPS to open it. - if (legacyHttp) { - url.setScheme(QStringLiteral("https")); - url.setPort(-1); - } - return true; -} -} // namespace - TappedOutInterface::TappedOutInterface(QObject *parent) : QObject(parent) { manager = NetworkManagerFactory::create(this); @@ -134,10 +113,6 @@ void TappedOutInterface::getAnalyzeRequestData(const DeckList &deck, QByteArray void TappedOutInterface::analyzeDeck(const DeckList &deck) { - if (analysisStarted) { - return; - } - analysisStarted = true; QByteArray data; getAnalyzeRequestData(deck, data); diff --git a/cockatrice/src/client/network/interfaces/tapped_out_interface.h b/cockatrice/src/client/network/interfaces/tapped_out_interface.h index e89e29551..27e8c2273 100644 --- a/cockatrice/src/client/network/interfaces/tapped_out_interface.h +++ b/cockatrice/src/client/network/interfaces/tapped_out_interface.h @@ -30,7 +30,6 @@ class TappedOutInterface : public QObject Q_OBJECT private: QNetworkAccessManager *manager; - bool analysisStarted = false; bool replyHandled = false; void showError(const QString &message);