diff --git a/client/core/controllers/gatewayController.cpp b/client/core/controllers/gatewayController.cpp index 132af459f..9dfbde391 100644 --- a/client/core/controllers/gatewayController.cpp +++ b/client/core/controllers/gatewayController.cpp @@ -46,12 +46,25 @@ namespace constexpr int httpStatusCodeConflict = 409; constexpr int httpStatusCodeNotImplemented = 501; + + QStringList splitUrls(const QString &urls) + { + QStringList parsedUrls = urls.split(",", Qt::SkipEmptyParts); + for (QString &url : parsedUrls) { + url = url.trimmed(); + } + parsedUrls.removeAll(""); + return parsedUrls; + } } GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs, - const bool isStrictKillSwitchEnabled, QObject *parent) + const bool isStrictKillSwitchEnabled, const QString &proxyStorageOverride, + const QString &proxyUrlOverride, QObject *parent) : QObject(parent), m_gatewayEndpoint(gatewayEndpoint), + m_proxyStorageOverride(proxyStorageOverride), + m_proxyUrlOverride(proxyUrlOverride), m_isDevEnvironment(isDevEnvironment), m_requestTimeoutMsecs(requestTimeoutMsecs), m_isStrictKillSwitchEnabled(isStrictKillSwitchEnabled) @@ -71,7 +84,8 @@ GatewayController::EncryptedRequestData GatewayController::prepareRequest(const encRequestData.request.setTransferTimeout(m_requestTimeoutMsecs); encRequestData.request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); encRequestData.request.setRawHeader(QString("X-Client-Request-ID").toUtf8(), QUuid::createUuid().toString(QUuid::WithoutBraces).toUtf8()); - encRequestData.request.setUrl(endpoint.arg(m_proxyUrl.isEmpty() ? m_gatewayEndpoint : m_proxyUrl)); + QString selectedProxyUrl = m_proxyUrlOverride.isEmpty() ? m_proxyUrl : m_proxyUrlOverride; + encRequestData.request.setUrl(endpoint.arg(selectedProxyUrl.isEmpty() ? m_gatewayEndpoint : selectedProxyUrl)); // bypass killSwitch exceptions for API-gateway #ifdef AMNEZIA_DESKTOP @@ -283,9 +297,9 @@ QFuture> GatewayController::postAsync(const QString QStringList baseUrls; if (m_isDevEnvironment) { - baseUrls = QString(DEV_S3_ENDPOINT).split(", "); + baseUrls = m_proxyStorageOverride.isEmpty() ? splitUrls(DEV_S3_ENDPOINT) : splitUrls(m_proxyStorageOverride); } else { - baseUrls = QString(PROD_S3_ENDPOINT).split(", "); + baseUrls = splitUrls(PROD_S3_ENDPOINT); } QStringList proxyStorageUrls; @@ -333,9 +347,9 @@ QStringList GatewayController::getProxyUrls(const QString &serviceType, const QS QStringList baseUrls; if (m_isDevEnvironment) { - baseUrls = QString(DEV_S3_ENDPOINT).split(", "); + baseUrls = m_proxyStorageOverride.isEmpty() ? splitUrls(DEV_S3_ENDPOINT) : splitUrls(m_proxyStorageOverride); } else { - baseUrls = QString(PROD_S3_ENDPOINT).split(", "); + baseUrls = splitUrls(PROD_S3_ENDPOINT); } QByteArray key = m_isDevEnvironment ? DEV_AGW_PUBLIC_KEY : PROD_AGW_PUBLIC_KEY; @@ -484,7 +498,9 @@ void GatewayController::bypassProxy(const QString &endpoint, const QString &serv return result; }; - if (m_proxyUrl.isEmpty()) { + QString selectedProxyUrl = m_proxyUrlOverride.isEmpty() ? m_proxyUrl : m_proxyUrlOverride; + + if (selectedProxyUrl.isEmpty()) { QNetworkRequest request; request.setTransferTimeout(1000); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); @@ -506,6 +522,7 @@ void GatewayController::bypassProxy(const QString &endpoint, const QString &serv m_proxyUrl = proxyUrl; if (!m_proxyUrl.isEmpty()) { + selectedProxyUrl = m_proxyUrl; break; } } else { @@ -514,8 +531,8 @@ void GatewayController::bypassProxy(const QString &endpoint, const QString &serv } } - if (!m_proxyUrl.isEmpty()) { - if (bypassFunction(endpoint, m_proxyUrl, requestFunction, replyProcessingFunction)) { + if (!selectedProxyUrl.isEmpty()) { + if (bypassFunction(endpoint, selectedProxyUrl, requestFunction, replyProcessingFunction)) { return; } } @@ -601,6 +618,11 @@ void GatewayController::getProxyUrlsAsync(const QStringList proxyStorageUrls, co void GatewayController::getProxyUrlAsync(const QStringList proxyUrls, const int currentProxyIndex, std::function onComplete) { + if (!m_proxyUrlOverride.isEmpty()) { + onComplete(m_proxyUrlOverride); + return; + } + if (currentProxyIndex >= proxyUrls.size()) { onComplete(""); return; diff --git a/client/core/controllers/gatewayController.h b/client/core/controllers/gatewayController.h index 96e842535..086298d99 100644 --- a/client/core/controllers/gatewayController.h +++ b/client/core/controllers/gatewayController.h @@ -20,7 +20,8 @@ class GatewayController : public QObject public: explicit GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs, - const bool isStrictKillSwitchEnabled, QObject *parent = nullptr); + const bool isStrictKillSwitchEnabled, const QString &proxyStorageOverride = "", + const QString &proxyUrlOverride = "", QObject *parent = nullptr); amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody); QFuture> postAsync(const QString &endpoint, const QJsonObject apiPayload); @@ -61,6 +62,8 @@ private: int m_requestTimeoutMsecs; QString m_gatewayEndpoint; + QString m_proxyStorageOverride; + QString m_proxyUrlOverride; bool m_isDevEnvironment = false; bool m_isStrictKillSwitchEnabled = false; diff --git a/client/settings.cpp b/client/settings.cpp index c11295ef6..03a4a66a6 100644 --- a/client/settings.cpp +++ b/client/settings.cpp @@ -539,6 +539,26 @@ QString Settings::getGatewayEndpoint(bool isTestPurchase) return isTestPurchase ? DEV_AGW_ENDPOINT : m_gatewayEndpoint; } +QString Settings::getDevProxyStorageEndpoint() const +{ + return value("Conf/devProxyStorageEndpoint", "").toString(); +} + +void Settings::setDevProxyStorageEndpoint(const QString &endpoint) +{ + setValue("Conf/devProxyStorageEndpoint", endpoint); +} + +QString Settings::getDevProxyUrl() const +{ + return value("Conf/devProxyUrl", "").toString(); +} + +void Settings::setDevProxyUrl(const QString &url) +{ + setValue("Conf/devProxyUrl", url); +} + bool Settings::isDevGatewayEnv(bool isTestPurchase) { return isTestPurchase ? true : value("Conf/devGatewayEnv", false).toBool(); diff --git a/client/settings.h b/client/settings.h index e8277304e..d9afc0fea 100644 --- a/client/settings.h +++ b/client/settings.h @@ -233,6 +233,10 @@ public: void setGatewayEndpoint(const QString &endpoint); void setDevGatewayEndpoint(); QString getGatewayEndpoint(bool isTestPurchase = false); + QString getDevProxyStorageEndpoint() const; + void setDevProxyStorageEndpoint(const QString &endpoint); + QString getDevProxyUrl() const; + void setDevProxyUrl(const QString &url); bool isDevGatewayEnv(bool isTestPurchase = false); void toggleDevGatewayEnv(bool enabled); diff --git a/client/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index 83ca3284f..8694f2554 100644 --- a/client/ui/controllers/api/apiConfigsController.cpp +++ b/client/ui/controllers/api/apiConfigsController.cpp @@ -769,7 +769,8 @@ bool ApiConfigsController::updateServiceFromTelegram(const int serverIndex) #endif GatewayController gatewayController(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), apiDefs::requestTimeoutMsecs, - m_settings->isStrictKillSwitchEnabled()); + m_settings->isStrictKillSwitchEnabled(), m_settings->getDevProxyStorageEndpoint(), + m_settings->getDevProxyUrl()); auto serverConfig = m_serversModel->getServerConfig(serverIndex); auto installationUuid = m_settings->getInstallationUuid(true); @@ -994,6 +995,7 @@ ErrorCode ApiConfigsController::executeRequest(const QString &endpoint, const QJ bool isTestPurchase) { GatewayController gatewayController(m_settings->getGatewayEndpoint(isTestPurchase), m_settings->isDevGatewayEnv(isTestPurchase), - apiDefs::requestTimeoutMsecs, m_settings->isStrictKillSwitchEnabled()); + apiDefs::requestTimeoutMsecs, m_settings->isStrictKillSwitchEnabled(), + m_settings->getDevProxyStorageEndpoint(), m_settings->getDevProxyUrl()); return gatewayController.post(endpoint, apiPayload, responseBody); } diff --git a/client/ui/controllers/api/apiNewsController.cpp b/client/ui/controllers/api/apiNewsController.cpp index 9e294f11f..1adeaec50 100644 --- a/client/ui/controllers/api/apiNewsController.cpp +++ b/client/ui/controllers/api/apiNewsController.cpp @@ -32,7 +32,10 @@ void ApiNewsController::fetchNews(bool showError) } auto gatewayController = QSharedPointer::create(m_settings->getGatewayEndpoint(), m_settings->isDevGatewayEnv(), - apiDefs::requestTimeoutMsecs, m_settings->isStrictKillSwitchEnabled()); + apiDefs::requestTimeoutMsecs, + m_settings->isStrictKillSwitchEnabled(), + m_settings->getDevProxyStorageEndpoint(), + m_settings->getDevProxyUrl()); QJsonObject payload; payload.insert("locale", m_settings->getAppLanguage().name().split("_").first()); diff --git a/client/ui/controllers/api/apiSettingsController.cpp b/client/ui/controllers/api/apiSettingsController.cpp index 59a68fd88..4f621dc94 100644 --- a/client/ui/controllers/api/apiSettingsController.cpp +++ b/client/ui/controllers/api/apiSettingsController.cpp @@ -57,7 +57,8 @@ bool ApiSettingsController::getAccountInfo(bool reload) bool isTestPurchase = apiConfig.value(apiDefs::key::isTestPurchase).toBool(false); GatewayController gatewayController(m_settings->getGatewayEndpoint(isTestPurchase), m_settings->isDevGatewayEnv(isTestPurchase), - requestTimeoutMsecs, m_settings->isStrictKillSwitchEnabled()); + requestTimeoutMsecs, m_settings->isStrictKillSwitchEnabled(), + m_settings->getDevProxyStorageEndpoint(), m_settings->getDevProxyUrl()); QJsonObject apiPayload; apiPayload[configKey::userCountryCode] = apiConfig.value(configKey::userCountryCode).toString(); diff --git a/client/ui/controllers/settingsController.cpp b/client/ui/controllers/settingsController.cpp index 6a3726828..efaf7d566 100644 --- a/client/ui/controllers/settingsController.cpp +++ b/client/ui/controllers/settingsController.cpp @@ -426,6 +426,28 @@ QString SettingsController::getGatewayEndpoint() return m_settings->isDevGatewayEnv() ? "Dev endpoint" : m_settings->getGatewayEndpoint(); } +void SettingsController::setDevProxyStorageEndpoint(const QString &endpoint) +{ + m_settings->setDevProxyStorageEndpoint(endpoint); + emit devProxyStorageEndpointChanged(endpoint); +} + +QString SettingsController::getDevProxyStorageEndpoint() +{ + return m_settings->getDevProxyStorageEndpoint(); +} + +void SettingsController::setDevProxyUrl(const QString &url) +{ + m_settings->setDevProxyUrl(url); + emit devProxyUrlChanged(url); +} + +QString SettingsController::getDevProxyUrl() +{ + return m_settings->getDevProxyUrl(); +} + bool SettingsController::isDevGatewayEnv() { return m_settings->isDevGatewayEnv(); diff --git a/client/ui/controllers/settingsController.h b/client/ui/controllers/settingsController.h index fa50bc23c..edcb0f368 100644 --- a/client/ui/controllers/settingsController.h +++ b/client/ui/controllers/settingsController.h @@ -30,6 +30,9 @@ public: Q_PROPERTY(bool isDevModeEnabled READ isDevModeEnabled NOTIFY devModeEnabled) Q_PROPERTY(QString gatewayEndpoint READ getGatewayEndpoint WRITE setGatewayEndpoint NOTIFY gatewayEndpointChanged) Q_PROPERTY(bool isDevGatewayEnv READ isDevGatewayEnv WRITE toggleDevGatewayEnv NOTIFY devGatewayEnvChanged) + Q_PROPERTY(QString devProxyStorageEndpoint READ getDevProxyStorageEndpoint WRITE setDevProxyStorageEndpoint NOTIFY + devProxyStorageEndpointChanged) + Q_PROPERTY(QString devProxyUrl READ getDevProxyUrl WRITE setDevProxyUrl NOTIFY devProxyUrlChanged) Q_PROPERTY(bool isHomeAdLabelVisible READ isHomeAdLabelVisible NOTIFY isHomeAdLabelVisibleChanged) Q_PROPERTY(bool startMinimized READ isStartMinimizedEnabled NOTIFY startMinimizedChanged) @@ -98,6 +101,10 @@ public slots: void resetGatewayEndpoint(); void setGatewayEndpoint(const QString &endpoint); QString getGatewayEndpoint(); + void setDevProxyStorageEndpoint(const QString &endpoint); + QString getDevProxyStorageEndpoint(); + void setDevProxyUrl(const QString &url); + QString getDevProxyUrl(); bool isDevGatewayEnv(); void toggleDevGatewayEnv(bool enabled); @@ -136,6 +143,8 @@ signals: void devModeEnabled(); void gatewayEndpointChanged(const QString &endpoint); void devGatewayEnvChanged(bool enabled); + void devProxyStorageEndpointChanged(const QString &endpoint); + void devProxyUrlChanged(const QString &url); void imeHeightChanged(int height); void safeAreaTopMarginChanged(); diff --git a/client/ui/qml/Pages2/PageDevMenu.qml b/client/ui/qml/Pages2/PageDevMenu.qml index 81f44edbc..fa40c64f8 100644 --- a/client/ui/qml/Pages2/PageDevMenu.qml +++ b/client/ui/qml/Pages2/PageDevMenu.qml @@ -73,6 +73,50 @@ PageType { } } } + + TextFieldWithHeaderType { + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + + headerText: qsTr("Proxy storage endpoint") + textField.text: SettingsController.devProxyStorageEndpoint + + buttonImageSource: textField.text !== "" ? "qrc:/images/controls/refresh-cw.svg" : "" + + clickedFunc: function() { + SettingsController.devProxyStorageEndpoint = "" + } + + textField.onEditingFinished: { + textField.text = textField.text.replace(/^\s+|\s+$/g, '') + if (textField.text !== SettingsController.devProxyStorageEndpoint) { + SettingsController.devProxyStorageEndpoint = textField.text + } + } + } + + TextFieldWithHeaderType { + Layout.fillWidth: true + Layout.rightMargin: 16 + Layout.leftMargin: 16 + + headerText: qsTr("Proxy endpoint") + textField.text: SettingsController.devProxyUrl + + buttonImageSource: textField.text !== "" ? "qrc:/images/controls/refresh-cw.svg" : "" + + clickedFunc: function() { + SettingsController.devProxyUrl = "" + } + + textField.onEditingFinished: { + textField.text = textField.text.replace(/^\s+|\s+$/g, '') + if (textField.text !== SettingsController.devProxyUrl) { + SettingsController.devProxyUrl = textField.text + } + } + } } footer: ColumnLayout {