diff --git a/client/core/api/apiUtils.cpp b/client/core/api/apiUtils.cpp index 7f3e6db39..78d2e3cb8 100644 --- a/client/core/api/apiUtils.cpp +++ b/client/core/api/apiUtils.cpp @@ -23,7 +23,7 @@ namespace bool apiUtils::isSubscriptionExpired(const QString &subscriptionEndDate) { - QDateTime now = QDateTime::currentDateTime(); + QDateTime now = QDateTime::currentDateTimeUtc(); QDateTime endDate = QDateTime::fromString(subscriptionEndDate, Qt::ISODateWithMs); return endDate < now; } diff --git a/client/core/defs.h b/client/core/defs.h index 64f52ce67..75dd26afd 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -120,6 +120,7 @@ namespace amnezia ApiNotFoundError = 1109, ApiMigrationError = 1110, ApiUpdateRequestError = 1111, + ApiSubscriptionExpiredError = 1112, // QFile errors OpenError = 1200, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index bd5ccaba6..c29187fb0 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -77,6 +77,7 @@ QString errorString(ErrorCode code) { case (ErrorCode::ApiNotFoundError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break; case (ErrorCode::ApiMigrationError): errorMessage = QObject::tr("A migration error has occurred. Please contact our technical support"); break; case (ErrorCode::ApiUpdateRequestError): errorMessage = QObject::tr("Please update the application to use this feature"); break; + case (ErrorCode::ApiSubscriptionExpiredError): errorMessage = QObject::tr("Your Amnezia Premium subscription has expired.\n Please check your email for renewal instructions.\n If you haven't received an email, please contact our support."); break; // QFile errors case(ErrorCode::OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break; diff --git a/client/ui/controllers/api/apiConfigsController.cpp b/client/ui/controllers/api/apiConfigsController.cpp index b1e3ad7ac..e543edf10 100644 --- a/client/ui/controllers/api/apiConfigsController.cpp +++ b/client/ui/controllers/api/apiConfigsController.cpp @@ -44,6 +44,9 @@ namespace constexpr char authData[] = "auth_data"; constexpr char config[] = "config"; + + constexpr char subscription[] = "subscription"; + constexpr char endDate[] = "end_date"; } struct ProtocolData @@ -164,7 +167,7 @@ namespace auto clientProtocolConfig = QJsonDocument::fromJson(serverProtocolConfig.value(config_key::last_config).toString().toUtf8()).object(); - //TODO looks like this block can be removed after v1 configs EOL + // TODO looks like this block can be removed after v1 configs EOL serverProtocolConfig[config_key::junkPacketCount] = clientProtocolConfig.value(config_key::junkPacketCount); serverProtocolConfig[config_key::junkPacketMinSize] = clientProtocolConfig.value(config_key::junkPacketMinSize); @@ -224,6 +227,19 @@ namespace return ErrorCode::NoError; } + + bool isSubscriptionExpired(const QJsonObject &apiConfig) + { + auto subscription = apiConfig.value(configKey::subscription).toObject(); + if (subscription.isEmpty()) { + return false; + } + auto subscriptionEndDate = subscription.value(configKey::endDate).toString(); + if (apiUtils::isSubscriptionExpired(subscriptionEndDate)) { + return true; + } + return false; + } } ApiConfigsController::ApiConfigsController(const QSharedPointer &serversModel, @@ -243,6 +259,11 @@ bool ApiConfigsController::exportNativeConfig(const QString &serverCountryCode, auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex()); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); + if (isSubscriptionExpired(apiConfigObject)) { + emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError); + return false; + } + GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString(APP_VERSION), m_settings->getInstallationUuid(true), @@ -278,6 +299,11 @@ bool ApiConfigsController::revokeNativeConfig(const QString &serverCountryCode) auto serverConfigObject = m_serversModel->getServerConfig(m_serversModel->getProcessedServerIndex()); auto apiConfigObject = serverConfigObject.value(configKey::apiConfig).toObject(); + if (isSubscriptionExpired(apiConfigObject)) { + emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError); + return false; + } + GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString(APP_VERSION), m_settings->getInstallationUuid(true), @@ -398,6 +424,11 @@ bool ApiConfigsController::updateServiceFromGateway(const int serverIndex, const auto serverConfig = m_serversModel->getServerConfig(serverIndex); auto apiConfig = serverConfig.value(configKey::apiConfig).toObject(); + if (isSubscriptionExpired(apiConfig)) { + emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError); + return false; + } + GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString(APP_VERSION), m_settings->getInstallationUuid(true), @@ -504,6 +535,11 @@ bool ApiConfigsController::deactivateDevice() return true; } + if (isSubscriptionExpired(apiConfigObject)) { + emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError); + return false; + } + GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString(APP_VERSION), m_settings->getInstallationUuid(true), @@ -538,6 +574,11 @@ bool ApiConfigsController::deactivateExternalDevice(const QString &uuid, const Q return true; } + if (isSubscriptionExpired(apiConfigObject)) { + emit errorOccurred(ErrorCode::ApiSubscriptionExpiredError); + return false; + } + GatewayRequestData gatewayRequestData { QSysInfo::productType(), QString(APP_VERSION), uuid, diff --git a/client/ui/models/api/apiAccountInfoModel.cpp b/client/ui/models/api/apiAccountInfoModel.cpp index bd3027a45..bdd7d68d4 100644 --- a/client/ui/models/api/apiAccountInfoModel.cpp +++ b/client/ui/models/api/apiAccountInfoModel.cpp @@ -31,7 +31,7 @@ QVariant ApiAccountInfoModel::data(const QModelIndex &index, int role) const return tr("Active"); } - return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("Inactive") : tr("Active"); + return apiUtils::isSubscriptionExpired(m_accountInfoData.subscriptionEndDate) ? tr("

Inactive") : tr("Active"); } case EndDateRole: { if (m_accountInfoData.configType == apiDefs::ConfigType::AmneziaFreeV3) { diff --git a/client/ui/qml/Controls2/LabelWithImageType.qml b/client/ui/qml/Controls2/LabelWithImageType.qml index 57d60d8fc..9ad4168fe 100644 --- a/client/ui/qml/Controls2/LabelWithImageType.qml +++ b/client/ui/qml/Controls2/LabelWithImageType.qml @@ -7,17 +7,20 @@ import Style 1.0 import "TextTypes" RowLayout { + id: root + property string imageSource property string leftText property var rightText property bool isRightTextUndefined: rightText === undefined + property int rightTextFormat: Text.PlainText visible: !isRightTextUndefined Image { Layout.preferredHeight: 18 Layout.preferredWidth: 18 - source: imageSource + source: root.imageSource } ListItemTitleType { @@ -25,14 +28,15 @@ RowLayout { Layout.rightMargin: 10 Layout.alignment: Qt.AlignRight - text: leftText + text: root.leftText } ParagraphTextType { - visible: rightText !== "" + visible: root.rightText !== "" Layout.alignment: Qt.AlignLeft - text: isRightTextUndefined ? "" : rightText + text: root.isRightTextUndefined ? "" : root.rightText + textFormat: root.rightTextFormat } } diff --git a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml index 1e947bd24..3a7290a6c 100644 --- a/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml +++ b/client/ui/qml/Pages2/PageSettingsApiServerInfo.qml @@ -29,6 +29,7 @@ PageType { readonly property string title: qsTr("Subscription Status") readonly property string contentKey: "subscriptionStatus" readonly property string objectImageSource: "qrc:/images/controls/info.svg" + readonly property bool isRichText: true } QtObject { @@ -37,6 +38,7 @@ PageType { readonly property string title: qsTr("Valid Until") readonly property string contentKey: "endDate" readonly property string objectImageSource: "qrc:/images/controls/history.svg" + readonly property bool isRichText: false } QtObject { @@ -45,6 +47,7 @@ PageType { readonly property string title: qsTr("Active Connections") readonly property string contentKey: "connectedDevices" readonly property string objectImageSource: "qrc:/images/controls/monitor.svg" + readonly property bool isRichText: false } property var processedServer @@ -134,6 +137,7 @@ PageType { imageSource: objectImageSource leftText: title rightText: ApiAccountInfoModel.data(contentKey) + rightTextFormat: isRichText ? Text.RichText : Text.PlainText visible: rightText !== "" }