mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
fixed crash app & up vercion & fix qml captha
This commit is contained in:
@@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(PROJECT AmneziaVPN)
|
||||
set(AMNEZIAVPN_VERSION 4.8.15.4)
|
||||
set(AMNEZIAVPN_VERSION 4.9.1.1)
|
||||
|
||||
set(QT_CREATOR_SKIP_PACKAGE_MANAGER_SETUP ON CACHE BOOL "" FORCE)
|
||||
set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <QEventLoop>
|
||||
#include <QFutureWatcher>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QPromise>
|
||||
#include <QSet>
|
||||
#include <QSysInfo>
|
||||
@@ -209,11 +210,19 @@ void SubscriptionController::updateApiConfigInJson(QJsonObject &serverConfigJson
|
||||
serverConfigJson[apiDefs::key::apiConfig] = apiConfig;
|
||||
}
|
||||
|
||||
ErrorCode SubscriptionController::executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody, bool isTestPurchase)
|
||||
ErrorCode SubscriptionController::executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody,
|
||||
bool isTestPurchase, QString *outEffectiveRequestBase,
|
||||
const QString &reuseRequestBase)
|
||||
{
|
||||
GatewayController gatewayController(m_appSettingsRepository->getGatewayEndpoint(isTestPurchase), m_appSettingsRepository->isDevGatewayEnv(isTestPurchase), apiDefs::requestTimeoutMsecs,
|
||||
m_appSettingsRepository->isStrictKillSwitchEnabled());
|
||||
return gatewayController.post(endpoint, apiPayload, responseBody);
|
||||
GatewayController gatewayController(m_appSettingsRepository->getGatewayEndpoint(isTestPurchase),
|
||||
m_appSettingsRepository->isDevGatewayEnv(isTestPurchase), apiDefs::requestTimeoutMsecs,
|
||||
m_appSettingsRepository->isStrictKillSwitchEnabled(), nullptr, reuseRequestBase);
|
||||
return gatewayController.post(endpoint, apiPayload, responseBody, outEffectiveRequestBase);
|
||||
}
|
||||
|
||||
void SubscriptionController::clearGatewayCaptchaSticky()
|
||||
{
|
||||
m_gatewayCaptchaStickyBase.clear();
|
||||
}
|
||||
|
||||
ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCountryCode, const QString &serviceType,
|
||||
@@ -235,9 +244,12 @@ ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCo
|
||||
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody);
|
||||
QString effectiveRequestBase;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody, false, &effectiveRequestBase,
|
||||
m_gatewayCaptchaStickyBase);
|
||||
|
||||
if (errorCode == ErrorCode::ApiCaptchaRequiredError) {
|
||||
m_gatewayCaptchaStickyBase = effectiveRequestBase;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseBody);
|
||||
if (jsonDoc.isObject()) {
|
||||
QJsonObject jsonObj = jsonDoc.object();
|
||||
@@ -249,6 +261,8 @@ ErrorCode SubscriptionController::importServiceFromGateway(const QString &userCo
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
m_gatewayCaptchaStickyBase.clear();
|
||||
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
return errorCode;
|
||||
}
|
||||
@@ -1110,7 +1124,8 @@ ErrorCode SubscriptionController::resolveImportServiceCaptcha(const QString &use
|
||||
const ProtocolData &protocolData,
|
||||
const QString &captchaId,
|
||||
const QString &captchaSolution,
|
||||
ServerConfig &serverConfig) {
|
||||
ServerConfig &serverConfig,
|
||||
CaptchaInfo *retryCaptchaOut) {
|
||||
GatewayRequestData gatewayRequestData{QSysInfo::productType(),
|
||||
QString(APP_VERSION),
|
||||
m_appSettingsRepository->getAppLanguage().name().split("_").first(),
|
||||
@@ -1125,14 +1140,42 @@ ErrorCode SubscriptionController::resolveImportServiceCaptcha(const QString &use
|
||||
appendProtocolDataToApiPayload(serviceProtocol, protocolData, apiPayload);
|
||||
|
||||
apiPayload["captcha_id"] = captchaId;
|
||||
apiPayload["captcha_solution"] = captchaSolution;
|
||||
QString normalizedSolution;
|
||||
normalizedSolution.reserve(captchaSolution.size());
|
||||
for (const QChar &ch : captchaSolution) {
|
||||
const ushort u = ch.unicode();
|
||||
if (u >= '0' && u <= '9') {
|
||||
normalizedSolution += ch;
|
||||
} else if (u >= 0xFF10 && u <= 0xFF19) {
|
||||
normalizedSolution += QChar(static_cast<char16_t>(u - 0xFF10 + '0'));
|
||||
}
|
||||
}
|
||||
apiPayload["captcha_solution"] = normalizedSolution.isEmpty() ? captchaSolution.trimmed() : normalizedSolution;
|
||||
|
||||
QByteArray responseBody;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody);
|
||||
QString effectiveRequestBase;
|
||||
ErrorCode errorCode = executeRequest(QString("%1v1/config"), apiPayload, responseBody, false, &effectiveRequestBase,
|
||||
m_gatewayCaptchaStickyBase);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
m_gatewayCaptchaStickyBase = effectiveRequestBase;
|
||||
if (retryCaptchaOut
|
||||
&& (errorCode == ErrorCode::ApiCaptchaInvalidError || errorCode == ErrorCode::ApiCaptchaRefreshError)) {
|
||||
const QJsonDocument jsonDoc = QJsonDocument::fromJson(responseBody);
|
||||
if (jsonDoc.isObject()) {
|
||||
const QJsonObject jsonObj = jsonDoc.object();
|
||||
if (jsonObj.contains(QStringLiteral("captcha_id")) && jsonObj.contains(QStringLiteral("captcha_image"))) {
|
||||
retryCaptchaOut->captchaId = jsonObj.value(QStringLiteral("captcha_id")).toString();
|
||||
retryCaptchaOut->captchaImageBase64 = jsonObj.value(QStringLiteral("captcha_image")).toString();
|
||||
retryCaptchaOut->hint = jsonObj.value(QStringLiteral("hint")).toString();
|
||||
retryCaptchaOut->isRequired = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
m_gatewayCaptchaStickyBase.clear();
|
||||
|
||||
QJsonObject serverConfigJson;
|
||||
errorCode = extractServerConfigJsonFromResponse(responseBody, serviceProtocol, protocolData, serverConfigJson);
|
||||
if (errorCode != ErrorCode::NoError) {
|
||||
|
||||
@@ -115,10 +115,14 @@ public:
|
||||
ErrorCode resolveImportServiceCaptcha(const QString &userCountryCode, const QString &serviceType,
|
||||
const QString &serviceProtocol, const ProtocolData &protocolData,
|
||||
const QString &captchaId, const QString &captchaSolution,
|
||||
ServerConfig &serverConfig);
|
||||
ServerConfig &serverConfig, CaptchaInfo *retryCaptchaOut = nullptr);
|
||||
|
||||
void clearGatewayCaptchaSticky();
|
||||
|
||||
private:
|
||||
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody, bool isTestPurchase = false);
|
||||
ErrorCode executeRequest(const QString &endpoint, const QJsonObject &apiPayload, QByteArray &responseBody,
|
||||
bool isTestPurchase = false, QString *outEffectiveRequestBase = nullptr,
|
||||
const QString &reuseRequestBase = QString());
|
||||
bool isApiKeyExpired(int serverIndex) const;
|
||||
|
||||
ErrorCode extractServerConfigJsonFromResponse(const QByteArray &apiResponseBody, const QString &protocol,
|
||||
@@ -129,6 +133,8 @@ private:
|
||||
|
||||
SecureServersRepository* m_serversRepository;
|
||||
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||
|
||||
QString m_gatewayCaptchaStickyBase;
|
||||
};
|
||||
|
||||
#endif // SUBSCRIPTIONCONTROLLER_H
|
||||
|
||||
@@ -56,13 +56,23 @@ namespace
|
||||
}
|
||||
|
||||
GatewayController::GatewayController(const QString &gatewayEndpoint, const bool isDevEnvironment, const int requestTimeoutMsecs,
|
||||
const bool isStrictKillSwitchEnabled, QObject *parent)
|
||||
const bool isStrictKillSwitchEnabled, QObject *parent, const QString &reuseAgwRequestBase)
|
||||
: QObject(parent),
|
||||
m_gatewayEndpoint(gatewayEndpoint),
|
||||
m_isDevEnvironment(isDevEnvironment),
|
||||
m_requestTimeoutMsecs(requestTimeoutMsecs),
|
||||
m_isStrictKillSwitchEnabled(isStrictKillSwitchEnabled)
|
||||
{
|
||||
if (!reuseAgwRequestBase.isEmpty()) {
|
||||
m_proxyUrl = reuseAgwRequestBase;
|
||||
}
|
||||
}
|
||||
|
||||
void GatewayController::writeEffectiveRequestBase(QString *outEffectiveRequestBase) const
|
||||
{
|
||||
if (outEffectiveRequestBase) {
|
||||
*outEffectiveRequestBase = m_proxyUrl.isEmpty() ? m_gatewayEndpoint : m_proxyUrl;
|
||||
}
|
||||
}
|
||||
|
||||
GatewayController::EncryptedRequestData GatewayController::prepareRequest(const QString &endpoint, const QJsonObject &apiPayload)
|
||||
@@ -172,10 +182,12 @@ GatewayController::DecryptionResult GatewayController::tryDecryptResponseBody(co
|
||||
return result;
|
||||
}
|
||||
|
||||
ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody)
|
||||
ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody,
|
||||
QString *outEffectiveRequestBase)
|
||||
{
|
||||
EncryptedRequestData encRequestData = prepareRequest(endpoint, apiPayload);
|
||||
if (encRequestData.errorCode != ErrorCode::NoError) {
|
||||
writeEffectiveRequestBase(outEffectiveRequestBase);
|
||||
return encRequestData.errorCode;
|
||||
}
|
||||
|
||||
@@ -206,7 +218,8 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
||||
tryDecryptResponseBody(encryptedResponseBody, replyError, encRequestData.key, encRequestData.iv, encRequestData.salt);
|
||||
#endif
|
||||
|
||||
if (!plaintextMock && sslErrors.isEmpty() && shouldBypassProxy(replyError, decryptionResult.decryptedBody, decryptionResult.isDecryptionSuccessful)) {
|
||||
if (!plaintextMock && sslErrors.isEmpty()
|
||||
&& shouldBypassProxy(replyError, decryptionResult.decryptedBody, decryptionResult.isDecryptionSuccessful, httpStatusCode)) {
|
||||
auto requestFunction = [&encRequestData, &encryptedResponseBody](const QString &url) {
|
||||
encRequestData.request.setUrl(url);
|
||||
return amnApp->networkManager()->post(encRequestData.request, encRequestData.requestBody);
|
||||
@@ -223,7 +236,7 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
||||
tryDecryptResponseBody(encryptedResponseBody, replyError, encRequestData.key, encRequestData.iv, encRequestData.salt);
|
||||
|
||||
if (!sslErrors.isEmpty()
|
||||
|| shouldBypassProxy(replyError, decryptionResult.decryptedBody, decryptionResult.isDecryptionSuccessful)) {
|
||||
|| shouldBypassProxy(replyError, decryptionResult.decryptedBody, decryptionResult.isDecryptionSuccessful, httpStatusCode)) {
|
||||
sslErrors = nestedSslErrors;
|
||||
return false;
|
||||
}
|
||||
@@ -239,14 +252,17 @@ ErrorCode GatewayController::post(const QString &endpoint, const QJsonObject api
|
||||
const auto errorCode =
|
||||
apiUtils::checkNetworkReplyErrors(sslErrors, replyErrorString, replyError, httpStatusCode, responseBody);
|
||||
if (errorCode) {
|
||||
writeEffectiveRequestBase(outEffectiveRequestBase);
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
if (!decryptionResult.isDecryptionSuccessful) {
|
||||
qCritical() << "error when decrypting the request body";
|
||||
writeEffectiveRequestBase(outEffectiveRequestBase);
|
||||
return ErrorCode::ApiConfigDecryptionError;
|
||||
}
|
||||
|
||||
writeEffectiveRequestBase(outEffectiveRequestBase);
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
@@ -312,7 +328,8 @@ QFuture<QPair<ErrorCode, QByteArray>> GatewayController::postAsync(const QString
|
||||
promise->finish();
|
||||
};
|
||||
|
||||
if (!plaintextMock && sslErrors->isEmpty() && shouldBypassProxy(replyError, decryptionResult.decryptedBody, decryptionResult.isDecryptionSuccessful)) {
|
||||
if (!plaintextMock && sslErrors->isEmpty()
|
||||
&& shouldBypassProxy(replyError, decryptionResult.decryptedBody, decryptionResult.isDecryptionSuccessful, httpStatusCode)) {
|
||||
auto serviceType = apiPayload.value(apiDefs::key::serviceType).toString("");
|
||||
auto userCountryCode = apiPayload.value(apiDefs::key::userCountryCode).toString("");
|
||||
|
||||
@@ -471,7 +488,7 @@ QStringList GatewayController::getProxyUrls(const QString &serviceType, const QS
|
||||
}
|
||||
|
||||
bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &replyError, const QByteArray &decryptedResponseBody,
|
||||
bool isDecryptionSuccessful)
|
||||
bool isDecryptionSuccessful, int httpStatusCode)
|
||||
{
|
||||
const QByteArray &responseBody = decryptedResponseBody;
|
||||
|
||||
@@ -497,6 +514,10 @@ bool GatewayController::shouldBypassProxy(const QNetworkReply::NetworkError &rep
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Reverse proxy or unknown route returns plaintext (e.g. "404 page not found") — not a proxy/CDN issue.
|
||||
if (httpStatusCode == httpStatusCodeNotFound || replyError == QNetworkReply::ContentNotFoundError) {
|
||||
return false;
|
||||
}
|
||||
qDebug() << "failed to decrypt the data";
|
||||
return true;
|
||||
}
|
||||
@@ -562,6 +583,10 @@ void GatewayController::bypassProxy(const QString &endpoint, const QString &serv
|
||||
|
||||
qDebug() << "go to the next proxy endpoint";
|
||||
QNetworkReply *reply = requestFunction(endpoint.arg(proxyUrl));
|
||||
if (!reply) {
|
||||
qWarning() << "GatewayController::bypassProxy: requestFunction returned null";
|
||||
return false;
|
||||
}
|
||||
|
||||
QObject::connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
@@ -584,6 +609,10 @@ void GatewayController::bypassProxy(const QString &endpoint, const QString &serv
|
||||
for (const QString &proxyUrl : proxyUrls) {
|
||||
request.setUrl(proxyUrl + "lmbd-health");
|
||||
reply = amnApp->networkManager()->get(request);
|
||||
if (!reply) {
|
||||
qWarning() << "GatewayController::bypassProxy: health check get() returned null";
|
||||
continue;
|
||||
}
|
||||
|
||||
connect(reply, &QNetworkReply::finished, &wait, &QEventLoop::quit);
|
||||
connect(reply, &QNetworkReply::sslErrors, [this, &sslErrors](const QList<QSslError> &errors) { sslErrors = errors; });
|
||||
|
||||
@@ -22,9 +22,11 @@ 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, QObject *parent = nullptr,
|
||||
const QString &reuseAgwRequestBase = QString());
|
||||
|
||||
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody);
|
||||
amnezia::ErrorCode post(const QString &endpoint, const QJsonObject apiPayload, QByteArray &responseBody,
|
||||
QString *outEffectiveRequestBase = nullptr);
|
||||
QFuture<QPair<amnezia::ErrorCode, QByteArray>> postAsync(const QString &endpoint, const QJsonObject apiPayload);
|
||||
|
||||
private:
|
||||
@@ -49,7 +51,8 @@ private:
|
||||
const QByteArray &key, const QByteArray &iv, const QByteArray &salt);
|
||||
|
||||
QStringList getProxyUrls(const QString &serviceType, const QString &userCountryCode);
|
||||
bool shouldBypassProxy(const QNetworkReply::NetworkError &replyError, const QByteArray &decryptedResponseBody, bool isDecryptionSuccessful);
|
||||
bool shouldBypassProxy(const QNetworkReply::NetworkError &replyError, const QByteArray &decryptedResponseBody,
|
||||
bool isDecryptionSuccessful, int httpStatusCode);
|
||||
void bypassProxy(const QString &endpoint, const QString &serviceType, const QString &userCountryCode,
|
||||
std::function<QNetworkReply *(const QString &url)> requestFunction,
|
||||
std::function<bool(QNetworkReply *reply, const QList<QSslError> &sslErrors)> replyProcessingFunction);
|
||||
@@ -61,12 +64,14 @@ private:
|
||||
const QString &endpoint, const QString &proxyUrl, EncryptedRequestData encRequestData,
|
||||
std::function<void(const QByteArray &, bool, const QList<QSslError> &, QNetworkReply::NetworkError, const QString &, int)> onComplete);
|
||||
|
||||
void writeEffectiveRequestBase(QString *outEffectiveRequestBase) const;
|
||||
|
||||
int m_requestTimeoutMsecs;
|
||||
QString m_gatewayEndpoint;
|
||||
bool m_isDevEnvironment = false;
|
||||
bool m_isStrictKillSwitchEnabled = false;
|
||||
|
||||
inline static QString m_proxyUrl;
|
||||
QString m_proxyUrl;
|
||||
};
|
||||
|
||||
#endif // GATEWAYCONTROLLER_H
|
||||
|
||||
@@ -37,6 +37,8 @@ UpdateController::UpdateController(SecureAppSettingsRepository* appSettingsRepos
|
||||
{
|
||||
}
|
||||
|
||||
UpdateController::~UpdateController() = default;
|
||||
|
||||
QString UpdateController::getRawChangelogText() const
|
||||
{
|
||||
return m_changelogText;
|
||||
@@ -97,6 +99,7 @@ void UpdateController::fetchGatewayUrl()
|
||||
m_appSettingsRepository->isDevGatewayEnv(),
|
||||
7000,
|
||||
m_appSettingsRepository->isStrictKillSwitchEnabled());
|
||||
m_activeGatewayController = gatewayController;
|
||||
|
||||
QJsonObject apiPayload;
|
||||
apiPayload[apiDefs::key::cliVersion] = QString(APP_VERSION);
|
||||
@@ -107,6 +110,7 @@ void UpdateController::fetchGatewayUrl()
|
||||
QTimer::singleShot(1000, this, [this, gatewayController, apiPayload]() {
|
||||
gatewayController->postAsync(QStringLiteral("%1v1/updater_endpoint"), apiPayload)
|
||||
.then(this, [this](QPair<ErrorCode, QByteArray> result) {
|
||||
m_activeGatewayController.clear();
|
||||
auto [err, gatewayResponse] = result;
|
||||
if (err != ErrorCode::NoError) {
|
||||
logger.error() << errorString(err);
|
||||
|
||||
@@ -7,11 +7,16 @@
|
||||
|
||||
#include "core/repositories/secureAppSettingsRepository.h"
|
||||
|
||||
#include <QSharedPointer>
|
||||
|
||||
class GatewayController;
|
||||
|
||||
class UpdateController : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UpdateController(SecureAppSettingsRepository* appSettingsRepository, QObject *parent = nullptr);
|
||||
~UpdateController() override;
|
||||
|
||||
QString getRawChangelogText() const;
|
||||
QString getReleaseDate() const;
|
||||
@@ -38,6 +43,8 @@ private:
|
||||
|
||||
SecureAppSettingsRepository* m_appSettingsRepository;
|
||||
|
||||
QSharedPointer<GatewayController> m_activeGatewayController;
|
||||
|
||||
QString m_baseUrl;
|
||||
QString m_changelogText;
|
||||
QString m_version;
|
||||
|
||||
@@ -170,6 +170,13 @@ amnezia::ErrorCode apiUtils::checkNetworkReplyErrors(const QList<QSslError> &ssl
|
||||
qDebug() << replyError;
|
||||
qDebug() << httpStatusCode;
|
||||
|
||||
if (httpStatusCode == httpStatusCodeNotFound) {
|
||||
const QJsonDocument probe = QJsonDocument::fromJson(responseBody);
|
||||
if (!probe.isObject()) {
|
||||
return amnezia::ErrorCode::ApiNotFoundError;
|
||||
}
|
||||
}
|
||||
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseBody);
|
||||
if (jsonDoc.isObject()) {
|
||||
QJsonObject jsonObj = jsonDoc.object();
|
||||
|
||||
@@ -255,6 +255,10 @@ bool SubscriptionUiController::restoreServiceFromAppStore()
|
||||
|
||||
bool SubscriptionUiController::importFreeFromGateway()
|
||||
{
|
||||
if (!isCaptchaAwaitingUser()) {
|
||||
m_subscriptionController->clearGatewayCaptchaSticky();
|
||||
}
|
||||
|
||||
QString userCountryCode = m_apiServicesModel->getCountryCode();
|
||||
QString serviceType = m_apiServicesModel->getSelectedServiceType();
|
||||
QString serviceProtocol = m_apiServicesModel->getSelectedServiceProtocol();
|
||||
@@ -307,6 +311,7 @@ void SubscriptionUiController::onCaptchaSolved(const QString &captchaId, const Q
|
||||
protocolData.xrayUuid = m_captchaState.xrayUuid;
|
||||
|
||||
ServerConfig serverConfig;
|
||||
SubscriptionController::CaptchaInfo retryCaptcha;
|
||||
ErrorCode errorCode = m_subscriptionController->resolveImportServiceCaptcha(
|
||||
m_captchaState.userCountryCode,
|
||||
m_captchaState.serviceType,
|
||||
@@ -314,16 +319,26 @@ void SubscriptionUiController::onCaptchaSolved(const QString &captchaId, const Q
|
||||
protocolData,
|
||||
captchaId,
|
||||
solution,
|
||||
serverConfig);
|
||||
|
||||
m_captchaState.isPending = false;
|
||||
serverConfig,
|
||||
&retryCaptcha);
|
||||
|
||||
if (errorCode == ErrorCode::NoError) {
|
||||
m_captchaState.isPending = false;
|
||||
emit captchaFlowDismissRequested();
|
||||
m_serversController->addServer(serverConfig);
|
||||
emit installServerFromApiFinished(tr("%1 installed successfully.").arg(m_apiServicesModel->getSelectedServiceName()));
|
||||
} else {
|
||||
emit errorOccurred(errorCode);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((errorCode == ErrorCode::ApiCaptchaInvalidError || errorCode == ErrorCode::ApiCaptchaRefreshError)
|
||||
&& retryCaptcha.isRequired) {
|
||||
emit captchaRequired(retryCaptcha.captchaId, retryCaptcha.captchaImageBase64,
|
||||
retryCaptcha.hint.isEmpty() ? tr("Enter the digits from the image to continue") : retryCaptcha.hint);
|
||||
return;
|
||||
}
|
||||
|
||||
m_captchaState.isPending = false;
|
||||
emit errorOccurred(errorCode);
|
||||
}
|
||||
|
||||
void SubscriptionUiController::onRefreshCaptchaRequested()
|
||||
|
||||
@@ -84,6 +84,7 @@ signals:
|
||||
|
||||
void vpnKeyExportReady();
|
||||
void captchaRequired(const QString &captchaId, const QString &captchaImageBase64, const QString &hint);
|
||||
void captchaFlowDismissRequested();
|
||||
|
||||
private:
|
||||
struct CaptchaState {
|
||||
|
||||
@@ -40,6 +40,18 @@ Popup {
|
||||
solutionField.textField.focus = true
|
||||
}
|
||||
|
||||
onCaptchaIdChanged: {
|
||||
if (opened) {
|
||||
solutionField.textField.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
onCaptchaImageBase64Changed: {
|
||||
if (opened) {
|
||||
solutionField.textField.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
onClosed: {
|
||||
FocusController.dropRootObject(root)
|
||||
}
|
||||
|
||||
@@ -214,7 +214,6 @@ Window {
|
||||
id: captchaDialog
|
||||
|
||||
onCaptchaSolved: function(captchaId, solution) {
|
||||
captchaDialog.close()
|
||||
SubscriptionUiController.onCaptchaSolved(captchaId, solution)
|
||||
}
|
||||
|
||||
@@ -336,6 +335,10 @@ Window {
|
||||
captchaDialog.hint = hint
|
||||
captchaDialog.open()
|
||||
}
|
||||
|
||||
function onCaptchaFlowDismissRequested() {
|
||||
captchaDialog.close()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
||||
Reference in New Issue
Block a user