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.
This commit is contained in:
Lukas Brübach 2026-11-04 06:21:51 +01:00
parent 02de7e1c55
commit 1229a95b7f
5 changed files with 44 additions and 54 deletions

View file

@ -0,0 +1,42 @@
/**
* @file deck_analysis_url.h
* @ingroup ApiInterfaces
*/
#ifndef DECK_ANALYSIS_URL_H
#define DECK_ANALYSIS_URL_H
#include <QString>
#include <QUrl>
/**
* 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

View file

@ -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 <libcockatrice/network/network_manager.h>
#include <version_string.h>
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);

View file

@ -21,7 +21,6 @@ class DeckStatsInterface : public QObject
Q_OBJECT
private:
QNetworkAccessManager *manager;
bool analysisStarted = false;
bool replyHandled = false;
void showError(const QString &message);

View file

@ -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 <libcockatrice/network/network_manager.h>
#include <version_string.h>
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);

View file

@ -30,7 +30,6 @@ class TappedOutInterface : public QObject
Q_OBJECT
private:
QNetworkAccessManager *manager;
bool analysisStarted = false;
bool replyHandled = false;
void showError(const QString &message);