diff --git a/.env.example b/.env.example index 95691653b..52f9be028 100644 --- a/.env.example +++ b/.env.example @@ -2,6 +2,16 @@ # Usage: Copy this .env.example file to .env # +# +# App +# + +# Serial key value to pre-fill the activation dialog +# SYNERGY_TEST_SERIAL_KEY="DEADBEEF" + +# Version checker URL to use (useful for testing) +# SYNERGY_VERSION_URL="http://localhost:8787?version=v1" + # # Build # diff --git a/.github/workflows/build-containers.yml b/.github/workflows/build-containers.yml index e58494291..4818a877e 100644 --- a/.github/workflows/build-containers.yml +++ b/.github/workflows/build-containers.yml @@ -21,6 +21,9 @@ jobs: timeout-minutes: 10 strategy: + # Build all images even if some fail. + fail-fast: false + matrix: os: - name: debian-12-amd64 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..e8afb2b3b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "sonarlint.pathToCompileCommands": "${workspaceFolder}/build/compile_commands.json", + "gcovViewer.buildDirectories": ["${workspaceFolder}/build/src/lib"] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c1831d975..fe5319f30 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -10,6 +10,13 @@ "group": { "kind": "build", "isDefault": true + }, + "problemMatcher": { + "base": "$gcc", + "fileLocation": ["absolute"] + }, + "presentation": { + "revealProblems": "onProblem" } }, { @@ -20,6 +27,38 @@ "preset": "${command:cmake.activeBuildPresetName}", "group": "build" }, + { + "label": "clean-gcda", + "type": "shell", + "command": "find . -name '*.gcda' -delete", + "windows": { + "command": "$null" + }, + "presentation": { + "reveal": "silent" + } + }, + { + "label": "clean-qt", + "type": "shell", + "command": "rm -r build/src/gui build/src/lib/gui", + "windows": { + "command": "remove-item -recurse build/src/gui,build/src/lib/gui" + } + }, + { + "label": "clean-config", + "type": "shell", + "linux": { + "command": "rm -r ~/.config/Synergy/Synergy.conf" + }, + "windows": { + "command": "remove-item -recurse $env:APPDATA\\Synergy\\Synergy" + }, + "osx": { + "command": "rm -r ~/Library/Application\\ Support/Synergy/Synergy" + } + }, { "label": "tests", "dependsOn": ["integtests", "unittests"], @@ -68,25 +107,6 @@ "type": "shell", "command": "python scripts/daemon.py --stop" }, - { - "label": "clean-gcda", - "type": "shell", - "command": "find . -name '*.gcda' -delete", - "windows": { - "command": "$null" - }, - "presentation": { - "reveal": "silent" - } - }, - { - "label": "clean-qt", - "type": "shell", - "command": "rm -r build/src/gui", - "windows": { - "command": "remove-item -recurse build/src/gui" - } - }, { "label": "unittests (current)", "type": "shell", diff --git a/ChangeLog b/ChangeLog index 50f7dcc30..f0081e2aa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -59,6 +59,7 @@ Enhancements: - #7406 Migrate scripts from `requirements.txt` to `pyproject.toml` - #7407 Implement safer memory use, improve dev env, fixed GUI bugs - #7412 Reduce GUI compile time by building a GUI library +- #7413 Improve UI design and reduce over-use of `#ifdef` # 1.14.6 diff --git a/cmake/Definitions.cmake b/cmake/Definitions.cmake index 159a1b241..ebb2d524a 100644 --- a/cmake/Definitions.cmake +++ b/cmake/Definitions.cmake @@ -53,6 +53,9 @@ macro(configure_definitions) if(NOT "$ENV{SYNERGY_PRODUCT_NAME}" STREQUAL "") set(PRODUCT_NAME $ENV{SYNERGY_PRODUCT_NAME}) endif() + endif() + + if(PRODUCT_NAME) message(STATUS "Product name: ${PRODUCT_NAME}") add_definitions(-DSYNERGY_PRODUCT_NAME="${PRODUCT_NAME}") endif() diff --git a/ext/googletest b/ext/googletest index 18f8200e3..e39786088 160000 --- a/ext/googletest +++ b/ext/googletest @@ -1 +1 @@ -Subproject commit 18f8200e3079b0e54fa00cb7ac55d4c39dcf6da6 +Subproject commit e39786088138f2749d64e9e90e0f9902daa77c40 diff --git a/src/cmd/synergyd/CMakeLists.txt b/src/cmd/synergyd/CMakeLists.txt index 545724242..8a391988f 100644 --- a/src/cmd/synergyd/CMakeLists.txt +++ b/src/cmd/synergyd/CMakeLists.txt @@ -36,7 +36,8 @@ target_link_libraries( net platform synlib - shared + global + license ${libs}) post_config() diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index e6e1da14b..68f1bf64c 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -28,18 +28,12 @@ file( src/validators/* src/widgets/*) file(GLOB GUI_UI_FILES src/*.ui) -file(GLOB ACTIVATION_FILES src/*Activation* src/*License*) if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") file(GLOB GUI_MAC_SOURCE_FILES src/*.mm) list(APPEND GUI_SOURCE_FILES ${GUI_MAC_SOURCE_FILES}) endif() -if(NOT ENABLE_LICENSING) - list(REMOVE_ITEM GUI_SOURCE_FILES ${ACTIVATION_FILES}) - list(REMOVE_ITEM GUI_UI_FILES ${ACTIVATION_FILES}) -endif() - if(WIN32) set(GUI_RC_FILES res/win/Synergy.rc ${CMAKE_BINARY_DIR}/src/version.rc) endif() @@ -56,8 +50,9 @@ include_directories(./src) target_link_libraries( ${target} - shared gui + global + license Qt6::Core Qt6::Widgets Qt6::Network) diff --git a/src/gui/src/AboutDialog.cpp b/src/gui/src/AboutDialog.cpp index 6b2423bc2..929e2c83c 100644 --- a/src/gui/src/AboutDialog.cpp +++ b/src/gui/src/AboutDialog.cpp @@ -18,21 +18,27 @@ #include "AboutDialog.h" +#include + #if defined(Q_OS_MAC) #include "OSXHelpers.h" #endif +#ifdef GIT_SHA_SHORT +const QString kVersionAppend = GIT_SHA_SHORT; +#else +const QString kVersionAppend; +#endif + AboutDialog::AboutDialog(MainWindow *parent, const AppConfig &config) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), Ui::AboutDialogBase() { setupUi(this); - m_versionChecker.setApp(parent->appPath(config.coreClientName())); - - QString version = SYNERGY_VERSION; -#ifdef GIT_SHA_SHORT - version += " (" GIT_SHA_SHORT ")"; -#endif + QString version(SYNERGY_VERSION); + if (!kVersionAppend.isEmpty()) { + version.append(QString(" (%1)").arg(kVersionAppend)); + } m_pLabelSynergyVersion->setText(version); @@ -84,7 +90,7 @@ QString AboutDialog::getImportantDevelopers() const { // Symless employees "Kyle Bloom, Daun Chung, Serhii Hadzhylov, " - "Oleksandr Lysytsia, Olena Kutytska, Francisco Magalhães."); + "Oleksandr Lysytsia, Olena Kutytska."); } QString AboutDialog::getCopyright() const { diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 5096a1928..63be610e2 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -16,45 +16,64 @@ */ #include "ActivationDialog.h" + +#include "gui/license_notices.h" +#include "ui_ActivationDialog.h" + #include "AppConfig.h" #include "CancelActivationDialog.h" -#include "LicenseManager.h" #include "MainWindow.h" -#include "shared/EditionType.h" -#include "ui_ActivationDialog.h" +#include "gui/LicenseHandler.h" +#include "gui/constants.h" +#include "gui/license_notices.h" +#include "license/ProductEdition.h" +#include "license/parse_serial_key.h" #include #include #include +using namespace synergy::gui; +using namespace synergy::license; + ActivationDialog::ActivationDialog( - QWidget *parent, AppConfig &appConfig, LicenseManager &licenseManager) + QWidget *parent, AppConfig &appConfig, LicenseHandler &licenseHandler) : QDialog(parent), - ui(new Ui::ActivationDialog), - m_appConfig(&appConfig), - m_LicenseManager(&licenseManager) { - ui->setupUi(this); + m_ui(new Ui::ActivationDialog), + m_pAppConfig(&appConfig), + m_licenseHandler(licenseHandler) { + + m_ui->setupUi(this); refreshSerialKey(); - time_t currentTime = ::time(0); - if (!m_LicenseManager->serialKey().isExpired(currentTime)) { - ui->m_trialWidget->hide(); + + if (!m_licenseHandler.license().isExpired()) { + m_ui->m_widgetNotice->hide(); + } + + const QString envSerialKey = ::getenv("SYNERGY_TEST_SERIAL_KEY"); + if (!envSerialKey.isEmpty()) { + qDebug() << "using env test serial key:" << envSerialKey; + m_ui->m_pTextEditSerialKey->setText(envSerialKey); } } void ActivationDialog::refreshSerialKey() { - ui->m_pTextEditSerialKey->setText(m_appConfig->serialKey()); - ui->m_pTextEditSerialKey->setFocus(); - ui->m_pTextEditSerialKey->moveCursor(QTextCursor::End); - ui->m_trialLabel->setText( - tr(m_LicenseManager->getLicenseNotice().toStdString().c_str())); + m_ui->m_pTextEditSerialKey->setText(m_pAppConfig->serialKey()); + m_ui->m_pTextEditSerialKey->setFocus(); + m_ui->m_pTextEditSerialKey->moveCursor(QTextCursor::End); + + const auto &license = m_licenseHandler.license(); + if (license.isTimeLimited()) { + m_ui->m_labelNotice->setText(licenseNotice(license)); + } } -ActivationDialog::~ActivationDialog() { delete ui; } +ActivationDialog::~ActivationDialog() { delete m_ui; } void ActivationDialog::reject() { // don't show the cancel confirmation dialog if they've already registered, // since it's not revent to customers who are changing their serial key. - if (m_LicenseManager->activeEdition() != kUnregistered) { + if (m_licenseHandler.productEdition() != Edition::kUnregistered) { QDialog::reject(); return; } @@ -67,56 +86,105 @@ void ActivationDialog::reject() { } void ActivationDialog::accept() { - QMessageBox message; - m_appConfig->setActivationHasRun(true); + using Result = LicenseHandler::ChangeSerialKeyResult; + auto serialKey = m_ui->m_pTextEditSerialKey->toPlainText(); - try { - SerialKey serialKey( - ui->m_pTextEditSerialKey->toPlainText().trimmed().toStdString()); - m_LicenseManager->setSerialKey(serialKey); - } catch (std::exception &e) { - message.critical( - this, "Activation failed", - tr("An error occurred while trying to activate Synergy. " - "" - "Please contact the helpdesk, and provide the following " - "information:" - "

%1") - .arg(e.what())); - refreshSerialKey(); + if (serialKey.isEmpty()) { + QMessageBox::information(this, "Activation", "Please enter a serial key."); return; } - m_LicenseManager->notifyActivation("serial:" + m_appConfig->serialKey()); - Edition edition = m_LicenseManager->activeEdition(); - time_t daysLeft = m_LicenseManager->serialKey().daysLeft(::time(0)); - if (edition != kUnregistered) { - QString thanksMessage = tr("Thanks for trying %1! %5\n\n%2 day%3 of " - "your trial remain%4") - .arg(m_LicenseManager->getEditionName(edition)) - .arg(daysLeft) - .arg((daysLeft == 1) ? "" : "s") - .arg((daysLeft == 1) ? "s" : ""); - - if (m_appConfig->cryptoAvailable()) { - m_appConfig->generateCertificate(); - thanksMessage = - thanksMessage.arg("If you're using SSL, " - "remember to activate all of your devices."); - } else { - thanksMessage = thanksMessage.arg(""); + try { + const auto result = m_licenseHandler.changeSerialKey(serialKey); + if (result != Result::kSuccess) { + showResultDialog(result); + return; } + } catch (const SerialKeyParseError &e) { + showErrorDialog(e.what()); + return; + } - if (m_LicenseManager->serialKey().isTrial()) { - message.information(this, "Thanks!", thanksMessage); - } else { - message.information( - this, "Activated!", - tr("Thanks for activating %1!") - .arg(m_LicenseManager->getEditionName(edition))); + showSuccessDialog(); + m_pAppConfig->setActivationHasRun(true); + QDialog::accept(); +} + +void ActivationDialog::showResultDialog( + LicenseHandler::ChangeSerialKeyResult result) { + const QString title = "Activation result"; + + switch (result) { + using enum LicenseHandler::ChangeSerialKeyResult; + + case kUnchanged: + QMessageBox::information( + this, title, + "Heads up, the serial key you entered was the same as last time."); + break; + + case kInvalid: + QMessageBox::critical( + this, title, + QString("Invalid serial key. " + R"(Please contact us for help.)") + .arg(kUrlContact) + .arg(kLinkStyleSecondary)); + break; + + case kExpired: + QMessageBox::warning( + this, title, + QString("Sorry, that serial key has expired. " + R"(Please renew your license.)") + .arg(kUrlPurchase) + .arg(kLinkStyleSecondary)); + break; + + default: + qFatal("unexpected change serial key result: %d", static_cast(result)); + } +} + +void ActivationDialog::showSuccessDialog() { + const auto &license = m_licenseHandler.license(); + + QString title = "Activation successful"; + QString message = tr("

Thanks for activating %1.

") + .arg(m_licenseHandler.productName()); + + TlsUtility tls(*m_pAppConfig, license); + if (tls.isAvailableAndEnabled()) { + message += + "

To ensure that TLS encryption works correctly, " + "please activate all of your computers with the same serial key.

"; + } + + if (license.isTimeLimited()) { + auto daysLeft = license.daysLeft().count(); + if (license.isTrial()) { + title = "Trial started"; + message += QString("Your trial will expire in %1 %2.") + .arg(daysLeft) + .arg((daysLeft == 1) ? "day" : "days"); + } else if (license.isSubscription()) { + message += QString("Your license will expire in %1 %2.") + .arg(daysLeft) + .arg((daysLeft == 1) ? "day" : "days"); } } - QDialog::accept(); + QMessageBox::information(this, title, message); +} + +void ActivationDialog::showErrorDialog(const QString &message) { + QString fullMessage = + QString("

There was a problem activating Synergy.

" + R"(

Please contact us )" + "and provide the following information:

" + "%3") + .arg(kUrlContact) + .arg(kLinkStyleSecondary) + .arg(message); + QMessageBox::critical(this, "Activation failed", fullMessage); } diff --git a/src/gui/src/ActivationDialog.h b/src/gui/src/ActivationDialog.h index ff50ebe56..ec3f3d31b 100644 --- a/src/gui/src/ActivationDialog.h +++ b/src/gui/src/ActivationDialog.h @@ -17,7 +17,8 @@ #pragma once -#include +#include "gui/LicenseHandler.h" + #include namespace Ui { @@ -31,8 +32,14 @@ class ActivationDialog : public QDialog { public: ActivationDialog( - QWidget *parent, AppConfig &appConfig, LicenseManager &licenseManager); - ~ActivationDialog(); + QWidget *parent, AppConfig &appConfig, LicenseHandler &licenseHandler); + ~ActivationDialog() override; + + class ActivationMessageError : public std::runtime_error { + public: + ActivationMessageError() + : std::runtime_error("could not show activation message") {} + }; public slots: void reject(); @@ -42,7 +49,11 @@ protected: void refreshSerialKey(); private: - Ui::ActivationDialog *ui; - AppConfig *m_appConfig; - LicenseManager *m_LicenseManager; + void showResultDialog(LicenseHandler::ChangeSerialKeyResult result); + void showSuccessDialog(); + void showErrorDialog(const QString &message); + + Ui::ActivationDialog *m_ui; + AppConfig *m_pAppConfig; + LicenseHandler &m_licenseHandler; }; diff --git a/src/gui/src/ActivationDialog.ui b/src/gui/src/ActivationDialog.ui index e89c2ac91..a60639797 100644 --- a/src/gui/src/ActivationDialog.ui +++ b/src/gui/src/ActivationDialog.ui @@ -6,7 +6,7 @@ 0 0 - 477 + 541 241 @@ -29,7 +29,7 @@ - <html><head/><body><p>Your serial key is on your <a href="https://symless.com/synergy/account?source=gui"><span style=" text-decoration: underline; color:#4285f4;">account</span></a> page. Don't have a license? <a href="https://symless.com/synergy/purchase?source=gui"><span style=" text-decoration: underline; color:#007af4;">Purchase Synergy</span></a></p></body></html> + <p>Your serial key is on your <a href="https://symless.com/synergy/account?source=gui" style="color:#4285f4;">account</span></a> page. Don't have a license? <a href="https://symless.com/synergy/purchase?source=gui" style="color:#4285f4;">Purchase Synergy</span></a></p> true @@ -51,7 +51,7 @@ p, li { white-space: pre-wrap; } hr { height: 1px; border-width: 0; } li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } -</style></head><body style=" font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;"><br /></p></body></html> @@ -60,7 +60,7 @@ li.checked::marker { content: "\2612"; } - + 2 @@ -75,18 +75,16 @@ li.checked::marker { content: "\2612"; } 8 - + - font-size: 13px; -font-weight: 700; -padding: 3px 5px; + padding: 3px 5px; border-radius: 3px; -background-color: #EC4C47; +background-color: #3B67D3; color: #ffffff; top: 3px; - + m_labelNotice true @@ -96,7 +94,7 @@ top: 3px; - Qt::Horizontal + Qt::Orientation::Horizontal @@ -112,10 +110,10 @@ top: 3px; - Qt::Horizontal + Qt::Orientation::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 81e45b2b4..2a1626b96 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -19,14 +19,13 @@ #include "AppConfig.h" #include "Config.h" -#include "SslCertificate.h" +#include "gui/constants.h" #include +#include #include #include #include -#include -#include using synergy::gui::Config; @@ -46,6 +45,7 @@ const char AppConfig::m_LogDir[] = "/var/log/"; const char AppConfig::m_ConfigFilename[] = "synergy.conf"; #endif +// TODO: instead, use key value pair table, which would be less fragile. const char *const AppConfig::m_SettingsName[] = { "screenName", "port", @@ -57,17 +57,17 @@ const char *const AppConfig::m_SettingsName[] = { "startedBefore", "elevateMode", "elevateModeEnum", - "edition", - "cryptoEnabled", + "", // 10 = edition, obsolete (using serial key instead) + "cryptoEnabled", // kTlsEnabled (retain legacy string value) "autoHide", "serialKey", "lastVersion", - "lastExpiringWarningTime", + "", // 15 = lastExpiringWarningTime, obsolete "activationHasRun", "minimizeToTray", - "ActivateEmail", + "", // 18 = ActivateEmail, obsolete "loadFromSystemScope", - "groupServerChecked", + "groupServerChecked", // kServerGroupChecked "useExternalConfig", "configFile", "useInternalConfig", @@ -78,23 +78,20 @@ const char *const AppConfig::m_SettingsName[] = { "preventSleep", "languageSync", "invertScrollDirection", - "guid", - "licenseRegistryUrl", + "", // 31 = guid, obsolete + "", // 32 = licenseRegistryUrl, obsolete "licenseNextCheck", - "initiateConnectionFromServer", - "clientHostMode", - "serverClientMode", + "initiateConnectionFromServer", // kInvertConnection + "", // 35 = clientHostMode, obsolete + "", // 36 = serverClientMode, obsolete "serviceEnabled", "closeToTray"}; static const char *logLevelNames[] = {"INFO", "DEBUG", "DEBUG1", "DEBUG2"}; -AppConfig::AppConfig() { - m_Config.registerReceiever(this); - load(); -} +AppConfig::AppConfig() { m_Config.registerReceiever(this); } -void AppConfig::load() { +void AppConfig::loadAllScopes() { m_Config.loadAll(); // User settings exist and the load from system scope variable is true @@ -112,6 +109,8 @@ void AppConfig::load() { void AppConfig::loadSettings() { using enum AppConfig::Setting; + qDebug("loading settings"); + m_ScreenName = loadSetting(kScreenName, QHostInfo::localHostName()).toString(); if (m_ScreenName.isEmpty()) { @@ -119,7 +118,7 @@ void AppConfig::loadSettings() { } m_Port = loadSetting(kPort, 24800).toInt(); - m_Interface = loadSetting(kInterfaceSetting).toString(); + m_Interface = loadSetting(kInterface).toString(); m_LogLevel = loadSetting(kLogLevel, 0).toInt(); m_LogToFile = loadSetting(kLogToFile, false).toBool(); m_LogFilename = @@ -130,98 +129,74 @@ void AppConfig::loadSettings() { QVariant elevateMode = loadSetting(kElevateModeEnum); if (!elevateMode.isValid()) { elevateMode = loadSetting( - kElevateModeSetting, QVariant(static_cast(kDefaultElevateMode))); + kElevateMode, QVariant(static_cast(kDefaultElevateMode))); } m_ElevateMode = static_cast(elevateMode.toInt()); - m_ActivateEmail = loadSetting(kActivateEmail, "").toString(); - m_CryptoEnabled = loadSetting(kCryptoEnabled, true).toBool(); m_AutoHide = loadSetting(kAutoHide, false).toBool(); m_LastVersion = loadSetting(kLastVersion, "Unknown").toString(); - m_LastExpiringWarningTime = loadSetting(kLastExpireWarningTime, 0).toInt(); m_ActivationHasRun = loadSetting(kActivationHasRun, false).toBool(); m_MinimizeToTray = loadSetting(kMinimizeToTray, false).toBool(); m_LoadFromSystemScope = loadCommonSetting(kLoadSystemSettings, false).toBool(); - m_ServerGroupChecked = loadSetting(kGroupServerCheck, false).toBool(); + m_ServerGroupChecked = loadSetting(kServerGroupChecked, false).toBool(); m_UseExternalConfig = loadSetting(kUseExternalConfig, false).toBool(); m_ConfigFile = loadSetting(kConfigFile, QDir::homePath() + "/" + m_ConfigFilename) .toString(); m_UseInternalConfig = loadSetting(kUseInternalConfig, false).toBool(); - m_ClientGroupChecked = loadSetting(kGroupClientCheck, false).toBool(); + m_ClientGroupChecked = loadSetting(kClientGroupChecked, false).toBool(); m_ServerHostname = loadSetting(kServerHostname).toString(); m_PreventSleep = loadSetting(kPreventSleep, false).toBool(); m_LanguageSync = loadSetting(kLanguageSync, false).toBool(); m_InvertScrollDirection = loadSetting(kInvertScrollDirection, false).toBool(); - m_Guid = loadCommonSetting(kGuid, QUuid::createUuid()).toString(); m_licenseNextCheck = loadCommonSetting(kLicenseNextCheck, 0).toULongLong(); - m_ClientHostMode = loadSetting(kClientHostMode, true).toBool(); - m_ServerClientMode = loadSetting(kServerClientMode, true).toBool(); - m_InitiateConnectionFromServer = - loadSetting(kInitiateConnectionFromServer, false).toBool(); + m_InvertConnection = loadSetting(kInvertConnection, false).toBool(); // only change the serial key if the settings being loaded contains a key - bool updateSerial = m_Config.hasSetting( + bool loadSerial = m_Config.hasSetting( settingName(kLoadSystemSettings), Config::Scope::Current); - // if the setting exists and is not empty - updateSerial = updateSerial && - !loadSetting(kSerialKey, "").toString().trimmed().isEmpty(); - if (updateSerial) { - m_Serialkey = loadSetting(kSerialKey, "").toString().trimmed(); - m_Edition = static_cast( - loadSetting(kEditionSetting, kUnregistered).toInt()); + if (loadSerial) { + const auto &serialKey = loadSetting(kSerialKey, "").toString().trimmed(); + if (!serialKey.isEmpty()) { + m_SerialKey = serialKey; + } } m_ServiceEnabled = loadSetting(kServiceEnabled, m_ServiceEnabled).toBool(); m_CloseToTray = loadSetting(kCloseToTray, m_CloseToTray).toBool(); + m_TlsEnabled = loadSetting(kTlsEnabled, m_TlsEnabled).toBool(); + m_TlsCertPath = loadSetting(kTlsCertPath, defaultTlsCertPath()).toString(); + m_TlsKeyLength = loadSetting(kTlsKeyLength, m_TlsKeyLength).toString(); - try { - // Set the default path of the TLS certificate file in the users DIR - QString certificateFilename = - QString("%1/%2/%3") - .arg(m_CoreInterface.getProfileDir(), "SSL", "Synergy.pem"); - - m_TlsCertPath = loadSetting(kTlsCertPath, certificateFilename).toString(); - m_TlsKeyLength = loadSetting(kTlsKeyLength, "2048").toString(); - } catch (const std::exception &e) { - qDebug() << e.what(); - qFatal("Failed to get profile dir, unable to configure TLS"); - } - - if (cryptoEnabled()) { - generateCertificate(); - } - - applyAppSettings(); + emit loaded(); } void AppConfig::saveSettings() { using enum Setting; + qDebug("saving settings"); + setCommonSetting(kWizardLastRun, m_WizardLastRun); setCommonSetting(kLoadSystemSettings, m_LoadFromSystemScope); - setCommonSetting(kGroupClientCheck, m_ClientGroupChecked); - setCommonSetting(kGroupServerCheck, m_ServerGroupChecked); - setCommonSetting(kGuid, m_Guid); + setCommonSetting(kClientGroupChecked, m_ClientGroupChecked); + setCommonSetting(kServerGroupChecked, m_ServerGroupChecked); setCommonSetting(kLicenseNextCheck, m_licenseNextCheck); if (isWritable()) { setSetting(kScreenName, m_ScreenName); setSetting(kPort, m_Port); - setSetting(kInterfaceSetting, m_Interface); + setSetting(kInterface, m_Interface); setSetting(kLogLevel, m_LogLevel); setSetting(kLogToFile, m_LogToFile); setSetting(kLogFilename, m_LogFilename); setSetting(kStartedBefore, m_StartedBefore); setSetting(kElevateModeEnum, static_cast(m_ElevateMode)); - setSetting(kEditionSetting, m_Edition); - setSetting(kCryptoEnabled, m_CryptoEnabled); + setSetting(kTlsEnabled, m_TlsEnabled); setSetting(kAutoHide, m_AutoHide); - setSetting(kSerialKey, m_Serialkey); + setSetting(kSerialKey, m_SerialKey); setSetting(kLastVersion, m_LastVersion); - setSetting(kLastExpireWarningTime, m_LastExpiringWarningTime); setSetting(kActivationHasRun, m_ActivationHasRun); setSetting(kMinimizeToTray, m_MinimizeToTray); setSetting(kUseExternalConfig, m_UseExternalConfig); @@ -231,17 +206,28 @@ void AppConfig::saveSettings() { setSetting(kPreventSleep, m_PreventSleep); setSetting(kLanguageSync, m_LanguageSync); setSetting(kInvertScrollDirection, m_InvertScrollDirection); - setSetting(kClientHostMode, m_ClientHostMode); - setSetting(kServerClientMode, m_ServerClientMode); + setSetting(kInvertConnection, m_InvertConnection); setSetting(kServiceEnabled, m_ServiceEnabled); setSetting(kCloseToTray, m_CloseToTray); // See enum ElevateMode declaration to understand why this setting is bool - setSetting(kElevateModeSetting, m_ElevateMode == ElevateAlways); + setSetting(kElevateMode, m_ElevateMode == ElevateAlways); } setModified(false); - applyAppSettings(); + saved(); + + if (m_TlsChanged) { + m_TlsChanged = false; + emit tlsChanged(); + } +} + +QString AppConfig::defaultTlsCertPath() const { + QDir path(m_CoreInterface.getProfileDir()); + path = path.filePath("SSL"); + path = path.filePath("Synergy.pem"); + return path.absolutePath(); } QString AppConfig::settingName(Setting name) { @@ -293,13 +279,15 @@ void AppConfig::loadScope(Config::Scope scope) { } } -void AppConfig::setDefaultValues() { m_InitiateConnectionFromServer = false; } +void AppConfig::setDefaultValues() { m_InvertConnection = false; } void AppConfig::setLoadFromSystemScope(bool value) { if (value) { + qDebug("loading system settings scope"); loadScope(Config::Scope::System); } else { + qDebug("loading user settings scope"); loadScope(Config::Scope::User); } @@ -323,59 +311,132 @@ void AppConfig::setSettingModified(T &variable, const T &newValue) { setModified(true); } } +QString AppConfig::logDir() const { + // by default log to home dir + return QDir::home().absolutePath() + "/"; +} -void AppConfig::generateCertificate(bool forceGeneration) const { - try { - SslCertificate sslCertificate; - sslCertificate.generateCertificate( - tlsCertPath(), tlsKeyLength(), forceGeneration); - emit sslToggled(); - } catch (const std::exception &e) { - qDebug() << e.what(); - qFatal("Failed to configure TLS"); +void AppConfig::persistLogDir() const { + QDir dir = logDir(); + + // persist the log directory + if (!dir.exists()) { + dir.mkpath(dir.path()); } } -void AppConfig::applyAppSettings() const { - QApplication::setQuitOnLastWindowClosed(!m_CloseToTray); -} - /////////////////////////////////////////////////////////////////////////////// -// Begin getters and setters +// Begin getters /////////////////////////////////////////////////////////////////////////////// -#ifdef SYNERGY_ENABLE_LICENSING - bool AppConfig::activationHasRun() const { return m_ActivationHasRun; } +QString AppConfig::serialKey() const { return m_SerialKey; } + +Config &AppConfig::config() { return m_Config; } + +const QString &AppConfig::screenName() const { return m_ScreenName; } + +int AppConfig::port() const { return m_Port; } + +const QString &AppConfig::networkInterface() const { return m_Interface; } + +int AppConfig::logLevel() const { return m_LogLevel; } + +bool AppConfig::logToFile() const { return m_LogToFile; } + +const QString &AppConfig::logFilename() const { return m_LogFilename; } + +QString AppConfig::logLevelText() const { return logLevelNames[logLevel()]; } + +ProcessMode AppConfig::processMode() const { + return m_ServiceEnabled ? ProcessMode::kService : ProcessMode::kDesktop; +} + +bool AppConfig::wizardShouldRun() const { + return m_WizardLastRun < kWizardVersion; +} + +bool AppConfig::startedBefore() const { return m_StartedBefore; } + +QString AppConfig::lastVersion() const { return m_LastVersion; } + +QString AppConfig::coreServerName() const { return m_CoreServerName; } + +QString AppConfig::coreClientName() const { return m_CoreClientName; } + +ElevateMode AppConfig::elevateMode() const { return m_ElevateMode; } + +bool AppConfig::tlsEnabled() const { return m_TlsEnabled; } + +bool AppConfig::autoHide() const { return m_AutoHide; } + +bool AppConfig::invertScrollDirection() const { + return m_InvertScrollDirection; +} + +unsigned long long AppConfig::licenseNextCheck() const { + return m_licenseNextCheck; +} + +bool AppConfig::languageSync() const { return m_LanguageSync; } + +bool AppConfig::preventSleep() const { return m_PreventSleep; } + +bool AppConfig::invertConnection() const { return m_InvertConnection; } + +bool AppConfig::minimizeToTray() const { return m_MinimizeToTray; } + +QString AppConfig::tlsCertPath() const { return m_TlsCertPath; } + +QString AppConfig::tlsKeyLength() const { return m_TlsKeyLength; } + +bool AppConfig::serviceEnabled() const { return m_ServiceEnabled; } + +bool AppConfig::closeToTray() const { return m_CloseToTray; } + +bool AppConfig::serverGroupChecked() const { return m_ServerGroupChecked; } + +bool AppConfig::useExternalConfig() const { return m_UseExternalConfig; } + +const QString &AppConfig::configFile() const { return m_ConfigFile; } + +bool AppConfig::useInternalConfig() const { return m_UseInternalConfig; } + +bool AppConfig::clientGroupChecked() const { return m_ClientGroupChecked; } + +QString AppConfig::serverHostname() const { return m_ServerHostname; } + void AppConfig::setActivationHasRun(bool value) { m_ActivationHasRun = value; } -void AppConfig::setEdition(Edition e) { - setSettingModified(m_Edition, e); - setCommonSetting(Setting::kEditionSetting, m_Edition); +/////////////////////////////////////////////////////////////////////////////// +// End getters +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Begin setters +/////////////////////////////////////////////////////////////////////////////// + +void AppConfig::clearSerialKey() { m_SerialKey.clear(); } + +void AppConfig::setTlsEnabled(bool value) { + setSettingModified(m_TlsEnabled, value); + m_TlsChanged = true; } -Edition AppConfig::edition() const { return m_Edition; } - -void AppConfig::setSerialKey(const QString &serial) { - setSettingModified(m_Serialkey, serial); - setCommonSetting(Setting::kSerialKey, m_Serialkey); +void AppConfig::setTlsCertPath(const QString &value) { + setSettingModified(m_TlsCertPath, value); + m_TlsChanged = true; } -void AppConfig::clearSerialKey() { m_Serialkey.clear(); } - -QString AppConfig::serialKey() const { return m_Serialkey; } - -int AppConfig::lastExpiringWarningTime() const { - return m_LastExpiringWarningTime; +void AppConfig::setTlsKeyLength(const QString &value) { + setSettingModified(m_TlsKeyLength, value); + m_TlsChanged = true; } - -void AppConfig::setLastExpiringWarningTime(int newValue) { - setSettingModified(m_LastExpiringWarningTime, newValue); +void AppConfig::setSerialKey(const QString &serialKey) { + setSettingModified(m_SerialKey, serialKey); + setCommonSetting(Setting::kSerialKey, m_SerialKey); } - -#endif // SYNERGY_ENABLE_LICENSING - void AppConfig::setServerGroupChecked(bool newValue) { setSettingModified(m_ServerGroupChecked, newValue); } @@ -399,57 +460,6 @@ void AppConfig::setClientGroupChecked(bool newValue) { void AppConfig::setServerHostname(const QString &newValue) { setSettingModified(m_ServerHostname, newValue); } - -void AppConfig::setClientHostMode(bool newValue) { - setSettingModified(m_ClientHostMode, newValue); -} - -void AppConfig::setServerClientMode(bool newValue) { - setSettingModified(m_ServerClientMode, newValue); -} - -Config &AppConfig::config() { return m_Config; } - -const QString &AppConfig::screenName() const { return m_ScreenName; } - -int AppConfig::port() const { return m_Port; } - -const QString &AppConfig::networkInterface() const { return m_Interface; } - -int AppConfig::logLevel() const { return m_LogLevel; } - -bool AppConfig::logToFile() const { return m_LogToFile; } - -const QString &AppConfig::logFilename() const { return m_LogFilename; } - -QString AppConfig::logDir() const { - // by default log to home dir - return QDir::home().absolutePath() + "/"; -} - -void AppConfig::persistLogDir() const { - QDir dir = logDir(); - - // persist the log directory - if (!dir.exists()) { - dir.mkpath(dir.path()); - } -} - -QString AppConfig::logLevelText() const { return logLevelNames[logLevel()]; } - -ProcessMode AppConfig::processMode() const { - return m_ServiceEnabled ? ProcessMode::kService : ProcessMode::kDesktop; -} - -bool AppConfig::wizardShouldRun() const { - return m_WizardLastRun < kWizardVersion; -} - -bool AppConfig::startedBefore() const { return m_StartedBefore; } - -QString AppConfig::lastVersion() const { return m_LastVersion; } - void AppConfig::setLastVersion(const QString &version) { setSettingModified(m_LastVersion, version); } @@ -485,61 +495,16 @@ void AppConfig::setElevateMode(ElevateMode em) { setSettingModified(m_ElevateMode, em); } -QString AppConfig::coreServerName() const { return m_CoreServerName; } - -QString AppConfig::coreClientName() const { return m_CoreClientName; } - -ElevateMode AppConfig::elevateMode() { return m_ElevateMode; } - -void AppConfig::setCryptoEnabled(bool newValue) { - if (m_CryptoEnabled != newValue && newValue) { - generateCertificate(); - } else { - emit sslToggled(); - } - setSettingModified(m_CryptoEnabled, newValue); -} - -bool AppConfig::cryptoAvailable() const { - bool result{true}; - -#ifdef SYNERGY_ENABLE_LICENSING - result = - (edition() == kPro || edition() == kProChina || edition() == kBusiness || - edition() == kUltimate); -#endif // SYNERGY_ENABLE_LICENSING - - return result; -} - -bool AppConfig::cryptoEnabled() const { - return cryptoAvailable() && m_CryptoEnabled; -} - void AppConfig::setAutoHide(bool b) { setSettingModified(m_AutoHide, b); } -bool AppConfig::autoHide() const { return m_AutoHide; } - void AppConfig::setMinimizeToTray(bool newValue) { setSettingModified(m_MinimizeToTray, newValue); } -bool AppConfig::invertScrollDirection() const { - return m_InvertScrollDirection; -} - void AppConfig::setLicenseNextCheck(unsigned long long time) { setSettingModified(m_licenseNextCheck, time); } -unsigned long long AppConfig::licenseNextCheck() const { - return m_licenseNextCheck; -} - -const QString &AppConfig::guid() const { return m_Guid; } - -bool AppConfig::languageSync() const { return m_LanguageSync; } - void AppConfig::setInvertScrollDirection(bool newValue) { setSettingModified(m_InvertScrollDirection, newValue); } @@ -548,63 +513,23 @@ void AppConfig::setLanguageSync(bool newValue) { setSettingModified(m_LanguageSync, newValue); } -bool AppConfig::preventSleep() const { return m_PreventSleep; } - -bool AppConfig::clientHostMode() const { - return (m_ClientHostMode && initiateConnectionFromServer()); -} - -bool AppConfig::serverClientMode() const { - return (m_ServerClientMode && initiateConnectionFromServer()); -} - -bool AppConfig::initiateConnectionFromServer() const { - return m_InitiateConnectionFromServer; -} - void AppConfig::setPreventSleep(bool newValue) { setSettingModified(m_PreventSleep, newValue); } -bool AppConfig::minimizeToTray() const { return m_MinimizeToTray; } - -void AppConfig::setTlsCertPath(const QString &path) { m_TlsCertPath = path; } - -QString AppConfig::tlsCertPath() const { return m_TlsCertPath; } - -QString AppConfig::tlsKeyLength() const { return m_TlsKeyLength; } - -void AppConfig::setTlsKeyLength(const QString &length) { - if (m_TlsKeyLength != length) { - m_TlsKeyLength = length; - generateCertificate(true); - } -} - void AppConfig::setServiceEnabled(bool enabled) { setSettingModified(m_ServiceEnabled, enabled); } -bool AppConfig::serviceEnabled() const { return m_ServiceEnabled; } - void AppConfig::setCloseToTray(bool minimize) { setSettingModified(m_CloseToTray, minimize); } -bool AppConfig::closeToTray() const { return m_CloseToTray; } - -bool AppConfig::serverGroupChecked() const { return m_ServerGroupChecked; } - -bool AppConfig::useExternalConfig() const { return m_UseExternalConfig; } - -const QString &AppConfig::configFile() const { return m_ConfigFile; } - -bool AppConfig::useInternalConfig() const { return m_UseInternalConfig; } - -bool AppConfig::clientGroupChecked() const { return m_ClientGroupChecked; } - -QString AppConfig::serverHostname() const { return m_ServerHostname; } +void AppConfig::setInvertConnection(bool value) { + setSettingModified(m_InvertConnection, value); + emit invertConnectionChanged(); +} /////////////////////////////////////////////////////////////////////////////// -// End getters and setters +// End setters /////////////////////////////////////////////////////////////////////////////// diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 294801176..d2797138a 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -20,10 +20,9 @@ #include "CommonConfig.h" #include "Config.h" -#include "CoreInterface.h" -#include "LicenseManager.h" +#include "gui/CoreInterface.h" #include "gui/ElevateMode.h" -#include "shared/EditionType.h" +#include "gui/IAppConfig.h" #include #include @@ -32,11 +31,9 @@ class QSettings; class SettingsDialog; -class LicenseRegister; class ServerConfig; -class LicenseManager; +class LicenseHandler; class ActivationDialog; -class LicenseRegistry; enum class ProcessMode { kService, kDesktop }; @@ -48,17 +45,67 @@ const ProcessMode kDefaultProcessMode = ProcessMode::kService; const ProcessMode kDefaultProcessMode = ProcessMode::kDesktop; #endif -/// @brief Reads and writes application specific settings -class AppConfig : public QObject, public synergy::gui::CommonConfig { +/** + * @brief Simply reads and writes app settings. + * + * Important: Maintain a clear separation of concerns and keep it simple. + * It is tempting to add logic (e.g. license checks) to this class since it + * instance is widely accessible, but that has previously led to this class + * becoming a god object. + */ +class AppConfig : public QObject, + public synergy::gui::CommonConfig, + public synergy::gui::IAppConfig { Q_OBJECT friend class SettingsDialog; friend class MainWindow; friend class SetupWizard; friend class ServerConfig; - friend class LicenseManager; friend class ActivationDialog; - friend class LicenseRegistry; + +private: + enum class Setting { + kScreenName = 0, + kPort = 1, + kInterface = 2, + kLogLevel = 3, + kLogToFile = 4, + kLogFilename = 5, + kWizardLastRun = 6, + kStartedBefore = 7, + kElevateMode = 8, + kElevateModeEnum = 9, + // 10 = edition, obsolete (using serial key instead) + kTlsEnabled = 11, + kAutoHide = 12, + kSerialKey = 13, + kLastVersion = 14, + // 15 = last expire time, obsolete + kActivationHasRun = 16, + kMinimizeToTray = 17, + // 18 = activate email, obsolete + kLoadSystemSettings = 19, + kServerGroupChecked = 20, + kUseExternalConfig = 21, + kConfigFile = 22, + kUseInternalConfig = 23, + kClientGroupChecked = 24, + kServerHostname = 25, + kTlsCertPath = 26, + kTlsKeyLength = 27, + kPreventSleep = 28, + kLanguageSync = 29, + kInvertScrollDirection = 30, + // 31 = guid, obsolete + // 32 = license registry url, obsolete + kLicenseNextCheck = 33, + kInvertConnection = 34, + // 35 = client-host-mode, obsolete + // 36 = server-client-mode, obsolete + kServiceEnabled = 37, + kCloseToTray = 38, + }; public: explicit AppConfig(); @@ -66,12 +113,8 @@ public: /// @brief Underlying configuration reader/writer synergy::gui::Config &config(); + /// @brief Saves the setting to the current scope void saveSettings() override; - void applyAppSettings() const; - - /// @brief Generates TLS certificate - /// @param [in] bool forceGeneration Generate certificate even if it's exists. - void generateCertificate(bool forceGeneration = false) const; /** * Getters @@ -93,18 +136,13 @@ public: QString coreClientName() const; QString logDir() const; void persistLogDir() const; - ElevateMode elevateMode(); - bool cryptoAvailable() const; - bool cryptoEnabled() const; + ElevateMode elevateMode() const; bool autoHide() const; bool invertScrollDirection() const; unsigned long long licenseNextCheck() const; - const QString &guid() const; bool languageSync() const; bool preventSleep() const; - bool clientHostMode() const; - bool serverClientMode() const; - bool initiateConnectionFromServer() const; + bool invertConnection() const; bool serverGroupChecked() const; bool useExternalConfig() const; const QString &configFile() const; @@ -115,70 +153,18 @@ public: bool serviceEnabled() const; bool minimizeToTray() const; bool closeToTray() const; - - /// @brief Gets the current TLS certificate path - /// @return QString The path to the cert - QString tlsCertPath() const; - - /// @brief Get the key length to be used for the private key of a TLS cert - /// @return QString The key length in bits - QString tlsKeyLength() const; - -#ifdef SYNERGY_ENABLE_LICENSING - Edition edition() const; QString serialKey() const; - int lastExpiringWarningTime() const; bool activationHasRun() const; -#endif - -protected: - enum class Setting { - kScreenName, - kPort, - kInterfaceSetting, - kLogLevel, - kLogToFile, - kLogFilename, - kWizardLastRun, - kStartedBefore, - kElevateModeSetting, - kElevateModeEnum, - kEditionSetting, - kCryptoEnabled, - kAutoHide, - kSerialKey, - kLastVersion, - kLastExpireWarningTime, - kActivationHasRun, - kMinimizeToTray, - kActivateEmail, - kLoadSystemSettings, - kGroupServerCheck, - kUseExternalConfig, - kConfigFile, - kUseInternalConfig, - kGroupClientCheck, - kServerHostname, - kTlsCertPath, - kTlsKeyLength, - kPreventSleep, - kLanguageSync, - kInvertScrollDirection, - kGuid, - kLicenseRegistryUrl, - kLicenseNextCheck, - kInitiateConnectionFromServer, - kClientHostMode, - kServerClientMode, - kServiceEnabled, - kCloseToTray - }; - - static QString settingName(AppConfig::Setting name); + bool tlsEnabled() const override; + QString tlsCertPath() const override; + QString tlsKeyLength() const override; +private: /// @brief Loads the setting from the current scope void loadSettings() override; + static QString settingName(AppConfig::Setting name); + /** * Setters */ @@ -192,11 +178,9 @@ protected: void setWizardHasRun(); void setStartedBefore(bool b); void setElevateMode(ElevateMode em); - void setCryptoEnabled(bool e); - void setEdition(Edition); - void setSerialKey(const QString &serial); + void setTlsEnabled(bool e); + void setSerialKey(const QString &serialKey); void clearSerialKey(); - void setLastExpiringWarningTime(int t); void setAutoHide(bool b); void setInvertScrollDirection(bool b); void setLicenseNextCheck(unsigned long long); @@ -208,13 +192,14 @@ protected: void setUseInternalConfig(bool); void setClientGroupChecked(bool); void setServerHostname(const QString &); - void setClientHostMode(bool newValue); - void setServerClientMode(bool newValue); void setMinimizeToTray(bool b); void setLastVersion(const QString &version); void setServiceEnabled(bool enabled); void setCloseToTray(bool minimize); void setActivationHasRun(bool value); + void setTlsCertPath(const QString &path); + void setTlsKeyLength(const QString &length); + void setInvertConnection(bool value); /// @brief Sets the user preference to load from SystemScope. /// @param [in] value @@ -223,17 +208,8 @@ protected: /// scope settings. void setLoadFromSystemScope(bool value); - /// @brief Set the path to the TLS/SSL certificate file that will be used - /// @param [in] path The path to the Certificate - void setTlsCertPath(const QString &path); - - /// @brief Sets the key length of the private key to use in a TLS connection - /// @param [in] QString length The key length eg: 1024, 2048, 4096 - void setTlsKeyLength(const QString &length); - -private: /// @brief Loads config from the underlying reader/writer - void load(); + void loadAllScopes(); /// @brief Sets the value of a setting /// @param [in] name The Setting to be saved @@ -273,6 +249,11 @@ private: /// for settings that shouldn't be copied from between scopes. void setDefaultValues(); + /** + * @brief Gets a TLS certificate path based on the user's profile dir. + */ + QString defaultTlsCertPath() const; + synergy::gui::Config m_Config; CoreInterface m_CoreInterface; QString m_ScreenName = ""; @@ -284,23 +265,18 @@ private: int m_WizardLastRun = 0; bool m_StartedBefore = false; ElevateMode m_ElevateMode = kDefaultElevateMode; - Edition m_Edition = Edition::kUnregistered; QString m_ActivateEmail = ""; - bool m_CryptoEnabled = false; + bool m_TlsEnabled = true; bool m_AutoHide = false; - QString m_Serialkey = ""; + QString m_SerialKey = ""; QString m_LastVersion = ""; - QString m_Guid = ""; unsigned long long m_licenseNextCheck = 0; - int m_LastExpiringWarningTime = 0; bool m_ActivationHasRun = false; bool m_MinimizeToTray = true; bool m_InvertScrollDirection = false; bool m_LanguageSync = true; bool m_PreventSleep = false; - bool m_InitiateConnectionFromServer = false; - bool m_ClientHostMode = true; - bool m_ServerClientMode = true; + bool m_InvertConnection = false; bool m_ServerGroupChecked = false; bool m_UseExternalConfig = false; QString m_ConfigFile = ""; @@ -308,13 +284,15 @@ private: bool m_ClientGroupChecked = false; QString m_ServerHostname = ""; bool m_ServiceEnabled = kDefaultProcessMode == ProcessMode::kService; - bool m_CloseToTray = true; - - /// @brief The path to the TLS certificate file + bool m_CloseToTray = false; QString m_TlsCertPath = ""; + QString m_TlsKeyLength = "2048"; - /// @brief The key length of the TLS cert to make - QString m_TlsKeyLength = ""; + /** + * @brief Flag is set when any TLS is setting is changed, and is reset + * when the TLS changed event is emitted. + */ + bool m_TlsChanged = false; /// @brief should the setting be loaded from /// SystemScope @@ -340,6 +318,9 @@ private: static const char m_ConfigFilename[]; signals: - void sslToggled() const; - void screenNameChanged() const; + void loaded(); + void saved(); + void tlsChanged(); + void screenNameChanged(); + void invertConnectionChanged(); }; diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp deleted file mode 100644 index 1ea44b40a..000000000 --- a/src/gui/src/LicenseManager.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "LicenseManager.h" - -#include "ActivationNotifier.h" -#include "AppConfig.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace { - -std::string getMaintenanceMessage(const SerialKey &serialKey) { - auto expiration = - QDateTime::fromSecsSinceEpoch(serialKey.getExpiration()).date(); - QString message = - "The license key you used will only work with versions of Synergy " - "released before %1." - "

To use this version, you’ll need to renew your Synergy maintenance " - "license. " - "Renew today.

"; - auto formatedDate = QLocale("en_US").toString(expiration, "MMM dd yyyy"); - return message.arg(formatedDate).toStdString(); -} - -void checkSerialKey(const SerialKey &serialKey, bool acceptExpired) { - if (serialKey.isMaintenance()) { - auto buildDate = - QDateTime::fromString(__TIMESTAMP__, "ddd MMM dd hh:mm:ss yyyy") - .toSecsSinceEpoch(); - - if (buildDate > serialKey.getExpiration()) { - throw std::runtime_error(getMaintenanceMessage(serialKey)); - } - } - - if (!acceptExpired && serialKey.isExpired(::time(nullptr))) { - throw std::runtime_error("Serial key expired"); - } - -#ifdef SYNERGY_ENABLE_LICENSING - if (!serialKey.isValid()) { - throw std::runtime_error("The serial key is not valid."); - } -#endif // SYNERGY_ENABLE_LICENSING -} - -} // namespace - -LicenseManager::LicenseManager(AppConfig *appConfig) - : m_pAppConfig(appConfig), - m_serialKey(appConfig->edition()), - m_registry(*appConfig) {} - -void LicenseManager::setSerialKey(SerialKey serialKey, bool acceptExpired) { - checkSerialKey(serialKey, acceptExpired); - - if (serialKey != m_serialKey) { - using std::swap; - swap(serialKey, m_serialKey); - m_pAppConfig->setSerialKey(QString::fromStdString(m_serialKey.toString())); - - emit showLicenseNotice(getLicenseNotice()); - validateSerialKey(); - m_registry.scheduleRegistration(); - - if (m_serialKey.edition() != serialKey.edition()) { - m_pAppConfig->setEdition(m_serialKey.edition()); - emit editionChanged(m_serialKey.edition()); - } - } -} - -void LicenseManager::notifyUpdate( - QString fromVersion, QString toVersion) const { - if ((fromVersion == "Unknown") && (m_serialKey == SerialKey(kUnregistered))) { - return; - } - - ActivationNotifier *notifier = new ActivationNotifier(); - notifier->setUpdateInfo( - fromVersion, toVersion, QString::fromStdString(m_serialKey.toString())); - - QThread *thread = new QThread(); - connect(notifier, SIGNAL(finished()), thread, SLOT(quit())); - connect(notifier, SIGNAL(finished()), notifier, SLOT(deleteLater())); - connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - - notifier->moveToThread(thread); - thread->start(); - - QMetaObject::invokeMethod(notifier, "notifyUpdate", Qt::QueuedConnection); -} - -Edition LicenseManager::activeEdition() const { return m_serialKey.edition(); } - -QString LicenseManager::activeEditionName() const { - return getEditionName(activeEdition(), m_serialKey.isTrial()); -} - -const SerialKey &LicenseManager::serialKey() const { return m_serialKey; } - -void LicenseManager::refresh() { - if (!m_pAppConfig->serialKey().isEmpty()) { - try { - SerialKey serialKey(m_pAppConfig->serialKey().toStdString()); - setSerialKey(serialKey, true); - } catch (const std::exception &e) { - qDebug() << e.what(); - m_serialKey = SerialKey(); - m_pAppConfig->clearSerialKey(); - } - } - if (!m_serialKey.isValid()) { - emit InvalidLicense(); - } -} - -QString LicenseManager::getEditionName(Edition const edition, bool trial) { - SerialKeyEdition KeyEdition(edition); - std::string name = KeyEdition.getProductName(); - - if (trial) { - name += " (Trial)"; - } - - return QString::fromUtf8(name.c_str(), static_cast(name.size())); -} - -void LicenseManager::notifyActivation(QString identity) const { - ActivationNotifier *notifier = new ActivationNotifier(); - notifier->setIdentity(identity); - - QThread *thread = new QThread(); - connect(notifier, SIGNAL(finished()), thread, SLOT(quit())); - connect(notifier, SIGNAL(finished()), notifier, SLOT(deleteLater())); - connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - - notifier->moveToThread(thread); - thread->start(); - - QMetaObject::invokeMethod(notifier, "notify", Qt::QueuedConnection); -} - -QString LicenseManager::getLicenseNotice() const { - QString Notice; - - if (m_serialKey.isTemporary()) { - if (m_serialKey.isTrial()) { - Notice = getTrialNotice(); - } else { - Notice = getTemporaryNotice(); - } - } - - return Notice; -} - -void LicenseManager::registerLicense() { m_registry.registerLicense(); } - -QString LicenseManager::getTrialNotice() const { - QString Notice; - - if (m_serialKey.isExpired(::time(0))) { - Notice = "

" - "Trial expired - " - "Buy now" - "

"; - } else { - Notice = "

" - "Trial expires in %1 day%2 - " - "Buy now" - "

"; - - time_t daysLeft = m_serialKey.daysLeft(::time(0)); - Notice = Notice.arg(daysLeft).arg((daysLeft == 1) ? "" : "s"); - } - - return Notice; -} - -QString LicenseManager::getTemporaryNotice() const { - QString Notice; - - if (m_serialKey.isExpired(::time(0))) { - Notice = "

" - "License expired - " - "Renew now" - "

"; - } else if (m_serialKey.isExpiring(::time(0))) { - Notice = "

" - "License expires in %1 day%2 - " - "Renew now" - "

"; - - time_t daysLeft = m_serialKey.daysLeft(::time(0)); - Notice = Notice.arg(daysLeft).arg((daysLeft == 1) ? "" : "s"); - } - - return Notice; -} - -void LicenseManager::validateSerialKey() const { - if (m_serialKey.isValid()) { - if (m_serialKey.isTemporary()) { - QTimer::singleShot( - m_serialKey.getSpanLeft(), this, SLOT(validateSerialKey())); - } - } else { - emit InvalidLicense(); - } -} diff --git a/src/gui/src/LicenseManager.h b/src/gui/src/LicenseManager.h deleted file mode 100644 index 42caf1b70..000000000 --- a/src/gui/src/LicenseManager.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "LicenseRegistry.h" -#include "shared/EditionType.h" -#include "shared/SerialKey.h" - -#include - -class AppConfig; - -class LicenseManager : public QObject { - Q_OBJECT - -public: - LicenseManager(AppConfig *appConfig); - void setSerialKey(SerialKey serialKey, bool acceptExpired = false); - void refresh(); - Edition activeEdition() const; - QString activeEditionName() const; - const SerialKey &serialKey() const; - void notifyUpdate(QString fromVersion, QString toVersion) const; - static QString getEditionName(Edition edition, bool trial = false); - void notifyActivation(QString identity) const; - QString getLicenseNotice() const; - -private: - AppConfig *m_pAppConfig; - SerialKey m_serialKey; - LicenseRegistry m_registry; - -public slots: - void validateSerialKey() const; - void registerLicense(); - -signals: - void editionChanged(Edition) const; - void InvalidLicense() const; - void showLicenseNotice(const QString ¬ice) const; - -protected: - QString getTrialNotice() const; - QString getTemporaryNotice() const; -}; diff --git a/src/gui/src/LicenseRegistry.cpp b/src/gui/src/LicenseRegistry.cpp deleted file mode 100644 index 9b36708bc..000000000 --- a/src/gui/src/LicenseRegistry.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2022 Synergy Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include -#include -#include - -#include "AppConfig.h" -#include "LicenseRegistry.h" -#include - -const char *const kLicenseRegistryUrl = - "https://api2.prod.symless.com/license/register"; - -LicenseRegistry::LicenseRegistry(AppConfig &config) : m_config(config) { - connect(&m_timer, SIGNAL(timeout()), this, SLOT(registerLicense())); -} - -void LicenseRegistry::registerLicense() { - m_timer.stop(); - if (m_config.edition() == Edition::kBusiness) { - const auto url = QUrl(kLicenseRegistryUrl); - - auto request = QNetworkRequest(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - - m_manager.post(request, getRequestData()); - connect( - &m_manager, SIGNAL(finished(QNetworkReply *)), this, - SLOT(handleResponse(QNetworkReply *))); - } -} - -void LicenseRegistry::handleResponse(QNetworkReply *reply) { - if (reply) { - const auto HOUR = (60 * 60); // seconds per hour - const auto DAY = (24 * HOUR); // seconds per day - const auto WEEK = (7 * DAY); // seconds per week - const auto currentTimestamp = time(nullptr); - - if (reply->error() == QNetworkReply::NoError) { - m_config.setLicenseNextCheck(currentTimestamp + WEEK); - } else { - m_config.setLicenseNextCheck(currentTimestamp + HOUR); - } - - scheduleRegistration(); - reply->deleteLater(); - } -} - -QByteArray LicenseRegistry::getRequestData() const { -#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) - QString guid(QSysInfo::machineUniqueId()); -#else - QString guid; -#endif - - QJsonObject data; - if (!guid.isEmpty()) { - data["guid"] = guid; - data["guid_type"] = "system"; - } else { - data["guid"] = m_config.guid(); - data["guid_type"] = "synergy"; - } - - data["key"] = m_config.serialKey(); - data["is_server"] = m_config.serverGroupChecked(); - - return QJsonDocument(data).toJson(); -} - -void LicenseRegistry::scheduleRegistration() { - const auto nextCheck = m_config.licenseNextCheck(); - const auto currentTimestamp = static_cast(time(nullptr)); - - if (currentTimestamp >= nextCheck) { - registerLicense(); - } else { - const auto interval = - (nextCheck - currentTimestamp) * 1000; // interval in milliseconds - m_timer.setInterval(static_cast(interval)); - m_timer.setSingleShot(true); - m_timer.start(); - } -} diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 7297aa4b0..a5ff8c23a 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -20,17 +20,20 @@ #include "AboutDialog.h" #include "ActivationDialog.h" -#include "Fingerprint.h" -#include "LicenseManager.h" #include "ServerConfigDialog.h" #include "SettingsDialog.h" -#include "shared/EditionType.h" -#include +#include "gui/LicenseHandler.h" +#include "gui/TlsFingerprint.h" +#include "gui/VersionChecker.h" +#include "gui/constants.h" +#include "gui/license_notices.h" +#include "license/License.h" #if defined(Q_OS_MAC) #include "OSXHelpers.h" #endif +#include #include #include #include @@ -43,16 +46,24 @@ #include #include #include +#include #if defined(Q_OS_MAC) #include #endif -static const char *const kDownloadUrl = "https://symless.com/?source=gui"; -static const char *const kHelpUrl = "https://symless.com/help?source=gui"; +using namespace synergy::gui; +using namespace synergy::license; + static const int kRetryDelay = 1000; static const int kDebugLogLevel = 1; +#ifdef SYNERGY_PRODUCT_NAME +const QString kProductName = SYNERGY_PRODUCT_NAME; +#else +const QString kProductName; +#endif + #if defined(Q_OS_MAC) static const char *const kLightIconFiles[] = { @@ -78,132 +89,364 @@ static const char *const kDefaultIconFiles[] = { ":/res/icons/16x16/synergy-transfering.png", ":/res/icons/16x16/synergy-disconnected.png"}; -#ifdef SYNERGY_ENABLE_LICENSING -MainWindow::MainWindow(AppConfig &appConfig, LicenseManager &licenseManager) -#else MainWindow::MainWindow(AppConfig &appConfig) -#endif - : -#ifdef SYNERGY_ENABLE_LICENSING - m_LicenseManager(&licenseManager), -#endif - m_AppConfig(appConfig), + : m_AppConfig(appConfig), m_ServerConfig(5, 3, &m_AppConfig, this), - m_serverConnection(*this), - m_clientConnection(*this) { + m_ServerConnection(*this), + m_ClientConnection(*this), + m_TlsUtility(appConfig, m_LicenseHandler.license()) { setupUi(this); - -#if defined(Q_OS_MAC) - m_pRadioGroupServer->setAttribute(Qt::WA_MacShowFocusRect, 0); - m_pRadioGroupClient->setAttribute(Qt::WA_MacShowFocusRect, 0); -#endif - - m_ServerConfig.loadSettings(); - + connectSlots(); createMenuBar(); - loadSettings(); - initConnections(); + secureSocket(false); - m_pWidgetUpdate->hide(); - m_VersionChecker.setApp(appPath(appConfig.coreClientName())); + m_pLabelUpdate->hide(); - updateScreenName(); - connect( - appConfigPtr(), SIGNAL(screenNameChanged()), this, - SLOT(updateScreenName())); m_pLabelIpAddresses->setText( tr("This computer's IP addresses: %1").arg(getIPAddresses())); -#if defined(Q_OS_WIN) - // ipc must always be enabled, so that we can disable command when switching - // to desktop mode. - connect( - &m_IpcClient, SIGNAL(readLogLine(const QString &)), this, - SLOT(appendLogRaw(const QString &))); - connect( - &m_IpcClient, SIGNAL(errorMessage(const QString &)), this, - SLOT(appendLogError(const QString &))); - connect( - &m_IpcClient, SIGNAL(infoMessage(const QString &)), this, - SLOT(appendLogInfo(const QString &))); - connect( - &m_IpcClient, SIGNAL(readLogLine(const QString &)), this, - SLOT(handleIdleService(const QString &))); - m_IpcClient.connectToHost(); -#endif + m_labelNotice->hide(); - // change default size based on os -#if defined(Q_OS_MAC) - resize(720, 550); - setMinimumSize(size()); -#elif defined(Q_OS_LINUX) - resize(700, 530); - setMinimumSize(size()); -#endif - - m_trialLabel->hide(); - - // hide padlock icon - secureSocket(false); - - connect( - this, SIGNAL(windowShown()), this, SLOT(on_windowShown()), - Qt::QueuedConnection); -#ifdef SYNERGY_ENABLE_LICENSING - connect( - m_LicenseManager, SIGNAL(editionChanged(Edition)), this, - SLOT(setEdition(Edition)), Qt::QueuedConnection); - - connect( - m_LicenseManager, SIGNAL(showLicenseNotice(QString)), this, - SLOT(showLicenseNotice(QString)), Qt::QueuedConnection); - - connect( - m_LicenseManager, SIGNAL(InvalidLicense()), this, SLOT(InvalidLicense()), - Qt::QueuedConnection); -#endif - - connect( - appConfigPtr(), SIGNAL(sslToggled()), this, - SLOT(updateLocalFingerprint()), Qt::QueuedConnection); - - updateWindowTitle(); - - QString lastVersion = m_AppConfig.lastVersion(); - if (lastVersion != SYNERGY_VERSION) { + if (m_AppConfig.lastVersion() != SYNERGY_VERSION) { m_AppConfig.setLastVersion(SYNERGY_VERSION); - -#ifdef SYNERGY_ENABLE_LICENSING - m_LicenseManager->notifyUpdate(lastVersion, SYNERGY_VERSION); -#endif } -#ifndef SYNERGY_ENABLE_LICENSING - m_pActivate->setVisible(false); + if (kLicensingEnabled) { + m_pActivate->setVisible(true); + } + +#if defined(Q_OS_MAC) + + resize(720, 550); + setMinimumSize(size()); + + m_pRadioGroupServer->setAttribute(Qt::WA_MacShowFocusRect, 0); + m_pRadioGroupClient->setAttribute(Qt::WA_MacShowFocusRect, 0); + +#elif defined(Q_OS_LINUX) + + resize(700, 530); + setMinimumSize(size()); + +#elif defined(Q_OS_WIN) + + // TODO: only connect permenantly to ipc when switching to service mode. + // if switching from service to desktop, connect only to stop the service and + // don't retry. + m_IpcClient.connectToHost(); + #endif + + emit created(); } MainWindow::~MainWindow() { if (appConfig().processMode() == ProcessMode::kDesktop) { m_ExpectedRunningState = RuningState::Stopped; - try { - stopDesktop(); - } catch (const std::exception &e) { - qDebug() << e.what(); - qFatal("Failed to stop synergy desktop process"); + stopDesktop(); + } +} + +////////////////////////////////////////////////////////////////////////////// +// Begin slots +////////////////////////////////////////////////////////////////////////////// + +// remember: using queued connection allows the render loop to run before +// executing the slot. the default is to instantly call the slot when the +// signal is emitted from the thread that owns the receiver's object. +void MainWindow::connectSlots() const { + + connect(this, &MainWindow::created, this, &MainWindow::onCreated); + + connect( + this, &MainWindow::shown, this, &MainWindow::onShown, + Qt::QueuedConnection); + + connect( + &m_AppConfig, &AppConfig::loaded, this, &MainWindow::onAppConfigLoaded); + + connect( + &m_AppConfig, &AppConfig::tlsChanged, this, + &MainWindow::onAppConfigTlsChanged); + + connect( + &m_AppConfig, &AppConfig::screenNameChanged, this, + &MainWindow::onAppConfigScreenNameChanged); + + connect( + &m_AppConfig, &AppConfig::invertConnection, this, + &MainWindow::onAppConfigInvertConnection); + + connect( + &m_LicenseHandler, &LicenseHandler::serialKeyChanged, this, + &MainWindow::onLicenseHandlerSerialKeyChanged); + + connect( + &m_LicenseHandler, &LicenseHandler::invalidLicense, this, + &MainWindow::onLicenseHandlerInvalidLicense); + + connect( + &m_IpcClient, &QIpcClient::readLogLine, this, + &MainWindow::onIpcClientReadLogLine); + + connect( + &m_IpcClient, &QIpcClient::errorMessage, this, + &MainWindow::onIpcClientErrorMessage); + + connect( + &m_IpcClient, &QIpcClient::infoMessage, this, + &MainWindow::onIpcClientInfoMessage); + + connect(m_pActionMinimize, &QAction::triggered, this, &MainWindow::hide); + + connect(m_pActionRestore, &QAction::triggered, this, &MainWindow::showNormal); + + connect( + m_pActionStartCore, &QAction::triggered, this, + &MainWindow::onActionStartCoreTriggered); + + connect( + m_pActionStopCore, &QAction::triggered, this, + &MainWindow::onActionStopCoreTriggered); + + connect(m_pActionQuit, &QAction::triggered, qApp, &QCoreApplication::quit); + + connect( + &m_VersionChecker, &VersionChecker::updateFound, this, + &MainWindow::onVersionCheckerUpdateFound); +} + +void MainWindow::onAppAboutToQuit() { m_AppConfig.saveSettings(); } + +void MainWindow::onCreated() { + // it may seem a bit over-complicated to load the app config through a + // signal/slot, but there is a good reason: intentionality; we want to + // ensure that the ctor is fully executed before we start loading the + // app config. a signal is the clearest way to communicate this. + m_AppConfig.loadAllScopes(); +} + +void MainWindow::onShown() { + if (kLicensingEnabled) { + const auto &license = m_LicenseHandler.license(); + if (!m_AppConfig.activationHasRun() || !license.isValid() || + license.isExpired()) { + showActivationDialog(); } } } +void MainWindow::onLicenseHandlerSerialKeyChanged(const QString &serialKey) { + setWindowTitle(m_LicenseHandler.productName()); + showLicenseNotice(); + + if (m_AppConfig.serialKey() != serialKey) { + m_AppConfig.setSerialKey(serialKey); + m_AppConfig.saveSettings(); + } +} + +void MainWindow::onLicenseHandlerInvalidLicense() { + stopCore(); + showActivationDialog(); +} + +void MainWindow::onAppConfigLoaded() { + QApplication::setQuitOnLastWindowClosed(!m_AppConfig.closeToTray()); + if (!m_AppConfig.serialKey().isEmpty()) { + m_LicenseHandler.changeSerialKey(m_AppConfig.serialKey()); + } + updateScreenName(); + applyConfig(); +} + +void MainWindow::onAppConfigTlsChanged() { + updateLocalFingerprint(); + if (m_TlsUtility.isAvailableAndEnabled()) { + m_TlsUtility.generateCertificate(true); + } +} + +void MainWindow::onIpcClientReadLogLine(const QString &text) { + processCoreLogLine(text); +} + +void MainWindow::onIpcClientErrorMessage(const QString &text) { + appendLogError(text); +} + +void MainWindow::onIpcClientInfoMessage(const QString &text) { + appendLogInfo(text); +} + +void MainWindow::onTrayIconCreate(QSystemTrayIcon::ActivationReason reason) { + if (reason == QSystemTrayIcon::DoubleClick) { + if (isVisible()) { + hide(); + } else { + showNormal(); + activateWindow(); + } + } +} + +void MainWindow::onCoreProcessReadyReadStandardOutput() { + if (m_pCoreProcess) { + QString text(m_pCoreProcess->readAllStandardOutput()); + for (QString line : text.split(QRegularExpression("\r|\n|\r\n"))) { + if (!line.isEmpty()) { + processCoreLogLine(line); + } + } + } +} + +void MainWindow::onCoreProcessReadyReadStandardError() { + if (m_pCoreProcess) { + processCoreLogLine(m_pCoreProcess->readAllStandardError()); + } +} + +void MainWindow::onVersionCheckerUpdateFound(const QString &version) { + const auto link = QString(kLinkDownload).arg(kUrlDownload, kLinkStyleWhite); + const auto text = + QString("A new version is available (v%1). %2").arg(version, link); + + m_pLabelUpdate->show(); + m_pLabelUpdate->setText(text); +} + +void MainWindow::onActionStartCoreTriggered() { + m_ClientConnection.setCheckConnection(true); + startCore(); +} + +void MainWindow::onCoreProcessRetryStart() { + // This function is only called after a failed start + // Only start synergy if the current state is pending retry + if (m_CoreState == CoreState::PendingRetry) { + startCore(); + } +} + +void MainWindow::onActionStopCoreTriggered() { stopCore(); } + +void MainWindow::onAppConfigScreenNameChanged() { updateScreenName(); } + +void MainWindow::onAppConfigInvertConnection() { applyConfig(); } + +void MainWindow::onCoreProcessFinished(int exitCode, QProcess::ExitStatus) { + if (exitCode == 0) { + appendLogInfo("process exited normally"); + } else { + appendLogError(QString("process exited with error code: %1").arg(exitCode)); + } + + if (m_ExpectedRunningState == RuningState::Started) { + + if (m_CoreState != CoreState::PendingRetry) { + QTimer::singleShot( + kRetryDelay, this, &MainWindow::onCoreProcessRetryStart); + appendLogInfo("detected process not running, auto restarting"); + } else { + appendLogInfo("detected process not running, already auto restarting"); + } + + setCoreState(CoreState::PendingRetry); + } else { + setCoreState(CoreState::Disconnected); + } +} + +bool MainWindow::on_m_pActionSave_triggered() { + QString fileName = + QFileDialog::getSaveFileName(this, tr("Save configuration as...")); + + if (!fileName.isEmpty() && !serverConfig().save(fileName)) { + QMessageBox::warning( + this, tr("Save failed"), tr("Could not save configuration to file.")); + return true; + } + + return false; +} + +void MainWindow::on_m_pActionAbout_triggered() { + AboutDialog dlg(this, appConfig()); + dlg.exec(); +} + +void MainWindow::on_m_pActionHelp_triggered() { + QDesktopServices::openUrl(QUrl(kUrlHelp)); +} + +void MainWindow::on_m_pActionSettings_triggered() { + auto result = + SettingsDialog(this, appConfig(), m_LicenseHandler.license()).exec(); + if (result == QDialog::Accepted) { + enableServer(appConfig().serverGroupChecked()); + enableClient(appConfig().clientGroupChecked()); + auto state = m_CoreState; + if ((state == CoreState::Connected) || (state == CoreState::Connecting) || + (state == CoreState::Listening)) { + restartCore(); + } + } +} + +void MainWindow::on_m_pButtonConfigureServer_clicked() { + showConfigureServer(); +} + +void MainWindow::on_m_pActivate_triggered() { showActivationDialog(); } + +void MainWindow::on_m_pButtonApply_clicked() { + m_ClientConnection.setCheckConnection(true); + restartCore(); +} + +void MainWindow::on_m_pLabelComputerName_linkActivated(const QString &) { + m_pActionSettings->trigger(); +} + +void MainWindow::on_m_pLabelFingerprint_linkActivated(const QString &) { + QMessageBox::information( + this, "SSL/TLS fingerprint", TlsFingerprint::local().readFirst()); +} + +void MainWindow::on_m_pRadioGroupServer_clicked(bool) { + enableServer(true); + enableClient(false); + m_AppConfig.saveSettings(); +} + +void MainWindow::on_m_pRadioGroupClient_clicked(bool) { + enableClient(true); + enableServer(false); + m_AppConfig.saveSettings(); +} + +void MainWindow::on_m_pButtonConnect_clicked() { on_m_pButtonApply_clicked(); } + +void MainWindow::on_m_pButtonConnectToClient_clicked() { + on_m_pButtonApply_clicked(); +} + +////////////////////////////////////////////////////////////////////////////// +// End slots +////////////////////////////////////////////////////////////////////////////// + void MainWindow::open() { std::array trayMenu = { m_pActionStartCore, m_pActionStopCore, nullptr, m_pActionMinimize, m_pActionRestore, nullptr, m_pActionQuit}; - m_trayIcon.create(trayMenu, [this](QObject const *o, const char *s) { - connect(o, s, this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))); + // TODO: this seems like a hack that needs investigating... + m_TrayIcon.create(trayMenu, [this](QObject const *o, const char *s) { + connect( + o, s, this, SLOT(onTrayIconCreate(QSystemTrayIcon::ActivationReason))); setIcon(CoreState::Disconnected); }); @@ -259,30 +502,26 @@ void MainWindow::createMenuBar() { setMenuBar(m_pMenuBar); } -void MainWindow::loadSettings() { - enableServer(appConfig().serverGroupChecked()); - enableClient(appConfig().clientGroupChecked()); +void MainWindow::applyConfig() { + const auto serverEnabled = appConfig().serverGroupChecked(); + const auto clientEnabled = appConfig().clientGroupChecked(); + + enableServer(serverEnabled); + enableClient(clientEnabled); m_pLineEditHostname->setText(appConfig().serverHostname()); - m_pLineEditClienIp->setText(serverConfig().getClientAddress()); -} + m_pLineEditClientIp->setText(serverConfig().getClientAddress()); -void MainWindow::initConnections() { - connect(m_pActionMinimize, SIGNAL(triggered()), this, SLOT(hide())); - connect(m_pActionRestore, SIGNAL(triggered()), this, SLOT(showNormal())); - connect(m_pActionStartCore, SIGNAL(triggered()), this, SLOT(actionStart())); - connect(m_pActionStopCore, SIGNAL(triggered()), this, SLOT(stopCore())); - connect(m_pActionQuit, SIGNAL(triggered()), qApp, SLOT(quit())); - connect( - &m_VersionChecker, SIGNAL(updateFound(const QString &)), this, - SLOT(updateFound(const QString &))); + if (serverEnabled || clientEnabled) { + startCore(); + } } void MainWindow::saveSettings() { appConfig().setServerGroupChecked(m_pRadioGroupServer->isChecked()); appConfig().setClientGroupChecked(m_pRadioGroupClient->isChecked()); appConfig().setServerHostname(m_pLineEditHostname->text()); - serverConfig().setClientAddress(m_pLineEditClienIp->text()); + serverConfig().setClientAddress(m_pLineEditClientIp->text()); appConfig().config().saveAll(); } @@ -309,83 +548,44 @@ void MainWindow::setIcon(CoreState state) const { icon.addFile(kDefaultIconFiles[index]); #endif - m_trayIcon.set(icon); -} - -void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason) { - if (reason == QSystemTrayIcon::DoubleClick) { - if (isVisible()) { - hide(); - } else { - showNormal(); - activateWindow(); - } - } -} - -void MainWindow::logOutput() { - if (m_pCoreProcess) { - QString text(m_pCoreProcess->readAllStandardOutput()); - for (QString line : text.split(QRegularExpression("\r|\n|\r\n"))) { - if (!line.isEmpty()) { - appendLogRaw(line); - } - } - } -} - -void MainWindow::logError() { - if (m_pCoreProcess) { - appendLogRaw(m_pCoreProcess->readAllStandardError()); - } -} - -void MainWindow::updateFound(const QString &version) { - m_pWidgetUpdate->show(); - m_pLabelUpdate->setText(tr("

Your version of Synergy is out of date. " - "Version %1 is now available to " - "download.

") - .arg(version) - .arg(kDownloadUrl)); + m_TrayIcon.set(icon); } void MainWindow::appendLogInfo(const QString &text) { - appendLogRaw(getTimeStamp() + " INFO: " + text); + processCoreLogLine(getTimeStamp() + " INFO: " + text); } void MainWindow::appendLogDebug(const QString &text) { + qDebug() << text; + if (appConfig().logLevel() >= kDebugLogLevel) { - appendLogRaw(getTimeStamp() + " DEBUG: " + text); + processCoreLogLine(getTimeStamp() + " DEBUG: " + text); } } void MainWindow::appendLogError(const QString &text) { - appendLogRaw(getTimeStamp() + " ERROR: " + text); + onIpcClientReadLogLine(getTimeStamp() + " ERROR: " + text); } -void MainWindow::appendLogRaw(const QString &text) { +void MainWindow::processCoreLogLine(const QString &text) { foreach (QString line, text.split(QRegularExpression("\r|\n|\r\n"))) { - if (!line.isEmpty()) { - - // HACK: macOS 10.13.4+ spamming error lines in logs making them - // impossible to read and debug; giving users a red herring. - if (line.contains("calling TIS/TSM in non-main thread environment")) { - continue; - } - - m_pLogOutput->appendPlainText(line); - updateFromLogLine(line); + if (line.isEmpty()) { + continue; } - } -} -void MainWindow::handleIdleService(const QString &text) { - foreach (QString line, text.split(QRegularExpression("\r|\n|\r\n"))) { // only start if there is no active service running - if (!line.isEmpty() && line.contains("service status: idle") && - appConfig().startedBefore()) { + if (line.contains("service status: idle") && appConfig().startedBefore()) { startCore(); } + + // HACK: macOS 10.13.4+ spamming error lines in logs making them + // impossible to read and debug; giving users a red herring. + if (line.contains("calling TIS/TSM in non-main thread environment")) { + continue; + } + + m_pLogOutput->appendPlainText(line); + updateFromLogLine(line); } } @@ -401,18 +601,18 @@ void MainWindow::updateFromLogLine(const QString &line) { checkOSXNotification(line); #endif -#ifdef SYNERGY_ENABLE_LICENSING - checkLicense(line); -#endif + if (kLicensingEnabled) { + checkLicense(line); + } } void MainWindow::checkConnected(const QString &line) { // TODO: implement ipc connection state messages to replace this hack. if (m_pRadioGroupServer->isChecked()) { - m_serverConnection.update(line); + m_ServerConnection.update(line); m_pLabelServerState->updateServerState(line); } else { - m_clientConnection.update(line); + m_ClientConnection.update(line); m_pLabelClientState->updateClientState(line); } @@ -439,14 +639,11 @@ void MainWindow::checkConnected(const QString &line) { } } -#ifdef SYNERGY_ENABLE_LICENSING void MainWindow::checkLicense(const QString &line) { if (line.contains("trial has expired")) { - licenseManager().refresh(); - raiseActivationDialog(); + showActivationDialog(); } } -#endif void MainWindow::checkFingerprint(const QString &line) { QRegularExpression re(".*server fingerprint: ([A-F0-9:]+)"); @@ -456,14 +653,14 @@ void MainWindow::checkFingerprint(const QString &line) { } auto fingerprint = match.captured(1); - if (Fingerprint::trustedServers().isTrusted(fingerprint)) { + if (TlsFingerprint::trustedServers().isTrusted(fingerprint)) { return; } static bool messageBoxAlreadyShown = false; if (!messageBoxAlreadyShown) { - stopCore(); + onActionStopCoreTriggered(); messageBoxAlreadyShown = true; QMessageBox::StandardButton fingerprintReply = QMessageBox::information( @@ -481,7 +678,7 @@ void MainWindow::checkFingerprint(const QString &line) { if (fingerprintReply == QMessageBox::Yes) { // restart core process after trusting fingerprint. - Fingerprint::trustedServers().trust(fingerprint); + TlsFingerprint::trustedServers().trust(fingerprint); startCore(); } @@ -520,7 +717,7 @@ void MainWindow::checkOSXNotification(const QString &line) { } #endif -QString MainWindow::getTimeStamp() { +QString MainWindow::getTimeStamp() const { QDateTime current = QDateTime::currentDateTime(); return '[' + current.toString(Qt::ISODate) + ']'; } @@ -532,29 +729,26 @@ void MainWindow::restartCore() { void MainWindow::showEvent(QShowEvent *event) { QMainWindow::showEvent(event); - emit windowShown(); + emit shown(); } -void MainWindow::clearLog() { m_pLogOutput->clear(); } - void MainWindow::startCore() { + appendLogDebug("starting core process"); + saveSettings(); #ifdef Q_OS_MAC requestOSXNotificationPermission(); #endif -#ifdef SYNERGY_ENABLE_LICENSING - SerialKey serialKey = m_LicenseManager->serialKey(); - if (!serialKey.isValid()) { - if (QDialog::Rejected == raiseActivationDialog()) { + if (kLicensingEnabled) { + const auto &license = m_LicenseHandler.license(); + if (license.isExpired() && showActivationDialog() == QDialog::Rejected) { + appendLogDebug("starting core process"); return; } } - m_LicenseManager->registerLicense(); -#endif - appendLogDebug("starting process"); m_ExpectedRunningState = RuningState::Started; setCoreState(CoreState::Connecting); @@ -600,7 +794,7 @@ void MainWindow::startCore() { #endif #if defined(Q_OS_WIN) - if (m_AppConfig.cryptoEnabled()) { + if (m_AppConfig.tlsEnabled()) { args << "--enable-crypto"; args << "--tls-cert" << m_AppConfig.tlsCertPath(); } @@ -617,7 +811,7 @@ void MainWindow::startCore() { } #else - if (m_AppConfig.cryptoEnabled()) { + if (m_AppConfig.tlsEnabled()) { args << "--enable-crypto"; args << "--tls-cert" << m_AppConfig.tlsCertPath(); } @@ -629,7 +823,7 @@ void MainWindow::startCore() { // put a space between last log output and new instance. if (!m_pLogOutput->toPlainText().isEmpty()) - appendLogRaw(""); + onIpcClientReadLogLine(""); appendLogInfo( "starting " + @@ -637,20 +831,20 @@ void MainWindow::startCore() { if ((coreMode() == CoreMode::Client && !clientArgs(args, app)) || (coreMode() == CoreMode::Server && !serverArgs(args, app))) { - stopCore(); + onActionStopCoreTriggered(); return; } if (mode == ProcessMode::kDesktop) { connect( - m_pCoreProcess.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this, - SLOT(coreProcessExit(int, QProcess::ExitStatus))); + m_pCoreProcess.get(), &QProcess::finished, this, + &MainWindow::onCoreProcessFinished); connect( - m_pCoreProcess.get(), SIGNAL(readyReadStandardOutput()), this, - SLOT(logOutput())); + m_pCoreProcess.get(), &QProcess::readyReadStandardOutput, this, + &MainWindow::onCoreProcessReadyReadStandardOutput); connect( - m_pCoreProcess.get(), SIGNAL(readyReadStandardError()), this, - SLOT(logError())); + m_pCoreProcess.get(), &QProcess::readyReadStandardError, this, + &MainWindow::onCoreProcessReadyReadStandardError); } // show command if debug log level... @@ -682,19 +876,6 @@ void MainWindow::startCore() { } } -void MainWindow::actionStart() { - m_clientConnection.setCheckConnection(true); - startCore(); -} - -void MainWindow::retryStart() { - // This function is only called after a failed start - // Only start synergy if the current state is pending retry - if (m_CoreState == CoreState::PendingRetry) { - startCore(); - } -} - bool MainWindow::clientArgs(QStringList &args, QString &app) { app = appPath(appConfig().coreClientName()); @@ -719,7 +900,7 @@ bool MainWindow::clientArgs(QStringList &args, QString &app) { args << "--invert-scroll"; } - if (appConfig().clientHostMode()) { + if (appConfig().invertConnection()) { args << "--host"; args << ":" + QString::number(appConfig().port()); } else { @@ -800,7 +981,7 @@ QString MainWindow::address() const { return (!i.isEmpty() ? i : "") + ":" + QString::number(appConfig().port()); } -QString MainWindow::appPath(const QString &name) { +QString MainWindow::appPath(const QString &name) const { QDir dir(QCoreApplication::applicationDirPath()); return dir.filePath(name); } @@ -815,7 +996,7 @@ bool MainWindow::serverArgs(QStringList &args, QString &app) { return false; } - if (appConfig().serverClientMode() && m_pLineEditClienIp->text().isEmpty()) { + if (appConfig().invertConnection() && m_pLineEditClientIp->text().isEmpty()) { QMessageBox::warning( this, tr("Client IP address or name is empty"), tr("Please fill in a client IP address or name.")); @@ -836,17 +1017,15 @@ bool MainWindow::serverArgs(QStringList &args, QString &app) { args << "-c" << configFilename << "--address" << address(); appendLogInfo("config file: " + configFilename); -#ifdef SYNERGY_ENABLE_LICENSING - if (!appConfig().serialKey().isEmpty()) { + if (kLicensingEnabled && !appConfig().serialKey().isEmpty()) { args << "--serial-key" << appConfig().serialKey(); } -#endif return true; } void MainWindow::stopCore() { - appendLogDebug("stopping process"); + appendLogDebug("stopping core process"); m_ExpectedRunningState = RuningState::Stopped; @@ -882,28 +1061,6 @@ void MainWindow::stopDesktop() { m_pCoreProcess->reset(); } -void MainWindow::coreProcessExit(int exitCode, QProcess::ExitStatus) { - if (exitCode == 0) { - appendLogInfo("process exited normally"); - } else { - appendLogError(QString("process exited with error code: %1").arg(exitCode)); - } - - if (m_ExpectedRunningState == RuningState::Started) { - - if (coreState() != CoreState::PendingRetry) { - QTimer::singleShot(kRetryDelay, this, SLOT(retryStart())); - appendLogInfo("detected process not running, auto restarting"); - } else { - appendLogInfo("detected process not running, already auto restarting"); - } - - setCoreState(CoreState::PendingRetry); - } else { - setCoreState(CoreState::Disconnected); - } -} - void MainWindow::setCoreState(CoreState state) { // always assume connection is not secure when connection changes // to anything except connected. the only way the padlock shows is @@ -912,17 +1069,17 @@ void MainWindow::setCoreState(CoreState state) { secureSocket(false); } - if (coreState() == state) + if (m_CoreState == state) return; if ((state == CoreState::Connected) || (state == CoreState::Connecting) || (state == CoreState::Listening) || (state == CoreState::PendingRetry)) { disconnect( - m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartCore, - SLOT(trigger())); + m_pButtonToggleStart, &QPushButton::clicked, m_pActionStartCore, + &QAction::trigger); connect( - m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopCore, - SLOT(trigger())); + m_pButtonToggleStart, &QPushButton::clicked, m_pActionStopCore, + &QAction::trigger); m_pButtonToggleStart->setText(tr("&Stop")); m_pButtonApply->setEnabled(true); @@ -932,11 +1089,11 @@ void MainWindow::setCoreState(CoreState state) { } else if (state == CoreState::Disconnected) { disconnect( - m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopCore, - SLOT(trigger())); + m_pButtonToggleStart, &QPushButton::clicked, m_pActionStopCore, + &QAction::trigger); connect( - m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartCore, - SLOT(trigger())); + m_pButtonToggleStart, &QPushButton::clicked, m_pActionStartCore, + &QAction::trigger); m_pButtonToggleStart->setText(tr("&Start")); m_pButtonApply->setEnabled(false); @@ -946,7 +1103,9 @@ void MainWindow::setCoreState(CoreState state) { } switch (state) { - case CoreState::Listening: { + using enum CoreState; + + case Listening: { if (coreMode() == CoreMode::Server) { setStatus( tr("Synergy is waiting for clients").arg(m_SecureSocketVersion)); @@ -954,7 +1113,7 @@ void MainWindow::setCoreState(CoreState state) { break; } - case CoreState::Connected: { + case Connected: { if (m_SecureSocket) { setStatus( tr("Synergy is connected (with %1)").arg(m_SecureSocketVersion)); @@ -964,13 +1123,13 @@ void MainWindow::setCoreState(CoreState state) { } break; } - case CoreState::Connecting: + case Connecting: setStatus(tr("Synergy is starting...")); break; - case CoreState::PendingRetry: + case PendingRetry: setStatus(tr("There was an error, retrying...")); break; - case CoreState::Disconnected: + case Disconnected: setStatus(tr("Synergy is not running")); break; } @@ -999,7 +1158,12 @@ void MainWindow::setVisible(bool visible) { #endif } -QString MainWindow::getIPAddresses() { +MainWindow::CoreMode MainWindow::coreMode() const { + auto isClient = m_pRadioGroupClient->isChecked(); + return isClient ? CoreMode::Client : CoreMode::Server; +} + +QString MainWindow::getIPAddresses() const { QStringList result; bool hinted = false; const auto localnet = QHostAddress::parseSubnet("192.168.0.0/16"); @@ -1029,40 +1193,27 @@ QString MainWindow::getIPAddresses() { return result.join(", "); } -void MainWindow::setEdition(Edition edition) { -#ifdef SYNERGY_ENABLE_LICENSING - setWindowTitle(m_LicenseManager->getEditionName(edition)); -#endif -} +void MainWindow::showLicenseNotice() { + const auto &license = m_LicenseHandler.license(); + const bool timeLimited = license.isTimeLimited(); -#ifdef SYNERGY_ENABLE_LICENSING -void MainWindow::InvalidLicense() { - stopCore(); - m_AppConfig.setActivationHasRun(false); -} - -void MainWindow::showLicenseNotice(const QString ¬ice) { - this->m_trialLabel->hide(); - - if (!notice.isEmpty()) { - this->m_trialLabel->setText(notice); - this->m_trialLabel->show(); + m_labelNotice->setVisible(timeLimited); + if (timeLimited) { + auto notice = licenseNotice(m_LicenseHandler.license()); + this->m_labelNotice->setText(notice); } - - setWindowTitle(m_LicenseManager->activeEditionName()); } -#endif void MainWindow::updateLocalFingerprint() { bool fingerprintExists = false; try { - fingerprintExists = Fingerprint::local().fileExists(); + fingerprintExists = TlsFingerprint::local().fileExists(); } catch (const std::exception &e) { qDebug() << e.what(); qFatal("Failed to check if fingerprint exists"); } - if (m_AppConfig.cryptoEnabled() && fingerprintExists && + if (m_AppConfig.tlsEnabled() && fingerprintExists && m_pRadioGroupServer->isChecked()) { m_pLabelFingerprint->setVisible(true); } else { @@ -1070,60 +1221,18 @@ void MainWindow::updateLocalFingerprint() { } } -#ifdef SYNERGY_ENABLE_LICENSING -LicenseManager &MainWindow::licenseManager() const { return *m_LicenseManager; } -#endif - -bool MainWindow::on_m_pActionSave_triggered() { - QString fileName = - QFileDialog::getSaveFileName(this, tr("Save configuration as...")); - - if (!fileName.isEmpty() && !serverConfig().save(fileName)) { - QMessageBox::warning( - this, tr("Save failed"), tr("Could not save configuration to file.")); - return true; - } - - return false; -} - -void MainWindow::on_m_pActionAbout_triggered() { - AboutDialog dlg(this, appConfig()); - dlg.exec(); -} - -void MainWindow::on_m_pActionHelp_triggered() { - QDesktopServices::openUrl(QUrl(kHelpUrl)); -} +LicenseHandler &MainWindow::licenseHandler() { return m_LicenseHandler; } void MainWindow::updateWindowTitle() { -#ifdef SYNERGY_ENABLE_LICENSING - setWindowTitle(m_LicenseManager->activeEditionName()); -#else - setWindowTitle(SYNERGY_PRODUCT_NAME); -#endif - -#ifdef SYNERGY_ENABLE_LICENSING - m_LicenseManager->refresh(); -#endif -} - -void MainWindow::on_m_pActionSettings_triggered() { - auto result = SettingsDialog(this, appConfig()).exec(); - if (result == QDialog::Accepted) { - enableServer(appConfig().serverGroupChecked()); - enableClient(appConfig().clientGroupChecked()); - auto state = coreState(); - if ((state == CoreState::Connected) || (state == CoreState::Connecting) || - (state == CoreState::Listening)) { - restartCore(); - } + if (kLicensingEnabled) { + setWindowTitle(m_LicenseHandler.productName()); + } else { + setWindowTitle(kProductName); } } void MainWindow::autoAddScreen(const QString name) { -#ifdef SYNERGY_ENABLE_LICENSING if (m_ActivationDialogRunning) { // TODO: refactor this code // add this screen to the pending list and check this list until @@ -1131,7 +1240,6 @@ void MainWindow::autoAddScreen(const QString name) { m_PendingClientNames.append(name); return; } -#endif int r = m_ServerConfig.autoAddScreen(name); if (r != kAutoAddScreenOk) { @@ -1151,43 +1259,31 @@ void MainWindow::autoAddScreen(const QString name) { } void MainWindow::showConfigureServer(const QString &message) { + using enum CoreState; + ServerConfigDialog dlg(this, serverConfig(), appConfig()); dlg.message(message); auto result = dlg.exec(); if (result == QDialog::Accepted) { - auto state = coreState(); - if ((state == CoreState::Connected) || (state == CoreState::Connecting) || - (state == CoreState::Listening)) { + auto state = m_CoreState; + if ((state == Connected) || (state == Connecting) || (state == Listening)) { restartCore(); } } } -void MainWindow::on_m_pButtonConfigureServer_clicked() { - showConfigureServer(); -} - -void MainWindow::on_m_pActivate_triggered() { -#ifdef SYNERGY_ENABLE_LICENSING - raiseActivationDialog(); -#endif -} - -void MainWindow::on_m_pButtonApply_clicked() { - m_clientConnection.setCheckConnection(true); - restartCore(); -} - -#ifdef SYNERGY_ENABLE_LICENSING -int MainWindow::raiseActivationDialog() { +int MainWindow::showActivationDialog() { if (m_ActivationDialogRunning) { return QDialog::Rejected; } - ActivationDialog activationDialog(this, appConfig(), licenseManager()); + + ActivationDialog activationDialog(this, m_AppConfig, m_LicenseHandler); + m_ActivationDialogRunning = true; int result = activationDialog.exec(); m_ActivationDialogRunning = false; + if (!m_PendingClientNames.empty()) { foreach (const QString &name, m_PendingClientNames) { autoAddScreen(name); @@ -1197,19 +1293,8 @@ int MainWindow::raiseActivationDialog() { } return result; } -#endif -void MainWindow::on_windowShown() { -#ifdef SYNERGY_ENABLE_LICENSING - auto serialKey = m_LicenseManager->serialKey(); - if (!m_AppConfig.activationHasRun() && !serialKey.isValid()) { - setEdition(Edition::kUnregistered); - raiseActivationDialog(); - } -#endif -} - -QString MainWindow::getProfileRootForArg() { +QString MainWindow::getProfileRootForArg() const { CoreInterface coreInterface; QString dir = coreInterface.getProfileDir(); @@ -1232,15 +1317,6 @@ void MainWindow::secureSocket(bool secureSocket) { } } -void MainWindow::on_m_pLabelComputerName_linkActivated(const QString &) { - m_pActionSettings->trigger(); -} - -void MainWindow::on_m_pLabelFingerprint_linkActivated(const QString &) { - QMessageBox::information( - this, "SSL/TLS fingerprint", Fingerprint::local().readFirst()); -} - void MainWindow::windowStateChanged() { if (windowState() == Qt::WindowMinimized && appConfig().minimizeToTray()) hide(); @@ -1248,78 +1324,25 @@ void MainWindow::windowStateChanged() { void MainWindow::updateScreenName() { m_pLabelComputerName->setText( - tr("This computer's name: %1 (change)") - .arg(appConfig().screenName())); + QString("This computer's name: %1 " + R"((change))") + .arg(appConfig().screenName()) + .arg(kLinkStyleSecondary)); serverConfig().updateServerName(); } void MainWindow::enableServer(bool enable) { m_AppConfig.setServerGroupChecked(enable); m_pRadioGroupServer->setChecked(enable); - - if (enable) { - if (m_AppConfig.serverClientMode()) { - m_pLabelClientIp->show(); - m_pLineEditClienIp->show(); - m_pButtonConnectToClient->show(); - } else { - m_pLabelClientIp->hide(); - m_pLineEditClienIp->hide(); - m_pButtonConnectToClient->hide(); - } - - m_pButtonConfigureServer->show(); - m_pLabelServerState->show(); - updateLocalFingerprint(); - m_pButtonToggleStart->setEnabled(enable); - } else { - m_pLabelFingerprint->hide(); - m_pButtonConfigureServer->hide(); - m_pLabelServerState->hide(); - m_pLabelClientIp->hide(); - m_pLineEditClienIp->hide(); - m_pButtonConnectToClient->hide(); - } + m_pWidgetServer->setEnabled(enable); + m_pWidgetServerInverse->setVisible(m_AppConfig.invertConnection()); + m_pButtonToggleStart->setEnabled(true); } void MainWindow::enableClient(bool enable) { m_AppConfig.setClientGroupChecked(enable); m_pRadioGroupClient->setChecked(enable); - - if (enable) { - if (m_AppConfig.clientHostMode()) { - m_pLabelServerName->hide(); - m_pLineEditHostname->hide(); - m_pButtonConnect->hide(); - } else { - m_pLabelServerName->show(); - m_pLineEditHostname->show(); - m_pButtonConnect->show(); - } - m_pButtonToggleStart->setEnabled(enable); - } else { - m_pLabelClientState->hide(); - m_pLabelServerName->hide(); - m_pLineEditHostname->hide(); - m_pButtonConnect->hide(); - } -} - -void MainWindow::on_m_pRadioGroupServer_clicked(bool) { - enableServer(true); - enableClient(false); - m_AppConfig.saveSettings(); -} - -void MainWindow::on_m_pRadioGroupClient_clicked(bool) { - enableClient(true); - enableServer(false); - m_AppConfig.saveSettings(); -} - -void MainWindow::on_m_pButtonConnect_clicked() { on_m_pButtonApply_clicked(); } - -void MainWindow::on_m_pButtonConnectToClient_clicked() { - on_m_pButtonApply_clicked(); + m_pWidgetClient->setEnabled(enable); + m_pWidgetClient->setVisible(!m_AppConfig.invertConnection()); + m_pButtonToggleStart->setEnabled(true); } diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 85b294e5c..0e75901e5 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -35,9 +35,10 @@ #include "ServerConfig.h" #include "ServerConnection.h" #include "TrayIcon.h" +#include "global/Ipc.h" #include "gui/QIpcClient.h" +#include "gui/TlsUtility.h" #include "gui/VersionChecker.h" -#include "shared/Ipc.h" class QAction; class QMenu; @@ -57,8 +58,7 @@ class QSynergyApplication; class SetupWizard; class DataDownloader; class CommandProcess; -class SslCertificate; -class LicenseManager; +class TlsCertificate; class MainWindow : public QMainWindow, public Ui::MainWindowBase { Q_OBJECT @@ -80,60 +80,57 @@ public: }; enum class CoreMode { Client, Server }; - enum class LogLevel { Error, Info }; - enum class RuningState { Started, Stopped }; public: -#ifdef SYNERGY_ENABLE_LICENSING - MainWindow(AppConfig &appConfig, LicenseManager &licenseManager); -#else explicit MainWindow(AppConfig &appConfig); -#endif ~MainWindow() override; -public: void setVisible(bool visible) override; - CoreMode coreMode() const { - auto isClient = m_pRadioGroupClient->isChecked(); - return isClient ? CoreMode::Client : CoreMode::Server; - } - CoreState coreState() const { return m_CoreState; } - QString hostname() const { return m_pLineEditHostname->text(); } - QString configFilename(); + CoreMode coreMode() const; QString address() const; - QString appPath(const QString &name); + QString appPath(const QString &name) const; void open(); - void clearLog(); - VersionChecker &versionChecker() { return m_VersionChecker; } ServerConfig &serverConfig() { return m_ServerConfig; } - void showConfigureServer(const QString &message); - void showConfigureServer() { showConfigureServer(""); } void autoAddScreen(const QString name); -#ifdef SYNERGY_ENABLE_LICENSING - LicenseManager &licenseManager() const; - int raiseActivationDialog(); -#endif - -public slots: - void setEdition(Edition edition); -#ifdef SYNERGY_ENABLE_LICENSING - void InvalidLicense(); - void showLicenseNotice(const QString &message); -#endif - void appendLogRaw(const QString &text); + LicenseHandler &licenseHandler(); + int showActivationDialog(); void appendLogInfo(const QString &text); void appendLogDebug(const QString &text); void appendLogError(const QString &text); - void startCore(); - void retryStart(); - void actionStart(); - void handleIdleService(const QString &text); -protected slots: - void updateLocalFingerprint(); - void updateScreenName(); +signals: + void created(); + void shown(); + +public slots: + void onAppAboutToQuit(); + +private slots: + void onCreated(); + void onShown(); + void onAppConfigLoaded(); + void onAppConfigTlsChanged(); + void onAppConfigScreenNameChanged(); + void onAppConfigInvertConnection(); + void onLicenseHandlerSerialKeyChanged(const QString &serialKey); + void onLicenseHandlerInvalidLicense(); + void onIpcClientReadLogLine(const QString &text); + void onIpcClientErrorMessage(const QString &text); + void onIpcClientInfoMessage(const QString &text); + void onCoreProcessFinished(int exitCode, QProcess::ExitStatus); + void onVersionCheckerUpdateFound(const QString &version); + void onTrayIconCreate(QSystemTrayIcon::ActivationReason reason); + void onActionStartCoreTriggered(); + void onActionStopCoreTriggered(); + void onCoreProcessReadyReadStandardOutput(); + void onCoreProcessReadyReadStandardError(); + void on_m_pButtonApply_clicked(); + void on_m_pLabelComputerName_linkActivated(const QString &link); + void on_m_pLabelFingerprint_linkActivated(const QString &link); + void on_m_pButtonConnect_clicked(); + void on_m_pButtonConnectToClient_clicked(); void on_m_pRadioGroupServer_clicked(bool); void on_m_pRadioGroupClient_clicked(bool); void on_m_pButtonConfigureServer_clicked(); @@ -142,24 +139,15 @@ protected slots: void on_m_pActionHelp_triggered(); void on_m_pActionSettings_triggered(); void on_m_pActivate_triggered(); - void coreProcessExit(int exitCode, QProcess::ExitStatus); - void trayActivated(QSystemTrayIcon::ActivationReason reason); - void stopCore(); - void logOutput(); - void logError(); - void updateFound(const QString &version); - void saveSettings(); -protected: +private: QSettings &settings() { return *appConfig().config().currentSettings(); } AppConfig &appConfig() { return m_AppConfig; } AppConfig const &appConfig() const { return m_AppConfig; } - AppConfig *appConfigPtr() { return &m_AppConfig; } - void initConnections(); void createMenuBar(); void createStatusBar(); void createTrayIcon(); - void loadSettings(); + void applyConfig(); void setIcon(CoreState state) const; void setCoreState(CoreState state); bool checkForApp(int which, QString &app); @@ -168,51 +156,45 @@ protected: void setStatus(const QString &status); void sendIpcMessage(IpcMessageType type, const char *buffer, bool showErrors); void updateFromLogLine(const QString &line); - QString getIPAddresses(); + QString getIPAddresses() const; void stopService(); void stopDesktop(); void enableServer(bool enable); void enableClient(bool enable); - - QString getProfileRootForArg(); + QString getProfileRootForArg() const; void checkConnected(const QString &line); void checkFingerprint(const QString &line); bool checkSecureSocket(const QString &line); + void checkLicense(const QString &line); + QString getTimeStamp() const; + void restartCore(); + void showEvent(QShowEvent *) override; + void secureSocket(bool secureSocket); + void windowStateChanged(); + void connectSlots() const; + void updateWindowTitle(); + void processCoreLogLine(const QString &line); + void startCore(); + void onCoreProcessRetryStart(); + void updateLocalFingerprint(); + void updateScreenName(); + void saveSettings(); + QString configFilename(); + void showConfigureServer(const QString &message); + void showConfigureServer() { showConfigureServer(""); } + void showLicenseNotice(); + void stopCore(); + #ifdef Q_OS_MAC void checkOSXNotification(const QString &line); #endif -#ifdef SYNERGY_ENABLE_LICENSING - void checkLicense(const QString &line); -#endif - QString getTimeStamp(); - void restartCore(); - void showEvent(QShowEvent *) override; - void secureSocket(bool secureSocket); - - void windowStateChanged(); - -private: - void updateWindowTitle(); - -#ifdef SYNERGY_ENABLE_LICENSING - LicenseManager *m_LicenseManager = nullptr; -#endif - - AppConfig &m_AppConfig; - ServerConfig m_ServerConfig; - ServerConnection m_serverConnection; - ClientConnection m_clientConnection; VersionChecker m_VersionChecker; QIpcClient m_IpcClient; - TrayIcon m_trayIcon; + TrayIcon m_TrayIcon; QMutex m_StopDesktopMutex; - -#ifdef SYNERGY_ENABLE_LICENSING bool m_ActivationDialogRunning = false; QStringList m_PendingClientNames; -#endif - RuningState m_ExpectedRunningState = RuningState::Stopped; std::unique_ptr m_pCoreProcess; QMenuBar *m_pMenuBar = nullptr; @@ -226,16 +208,10 @@ private: bool m_SecureSocket = false; QString m_SecureSocketVersion = ""; -private slots: - void on_m_pButtonApply_clicked(); - void on_windowShown(); - - void on_m_pLabelComputerName_linkActivated(const QString &link); - void on_m_pLabelFingerprint_linkActivated(const QString &link); - - void on_m_pButtonConnect_clicked(); - void on_m_pButtonConnectToClient_clicked(); - -signals: - void windowShown(); + AppConfig &m_AppConfig; + LicenseHandler m_LicenseHandler; + ServerConfig m_ServerConfig; + ServerConnection m_ServerConnection; + ClientConnection m_ClientConnection; + synergy::gui::TlsUtility m_TlsUtility; }; diff --git a/src/gui/src/MainWindowBase.ui b/src/gui/src/MainWindowBase.ui index 999a175e8..8181aaf2b 100644 --- a/src/gui/src/MainWindowBase.ui +++ b/src/gui/src/MainWindowBase.ui @@ -7,11 +7,11 @@ 0 0 750 - 650 + 700
- + 0 0 @@ -34,83 +34,45 @@ 20 - - - - 6 - - - 2 - - - 0 - - - 0 - - - 10 - - - - - - - - :/res/icons/16x16/warning.png - - - - - - - m_pLabelUpdate - - - true - - - - - - - Qt::Horizontal - - - - 469 - 20 - - - - - - - - - - - - 0 - 0 - - - - - 65535 - 65535 - - - - - Arial - true - - - - This computer's name: - - + + + + + This computer's name: + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + padding: 3px 5px; +border-radius: 3px; +background-color: #3B67D3; +color: #ffffff; +top: 3px; + + + m_pLabelUpdate + + + true + + + + @@ -138,23 +100,11 @@ - + 0 0 - - - 0 - 220 - - - - - 16777215 - 16777215 - - 16 @@ -173,17 +123,11 @@ - - - 0 - 0 - - - - - 16777215 - 250 - + + + 0 + 0 + .QFrame{ @@ -192,193 +136,162 @@ border-radius: 4px; background-color: rgba(192,192,192, 0.1); } - - QFrame::StyledPanel - - - QFrame::Raised - - - 11 + + 15 - + - - - 0 - 0 - - - - - 300 - 30 - - - - - 65535 - 30 - - QRadioButton::indicator{ - top: -7px; + top: -7px; } Use this computer's keyboard and mouse -(make this computer the server) +(make this computer the &server) - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - Client IP address or hostname: - - - 20 - + + + + + 15 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 15 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Client IP address or hostname: + + + 20 + + + + + + + 20 + + + + + + + + Connect + + + + + + + + + + + + No clients connected + + + 20 + + + + + + + <html><head/><body><p>TLS enabled (<a href="#"><span style=" text-decoration: underline; color:#4285f4;">fingerprint</span></a>)</p></body></html> + + + Qt::RichText + + + 20 + + + + + + + Qt::Vertical + + + + 20 + 10 + + + + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + &Configure + + + + + + - - - - 20 - - - 0 - - - - - - - - Connect - - - - - - - - - No clients connected - - - 20 - - - - - - - - 0 - 0 - - - - - 16777215 - 20 - - - - <html><head/><body><p>TLS enabled (<a href="#"><span style=" text-decoration: underline; color:#4285f4;">fingerprint</span></a>)</p></body></html> - - - Qt::RichText - - - 20 - - - - - - - Qt::Vertical - - - - 20 - 10 - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - &Configure - - - - - - - - 0 - 0 - - - - - 16777215 - 250 - + + + 0 + 0 + .QFrame{ @@ -387,36 +300,15 @@ border-radius: 4px; background-color: rgba(192,192,192, 0.1); } - - QFrame::StyledPanel - - - QFrame::Raised - + + 15 + 11 - + - - - 0 - 0 - - - - - 0 - 30 - - - - - 300 - 30 - - QRadioButton::indicator{ top: -7px; @@ -424,18 +316,83 @@ background-color: rgba(192,192,192, 0.1); Use another computer’s mouse and keyboard -(make this computer the client) +(make this computer the &client) + + + + + 15 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Server IP address or hostname: + + + 20 + + + + + + + 20 + + + + + + 0 + 0 + + + + Hostname or IP address of the server computer. + + + + + + + Connect + + + + + + + + + Connected to server + + + 20 + + + + + + - + Qt::Vertical - - QSizePolicy::Minimum - 20 @@ -444,104 +401,6 @@ background-color: rgba(192,192,192, 0.1); - - - - 19 - - - 10 - - - - - - 0 - 0 - - - - &Server IP address or hostname: - - - m_pLineEditHostname - - - - - - - - 0 - 0 - - - - - 200 - 0 - - - - Hostname or IP address of the server computer. - - - - - - - - - Connected to server - - - 20 - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 10 - - - - - - - - 0 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Connect - - - - - @@ -687,28 +546,19 @@ background-color: rgba(192,192,192, 0.1); - - - - Arial - -1 - true - - + false - font-size: 13px; -font-weight: 700; -padding: 3px 5px; + padding: 3px 5px; border-radius: 3px; -background-color: #EC4C47; +background-color: #3B67D3; color: #ffffff; top: 3px; - + m_labelNotice Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index 2fa48ab18..ac4eacbef 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013-2016 Symless Ltd. + * Copyright (C) 2013 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -17,7 +17,6 @@ #include "QUtility.h" -#include "CommandProcess.h" #include "ProcessorArch.h" #if defined(Q_OS_LINUX) diff --git a/src/gui/src/ServerConfig.cpp b/src/gui/src/ServerConfig.cpp index 08264c5d1..478d220d8 100644 --- a/src/gui/src/ServerConfig.cpp +++ b/src/gui/src/ServerConfig.cpp @@ -22,14 +22,13 @@ #include "Config.h" #include "Hotkey.h" #include "MainWindow.h" +#include "gui/constants.h" #include #include #include #include -using synergy::gui::Config; - static const struct { int x; int y; @@ -162,9 +161,6 @@ void ServerConfig::saveSettings() { settings().endArray(); settings().endGroup(); - - m_pAppConfig->saveSettings(); - m_pAppConfig->config().markUnsaved(); } void ServerConfig::loadSettings() { @@ -541,7 +537,7 @@ size_t ServerConfig::setClipboardSharingSize(size_t size) { } void ServerConfig::setClientAddress(const QString &address) { - if (m_pAppConfig->serverClientMode()) { + if (m_pAppConfig->invertConnection()) { m_ClientAddress = address; } } @@ -549,7 +545,7 @@ void ServerConfig::setClientAddress(const QString &address) { QString ServerConfig::getClientAddress() const { QString clientAddress; - if (m_pAppConfig->serverClientMode()) { + if (m_pAppConfig->invertConnection()) { clientAddress = m_ClientAddress.trimmed(); } @@ -559,11 +555,3 @@ QString ServerConfig::getClientAddress() const { QSettings &ServerConfig::settings() { return *m_pAppConfig->config().currentSettings(); } - -bool ServerConfig::isHotkeysAvailable() const { -#ifdef SYNERGY_ENABLE_LICENSING - return (m_pAppConfig->edition() != Edition::kLite); -#else - return true; -#endif -} diff --git a/src/gui/src/ServerConfig.h b/src/gui/src/ServerConfig.h index d0e13655b..0188a5836 100644 --- a/src/gui/src/ServerConfig.h +++ b/src/gui/src/ServerConfig.h @@ -81,7 +81,6 @@ public: bool isFull() const; bool isScreenExists(const QString &screenName) const; void addClient(const QString &clientName); - bool isHotkeysAvailable() const; QString getClientAddress() const; void setClientAddress(const QString &address); diff --git a/src/gui/src/ServerConfigDialog.cpp b/src/gui/src/ServerConfigDialog.cpp index 3b49881da..e71f232a7 100644 --- a/src/gui/src/ServerConfigDialog.cpp +++ b/src/gui/src/ServerConfigDialog.cpp @@ -22,7 +22,8 @@ #include "ScreenSettingsDialog.h" #include "ServerConfig.h" #include "UpgradeDialog.h" -#include "shared/EditionType.h" +#include "gui/constants.h" +#include "license/ProductEdition.h" #include #include @@ -273,27 +274,12 @@ void ServerConfigDialog::reject() { } void ServerConfigDialog::on_m_pButtonNewHotkey_clicked() { - if (serverConfig().isHotkeysAvailable()) { - Hotkey hotkey; - HotkeyDialog dlg(this, hotkey); - if (dlg.exec() == QDialog::Accepted) { - serverConfig().hotkeys().append(hotkey); - m_pListHotkeys->addItem(hotkey.text()); - onChange(); - } - } else { -#ifdef SYNERGY_ENABLE_LICENSING - auto edition = appConfig().edition(); - if (edition == Edition::kLite || edition == Edition::kBasic) { - UpgradeDialog upgradeDialog(this); - if (appConfig().edition() == Edition::kLite) { - upgradeDialog.showDialog( - "Upgrade to Synergy 1 Ultimate to enable hotkeys"); - } else if (appConfig().edition() == Edition::kBasic) { - upgradeDialog.showDialog("Upgrade to Synergy 1 Pro to enable hotkeys"); - } - } -#endif // SYNERGY_ENABLE_LICENSING + Hotkey hotkey; + HotkeyDialog dlg(this, hotkey); + if (dlg.exec() == QDialog::Accepted) { + serverConfig().hotkeys().append(hotkey); + m_pListHotkeys->addItem(hotkey.text()); + onChange(); } } diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index 27b546864..033c6e95f 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -20,11 +20,9 @@ #include "AppConfig.h" #include "MainWindow.h" -#include "SslCertificate.h" - -#ifdef SYNERGY_ENABLE_LICENSING #include "UpgradeDialog.h" -#endif // SYNERGY_ENABLE_LICENSING +#include "gui/TlsCertificate.h" +#include "gui/constants.h" #include #include @@ -33,13 +31,20 @@ #include #include -SettingsDialog::SettingsDialog(QWidget *parent, AppConfig &config) +using namespace synergy::license; + +const char *const kProProductName = "Synergy 1 Pro"; + +SettingsDialog::SettingsDialog( + QWidget *parent, AppConfig &config, const License &license) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), Ui::SettingsDialogBase(), - m_appConfig(config) { + m_appConfig(config), + m_license(license), + m_tlsUtility(config, license) { + setupUi(this); - // TODO: maybe just accept MainWindow type in ctor? m_pMainWindow = dynamic_cast(parent); loadFromConfig(); @@ -79,9 +84,6 @@ SettingsDialog::SettingsDialog(QWidget *parent, AppConfig &config) connect(m_pCheckBoxLanguageSync, SIGNAL(clicked()), this, SLOT(onChange())); connect( m_pCheckBoxScrollDirection, SIGNAL(clicked()), this, SLOT(onChange())); - connect(m_pCheckBoxClientHostMode, SIGNAL(clicked()), this, SLOT(onChange())); - connect( - m_pCheckBoxServerClientMode, SIGNAL(clicked()), this, SLOT(onChange())); adjustSize(); } @@ -106,13 +108,12 @@ void SettingsDialog::accept() { appConfig().setMinimizeToTray(m_pCheckBoxMinimizeToTray->isChecked()); appConfig().setTlsCertPath(m_pLineEditCertificatePath->text()); appConfig().setTlsKeyLength(m_pComboBoxKeyLength->currentText()); - appConfig().setCryptoEnabled(m_pCheckBoxEnableCrypto->isChecked()); + appConfig().setTlsEnabled(m_pCheckBoxEnableCrypto->isChecked()); appConfig().setLanguageSync(m_pCheckBoxLanguageSync->isChecked()); appConfig().setInvertScrollDirection(m_pCheckBoxScrollDirection->isChecked()); - appConfig().setClientHostMode(m_pCheckBoxClientHostMode->isChecked()); - appConfig().setServerClientMode(m_pCheckBoxServerClientMode->isChecked()); appConfig().setServiceEnabled(m_pCheckBoxServiceEnabled->isChecked()); appConfig().setCloseToTray(m_pCheckBoxCloseToTray->isChecked()); + appConfig().setInvertConnection(m_pInvertConnection->isChecked()); appConfig().saveSettings(); QDialog::accept(); @@ -139,11 +140,9 @@ void SettingsDialog::loadFromConfig() { m_pCheckBoxPreventSleep->setChecked(appConfig().preventSleep()); m_pCheckBoxMinimizeToTray->setChecked(appConfig().minimizeToTray()); m_pLineEditCertificatePath->setText(appConfig().tlsCertPath()); - m_pCheckBoxEnableCrypto->setChecked(m_appConfig.cryptoEnabled()); + m_pCheckBoxEnableCrypto->setChecked(m_appConfig.tlsEnabled()); m_pCheckBoxLanguageSync->setChecked(m_appConfig.languageSync()); m_pCheckBoxScrollDirection->setChecked(m_appConfig.invertScrollDirection()); - m_pCheckBoxClientHostMode->setChecked(m_appConfig.clientHostMode()); - m_pCheckBoxServerClientMode->setChecked(m_appConfig.serverClientMode()); m_pCheckBoxServiceEnabled->setChecked(m_appConfig.serviceEnabled()); m_pCheckBoxCloseToTray->setChecked(m_appConfig.closeToTray()); @@ -153,6 +152,10 @@ void SettingsDialog::loadFromConfig() { m_pRadioUserScope->setChecked(true); } + m_pInvertConnection->setChecked(m_appConfig.invertConnection()); + m_pInvertConnection->setEnabled( + m_license.productEdition() == Edition::kBusiness); + updateTlsControls(); } @@ -164,20 +167,20 @@ void SettingsDialog::updateTlsControls() { m_pComboBoxKeyLength->findText(appConfig().tlsKeyLength())); } - m_pCheckBoxEnableCrypto->setChecked(m_appConfig.cryptoEnabled()); + m_pCheckBoxEnableCrypto->setChecked(m_appConfig.tlsEnabled()); updateTlsControlsEnabled(); } void SettingsDialog::updateTlsControlsEnabled() { auto clientMode = appConfig().clientGroupChecked(); - auto cryptoAvailable = appConfig().cryptoAvailable(); + auto tlsAvailable = m_tlsUtility.isAvailableAndEnabled(); auto tlsChecked = m_pCheckBoxEnableCrypto->isChecked(); - auto enabled = !clientMode && cryptoAvailable && tlsChecked; + auto enabled = !clientMode && tlsAvailable && tlsChecked; qDebug( - "TLS controls enabled=%d, client=%d, crypto=%d, checked=%d", enabled, - clientMode, cryptoAvailable, tlsChecked); + "TLS controls enabled=%d, client=%d, available=%d, checked=%d", enabled, + clientMode, tlsAvailable, tlsChecked); m_pLabelKeyLength->setEnabled(enabled); m_pComboBoxKeyLength->setEnabled(enabled); @@ -209,24 +212,17 @@ void SettingsDialog::on_m_pButtonBrowseLog_clicked() { } } -void SettingsDialog::on_m_pCheckBoxEnableCrypto_clicked(bool checked) { +void SettingsDialog::on_m_pCheckBoxEnableCrypto_clicked(bool) { updateTlsControlsEnabled(); - if (!appConfig().cryptoAvailable()) { - -#ifdef SYNERGY_ENABLE_LICENSING - auto edition = appConfig().edition(); - if (edition == Edition::kLite || edition == Edition::kBasic) { + if (kLicensingEnabled && !m_tlsUtility.isAvailable()) { + auto edition = m_license.productEdition(); + if (edition == Edition::kBasic) { UpgradeDialog upgradeDialog(this); - if (appConfig().edition() == Edition::kLite) { - upgradeDialog.showDialog( - "Upgrade to Synergy Ultimate to enable TLS encryption."); - } else if (appConfig().edition() == Edition::kBasic) { - upgradeDialog.showDialog( - "Upgrade to Synergy Pro to enable TLS encryption."); - } + upgradeDialog.showDialog( + QString("Upgrade to %1 to enable TLS encryption.") + .arg(kProProductName)); } -#endif // SYNERGY_ENABLE_LICENSING } } @@ -271,11 +267,11 @@ void SettingsDialog::updateTlsRegenerateButton() { } void SettingsDialog::on_m_pPushButtonRegenCert_clicked() { - appConfig().generateCertificate(true); + m_tlsUtility.generateCertificate(true); } void SettingsDialog::updateKeyLengthOnFile(const QString &path) { - SslCertificate ssl; + TlsCertificate ssl; auto length = ssl.getCertKeyLength(path); auto index = m_pComboBoxKeyLength->findText(length); m_pComboBoxKeyLength->setCurrentIndex(index); @@ -299,8 +295,6 @@ void SettingsDialog::updateControlsEnabled() { m_pComboBoxKeyLength->setEnabled(writable); m_pPushButtonBrowseCert->setEnabled(writable); m_pCheckBoxEnableCrypto->setEnabled(writable); - m_pCheckBoxClientHostMode->setEnabled(writable); - m_pCheckBoxServerClientMode->setEnabled(writable); m_pCheckBoxServiceEnabled->setEnabled(writable); m_pCheckBoxCloseToTray->setEnabled(writable); @@ -311,12 +305,6 @@ void SettingsDialog::updateControlsEnabled() { m_pCheckBoxServiceEnabled->setEnabled(false); #endif - m_pCheckBoxClientHostMode->setEnabled( - writable && isClientMode() && appConfig().initiateConnectionFromServer()); - m_pCheckBoxServerClientMode->setEnabled( - writable && !isClientMode() && - appConfig().initiateConnectionFromServer()); - m_pLabelLogPath->setEnabled(writable && m_pCheckBoxLogToFile->isChecked()); m_pLineEditLogFilename->setEnabled( writable && m_pCheckBoxLogToFile->isChecked()); diff --git a/src/gui/src/SettingsDialog.h b/src/gui/src/SettingsDialog.h index e1917dede..83d820422 100644 --- a/src/gui/src/SettingsDialog.h +++ b/src/gui/src/SettingsDialog.h @@ -18,9 +18,11 @@ #pragma once +#include "gui/TlsUtility.h" #include "ui_SettingsDialogBase.h" -#include "CoreInterface.h" +#include "gui/CoreInterface.h" +#include "license/License.h" #include "validators/ScreenNameValidator.h" #include @@ -33,7 +35,9 @@ class SettingsDialog : public QDialog, public Ui::SettingsDialogBase { Q_OBJECT public: - SettingsDialog(QWidget *parent, AppConfig &config); + SettingsDialog( + QWidget *parent, AppConfig &config, + const synergy::license::License &license); static QString browseForSynergyc( QWidget *parent, const QString &programDir, const QString &coreClientName); @@ -63,9 +67,12 @@ protected: void updateTlsControlsEnabled(); private: + QString m_nameError = ""; MainWindow *m_pMainWindow; AppConfig &m_appConfig; - CoreInterface m_CoreInterface; + CoreInterface m_coreInterface; + const synergy::license::License &m_license; + synergy::gui::TlsUtility m_tlsUtility; std::unique_ptr m_screenNameValidator; /// @brief Stores settings scope at start of settings dialog @@ -73,8 +80,6 @@ private: /// the scope and doesn't save changes bool m_isSystemAtStart = false; - QString m_nameError = ""; - private slots: void on_m_pCheckBoxEnableCrypto_clicked(bool checked); void on_m_pCheckBoxLogToFile_stateChanged(int); diff --git a/src/gui/src/SettingsDialogBase.ui b/src/gui/src/SettingsDialogBase.ui index 030e355e0..bf7374c93 100644 --- a/src/gui/src/SettingsDialogBase.ui +++ b/src/gui/src/SettingsDialogBase.ui @@ -6,8 +6,8 @@ 0 0 - 417 - 786 + 500 + 830 @@ -38,195 +38,6 @@ 5 - - - - .QFrame{ -border: 1px solid rgba(192,192,192, 0.2); -border-radius: 4px; -background-color: rgba(192,192,192, 0.1); -} - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 15 - - - 15 - - - 7 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Log to file - - - - - - - - Info - - - - - Debug - - - - - Debug1 - - - - - Debug2 - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - Level - - - m_pComboLogLevel - - - - - - - 3 - - - 0 - - - - - - 0 - 0 - - - - Log path - - - - - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 30 - 20 - - - - - - - - false - - - - 0 - 0 - - - - - - - - false - - - PointingHandCursor - - - margin: 0px; padding: 0px; - - - - - - - :/res/icons/64x64/folder.png:/res/icons/64x64/folder.png - - - - 20 - 13 - - - - true - - - - - - - - - - - - - 0 - 0 - - - - Use settings profile from - - - @@ -238,10 +49,10 @@ background-color: rgba(192,192,192, 0.1); - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised @@ -277,10 +88,10 @@ background-color: rgba(192,192,192, 0.1); - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::Maximum + QSizePolicy::Policy::Maximum @@ -382,7 +193,7 @@ background-color: rgba(192,192,192, 0.1); - Qt::Horizontal + Qt::Orientation::Horizontal @@ -413,7 +224,7 @@ background-color: rgba(192,192,192, 0.1); - Qt::LeftToRight + Qt::LayoutDirection::LeftToRight Minimize to system &tray @@ -432,26 +243,13 @@ background-color: rgba(192,192,192, 0.1); - - - - - 0 - 0 - - - - Security - - - - - + + - Qt::Vertical + Qt::Orientation::Vertical - QSizePolicy::Fixed + QSizePolicy::Policy::Fixed @@ -461,6 +259,61 @@ background-color: rgba(192,192,192, 0.1); + + + + + 0 + 0 + + + + Advanced + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 20 + 3 + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + Logs + + + @@ -478,34 +331,44 @@ background-color: rgba(192,192,192, 0.1); - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised - + + + + Stop this computer from sleeping + + + + Invert scroll direction on this computer (client mode) - + + + + false + + + Invert server/client TCP connection (business edition) + + + + Use server's keyboard language on this computer (client mode) - - - - Use background service (daemon) - - - - + @@ -559,46 +422,87 @@ background-color: rgba(192,192,192, 0.1); - - + + - Stop this computer from sleeping + Use background service (daemon) - - - - QLayout::SetNoConstraint + + + + + + + + 0 + 0 + + + + Use settings profile from + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 20 + 3 + + + + + + + + .QFrame{ +border: 1px solid rgba(192,192,192, 0.2); +border-radius: 4px; +background-color: rgba(192,192,192, 0.1); +} + + + + QFrame::Shape::StyledPanel + + + QFrame::Shadow::Raised + + + + 15 + + + 15 + + + 6 + + + + + All users - - - - Use server as a client - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Use client in host mode - - - - + + true + + + + + + + Current user + + @@ -614,10 +518,10 @@ background-color: rgba(192,192,192, 0.1); - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised @@ -647,7 +551,7 @@ background-color: rgba(192,192,192, 0.1); - Qt::Horizontal + Qt::Orientation::Horizontal @@ -715,10 +619,10 @@ background-color: rgba(192,192,192, 0.1); - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::Minimum + QSizePolicy::Policy::Minimum @@ -774,7 +678,7 @@ background-color: rgba(192,192,192, 0.1); - Qt::Horizontal + Qt::Orientation::Horizontal @@ -796,63 +700,18 @@ background-color: rgba(192,192,192, 0.1); - - - - - 0 - 0 - + + + + Qt::Orientation::Horizontal - - Advanced + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Save - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 0 - 0 - - - - Logs - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 3 - - - - - - + + .QFrame{ border: 1px solid rgba(192,192,192, 0.2); @@ -862,12 +721,12 @@ background-color: rgba(192,192,192, 0.1); - QFrame::StyledPanel + QFrame::Shape::StyledPanel - QFrame::Raised + QFrame::Shadow::Raised - + 15 @@ -875,61 +734,178 @@ background-color: rgba(192,192,192, 0.1); 15 - 6 + 7 - - - All users + + + Qt::Orientation::Horizontal - - true + + + 40 + 20 + + + + + + + + Log to file - - + + + + + Info + + + + + Debug + + + + + Debug1 + + + + + Debug2 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + - Current user + Level + + + m_pComboLogLevel + + + + 3 + + + 0 + + + + + + 0 + 0 + + + + Log path + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Minimum + + + + 30 + 20 + + + + + + + + false + + + + 0 + 0 + + + + + + + + false + + + PointingHandCursor + + + margin: 0px; padding: 0px; + + + + + + + :/res/icons/64x64/folder.png:/res/icons/64x64/folder.png + + + + 20 + 13 + + + + true + + + + +
- - - - Qt::Horizontal + + + + + 0 + 0 + - - QDialogButtonBox::Cancel|QDialogButtonBox::Save + + Security - - + + - Qt::Vertical + Qt::Orientation::Vertical - QSizePolicy::Fixed - - - - 20 - 3 - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed + QSizePolicy::Policy::Fixed @@ -962,10 +938,6 @@ background-color: rgba(192,192,192, 0.1); m_pCheckBoxPreventSleep m_pCheckBoxServiceEnabled m_pComboElevate - m_pCheckBoxScrollDirection - m_pCheckBoxLanguageSync - m_pCheckBoxServerClientMode - m_pCheckBoxClientHostMode diff --git a/src/gui/src/TrayIcon.cpp b/src/gui/src/TrayIcon.cpp index 35c9940c9..5cb57fd1d 100644 --- a/src/gui/src/TrayIcon.cpp +++ b/src/gui/src/TrayIcon.cpp @@ -1,3 +1,20 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2021 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "TrayIcon.h" void TrayIcon::tryCreate() const { diff --git a/src/gui/src/TrayIcon.h b/src/gui/src/TrayIcon.h index 00487a80f..a8bdb0140 100644 --- a/src/gui/src/TrayIcon.h +++ b/src/gui/src/TrayIcon.h @@ -1,4 +1,22 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2021 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #pragma once + #include #include #include diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index 3626475af..1ec3aeddd 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -17,11 +17,11 @@ */ #include "AppConfig.h" -#include "LicenseManager.h" #include "MainWindow.h" #include "QSynergyApplication.h" #include "SetupWizard.h" #include "SetupWizardBlocker.h" +#include "gui/dotenv.h" #include #include @@ -29,9 +29,6 @@ #if defined(Q_OS_MAC) #include -#endif - -#ifdef Q_OS_DARWIN #include #endif @@ -55,6 +52,8 @@ int main(int argc, char *argv[]) { ::setenv("QT_BEARER_POLL_TIMEOUT", "-1", 1); #endif + dotenv(".env"); + QCoreApplication::setOrganizationName("Synergy"); QCoreApplication::setOrganizationDomain("http://symless.com/"); QCoreApplication::setApplicationName("Synergy"); @@ -79,16 +78,11 @@ int main(int argc, char *argv[]) { AppConfig appConfig; qRegisterMetaType("Edition"); -#ifdef SYNERGY_ENABLE_LICENSING - LicenseManager licenseManager(&appConfig); - MainWindow mainWindow(appConfig, licenseManager); -#else MainWindow mainWindow(appConfig); -#endif QObject::connect( - dynamic_cast(&app), SIGNAL(aboutToQuit()), &mainWindow, - SLOT(saveSettings())); + &app, &QSynergyApplication::aboutToQuit, &mainWindow, + &MainWindow::onAppAboutToQuit); std::unique_ptr setupBlocker; if (qgetenv("XDG_SESSION_TYPE") == "wayland") { diff --git a/src/gui/src/widgets/ServerStateLabel.cpp b/src/gui/src/widgets/ServerStateLabel.cpp index 3a52a1699..c867d1a5e 100644 --- a/src/gui/src/widgets/ServerStateLabel.cpp +++ b/src/gui/src/widgets/ServerStateLabel.cpp @@ -35,14 +35,10 @@ void ServerStateLabel::updateServerState(const QString &line) { m_clients.removeAll(message.getClientName()); } - updateState(); -} - -void ServerStateLabel::updateState() { if (m_clients.isEmpty()) { - show(); + setText(tr("No clients connected")); } else { - hide(); + setText(tr("Connected clients: %1").arg(m_clients.join(", "))); } } diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index aa70da5e4..a5c5981bc 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -25,5 +25,6 @@ add_subdirectory(net) add_subdirectory(platform) add_subdirectory(server) add_subdirectory(synergy) -add_subdirectory(shared) +add_subdirectory(global) +add_subdirectory(license) add_subdirectory(gui) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 75028710d..dac37fbb7 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -56,7 +56,7 @@ Client::Client( IEventQueue *events, const String &name, const NetworkAddress &address, ISocketFactory *socketFactory, synergy::Screen *screen, - lib::synergy::ClientArgs const &args) + synergy::ClientArgs const &args) : m_mock(false), m_name(name), m_serverAddress(address), diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index 0072e61c3..274851034 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -65,7 +65,7 @@ public: Client( IEventQueue *events, const String &name, const NetworkAddress &address, ISocketFactory *socketFactory, synergy::Screen *screen, - lib::synergy::ClientArgs const &args); + synergy::ClientArgs const &args); Client(Client const &) = delete; Client(Client &&) = delete; ~Client(); @@ -248,6 +248,6 @@ private: bool m_useSecureNetwork; bool m_enableClipboard; size_t m_maximumClipboardSize; - lib::synergy::ClientArgs m_args; + synergy::ClientArgs m_args; size_t m_resolvedAddressesCount = 0; }; diff --git a/src/lib/shared/CMakeLists.txt b/src/lib/global/CMakeLists.txt similarity index 91% rename from src/lib/shared/CMakeLists.txt rename to src/lib/global/CMakeLists.txt index a58291bfe..909e421eb 100644 --- a/src/lib/shared/CMakeLists.txt +++ b/src/lib/global/CMakeLists.txt @@ -21,6 +21,6 @@ if(ADD_HEADERS_TO_SOURCES) list(APPEND sources ${headers}) endif() -add_library(shared STATIC ${sources}) +add_library(global STATIC ${sources}) -target_link_libraries(shared arch base) +target_link_libraries(global) diff --git a/src/lib/shared/Ipc.cpp b/src/lib/global/Ipc.cpp similarity index 100% rename from src/lib/shared/Ipc.cpp rename to src/lib/global/Ipc.cpp diff --git a/src/lib/shared/Ipc.h b/src/lib/global/Ipc.h similarity index 100% rename from src/lib/shared/Ipc.h rename to src/lib/global/Ipc.h diff --git a/src/lib/gui/CMakeLists.txt b/src/lib/gui/CMakeLists.txt index bda1d9378..67ff3679d 100644 --- a/src/lib/gui/CMakeLists.txt +++ b/src/lib/gui/CMakeLists.txt @@ -30,7 +30,8 @@ endif() add_library(${target} STATIC ${sources}) target_link_libraries( ${target} - shared + global + license Qt6::Core Qt6::Widgets Qt6::Network) diff --git a/src/gui/src/CommandProcess.cpp b/src/lib/gui/CommandProcess.cpp similarity index 93% rename from src/gui/src/CommandProcess.cpp rename to src/lib/gui/CommandProcess.cpp index 8a54e6554..b43b07102 100644 --- a/src/gui/src/CommandProcess.cpp +++ b/src/lib/gui/CommandProcess.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014-2016 Symless Ltd. + * Copyright (C) 2014 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -20,8 +20,8 @@ #include CommandProcess::CommandProcess( - QString cmd, QStringList arguments, QString input) - : m_Command(cmd), + QString command, QStringList arguments, QString input) + : m_Command(command), m_Arguments(arguments), m_Input(input) {} diff --git a/src/gui/src/CommandProcess.h b/src/lib/gui/CommandProcess.h similarity index 96% rename from src/gui/src/CommandProcess.h rename to src/lib/gui/CommandProcess.h index 98eb4aecd..bf0c9b2c8 100644 --- a/src/gui/src/CommandProcess.h +++ b/src/lib/gui/CommandProcess.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014-2016 Symless Ltd. + * Copyright (C) 2014 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/gui/src/CoreInterface.cpp b/src/lib/gui/CoreInterface.cpp similarity index 85% rename from src/gui/src/CoreInterface.cpp rename to src/lib/gui/CoreInterface.cpp index 43b95c3ba..b5370d12a 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/lib/gui/CoreInterface.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-2016 Symless Ltd. + * Copyright (C) 2015 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -#include "CoreInterface.h" +#include "gui/CoreInterface.h" #include "CommandProcess.h" @@ -32,22 +32,22 @@ static const char kSerialKeyFilename[] = "Synergy.subkey"; static const char kSerialKeyFilename[] = ".synergy.subkey"; #endif -QString CoreInterface::getProfileDir() { +QString CoreInterface::getProfileDir() const { QStringList args("--get-profile-dir"); return QDir::cleanPath(run(args)); } -QString CoreInterface::getInstalledDir() { +QString CoreInterface::getInstalledDir() const { QStringList args("--get-installed-dir"); return QDir::cleanPath(run(args)); } -QString CoreInterface::getArch() { +QString CoreInterface::getArch() const { QStringList args("--get-arch"); return run(args); } -QString CoreInterface::getSerialKeyFilePath() { +QString CoreInterface::getSerialKeyFilePath() const { auto filename = getProfileDir() + QDir::separator() + kSerialKeyFilename; return QDir::cleanPath(filename); } diff --git a/src/gui/src/CoreInterface.h b/src/lib/gui/CoreInterface.h similarity index 82% rename from src/gui/src/CoreInterface.h rename to src/lib/gui/CoreInterface.h index 8cabff22c..3d1b62bd7 100644 --- a/src/gui/src/CoreInterface.h +++ b/src/lib/gui/CoreInterface.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-2016 Symless Ltd. + * Copyright (C) 2015 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -21,9 +21,9 @@ class CoreInterface { public: - QString getProfileDir(); - QString getInstalledDir(); - QString getArch(); - QString getSerialKeyFilePath(); + QString getProfileDir() const; + QString getInstalledDir() const; + QString getArch() const; + QString getSerialKeyFilePath() const; QString run(const QStringList &args, const QString &input = "") const; }; diff --git a/src/gui/src/LicenseRegistry.h b/src/lib/gui/IAppConfig.h similarity index 58% rename from src/gui/src/LicenseRegistry.h rename to src/lib/gui/IAppConfig.h index 1712ab5bc..8e6e9fee1 100644 --- a/src/gui/src/LicenseRegistry.h +++ b/src/lib/gui/IAppConfig.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2022 Synergy Ltd. + * Copyright (C) 2024 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -14,30 +14,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + #pragma once -#include -#include -#include +#include -class QNetworkReply; -class AppConfig; - -class LicenseRegistry : public QObject { - Q_OBJECT +namespace synergy::gui { +class IAppConfig { public: - explicit LicenseRegistry(AppConfig &config); - void scheduleRegistration(); - -public slots: - void registerLicense(); - void handleResponse(QNetworkReply *reply); - -private: - AppConfig &m_config; - QNetworkAccessManager m_manager; - QTimer m_timer; - - QByteArray getRequestData() const; + virtual ~IAppConfig() = default; + virtual QString tlsCertPath() const = 0; + virtual QString tlsKeyLength() const = 0; + virtual bool tlsEnabled() const = 0; }; + +} // namespace synergy::gui diff --git a/src/lib/gui/IpcReader.cpp b/src/lib/gui/IpcReader.cpp index 8631e010f..7848df17b 100644 --- a/src/lib/gui/IpcReader.cpp +++ b/src/lib/gui/IpcReader.cpp @@ -1,7 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2012 Nick Bolton + * Copyright (C) 2012 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -17,7 +16,8 @@ */ #include "IpcReader.h" -#include "shared/Ipc.h" +#include "global/Ipc.h" + #include #include #include diff --git a/src/lib/gui/IpcReader.h b/src/lib/gui/IpcReader.h index 0e473aa36..1d8a7c366 100644 --- a/src/lib/gui/IpcReader.h +++ b/src/lib/gui/IpcReader.h @@ -1,7 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2012 Nick Bolton + * Copyright (C) 2012 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/lib/gui/LicenseHandler.cpp b/src/lib/gui/LicenseHandler.cpp new file mode 100644 index 000000000..49939e60a --- /dev/null +++ b/src/lib/gui/LicenseHandler.cpp @@ -0,0 +1,94 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "LicenseHandler.h" + +#include "license/ProductEdition.h" +#include "license/parse_serial_key.h" + +#include +#include +#include + +using namespace std::chrono; +using namespace synergy::license; + +const License &LicenseHandler::license() const { return m_license; } + +Edition LicenseHandler::productEdition() const { + return m_license.productEdition(); +} + +QString LicenseHandler::productName() const { + return QString::fromStdString(m_license.productName()); +} + +LicenseHandler::ChangeSerialKeyResult +LicenseHandler::changeSerialKey(const QString &hexString) { + using enum LicenseHandler::ChangeSerialKeyResult; + + if (hexString.isEmpty()) { + qFatal("serial key is empty"); + return kFatal; + } + + qDebug() << "changing serial key to:" << hexString; + auto serialKey = parseSerialKey(hexString.toStdString()); + + if (serialKey == m_license.serialKey()) { + qDebug("serial key did not change, ignoring"); + return kUnchanged; + } + + if (!serialKey.isValid) { + qDebug() << "invalid serial key, ignoring"; + return kInvalid; + } + + const auto license = License(serialKey); + if (license.isExpired()) { + qDebug("license is expired, ignoring"); + return kExpired; + } + + m_license = license; + emit serialKeyChanged(hexString); + + if (m_license.isSubscription()) { + auto daysLeft = m_license.daysLeft(); + auto msLeft = duration_cast(daysLeft); + if (msLeft.count() < INT_MAX) { + QTimer::singleShot(msLeft, this, SLOT(validateLicense())); + } else { + qDebug("license expiry too distant to schedule timer"); + } + } + + return kSuccess; +} + +void LicenseHandler::validate() const { + if (!m_license.isValid()) { + qDebug("license validation failed, license invalid"); + emit invalidLicense(); + } + + if (m_license.isExpired()) { + qDebug("license validation failed, license expired"); + emit invalidLicense(); + } +} diff --git a/src/lib/gui/LicenseHandler.h b/src/lib/gui/LicenseHandler.h new file mode 100644 index 000000000..717cb99c1 --- /dev/null +++ b/src/lib/gui/LicenseHandler.h @@ -0,0 +1,54 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "license/License.h" +#include "license/ProductEdition.h" + +#include + +/** + * @brief A convenience wrapper for `License` that provides Qt signals, etc. + */ +class LicenseHandler : public QObject { + Q_OBJECT + using License = synergy::license::License; + using SerialKey = synergy::license::SerialKey; + +public: + enum class ChangeSerialKeyResult { + kSuccess, + kFatal, + kUnchanged, + kInvalid, + kExpired, + }; + + Edition productEdition() const; + const License &license() const; + void validate() const; + QString productName() const; + ChangeSerialKeyResult changeSerialKey(const QString &hexString); + +signals: + void serialKeyChanged(const QString &serialKey) const; + void invalidLicense() const; + +private: + License m_license = License::invalid(); +}; diff --git a/src/lib/gui/QDataStreamProxy.h b/src/lib/gui/QDataStreamProxy.h index 9a34d5149..5398db4e9 100644 --- a/src/lib/gui/QDataStreamProxy.h +++ b/src/lib/gui/QDataStreamProxy.h @@ -1,3 +1,20 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Synergy Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include /** diff --git a/src/lib/gui/QIpcClient.cpp b/src/lib/gui/QIpcClient.cpp index 157c956e3..3ec540032 100644 --- a/src/lib/gui/QIpcClient.cpp +++ b/src/lib/gui/QIpcClient.cpp @@ -18,7 +18,7 @@ #include "QIpcClient.h" #include "IpcReader.h" -#include "shared/Ipc.h" +#include "global/Ipc.h" #include #include diff --git a/src/gui/src/SslCertificate.cpp b/src/lib/gui/TlsCertificate.cpp similarity index 93% rename from src/gui/src/SslCertificate.cpp rename to src/lib/gui/TlsCertificate.cpp index 5cec5608c..f7206072c 100644 --- a/src/gui/src/SslCertificate.cpp +++ b/src/lib/gui/TlsCertificate.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-2016 Symless Ltd. + * Copyright (C) 2015 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -15,9 +15,9 @@ * along with this program. If not, see . */ -#include "SslCertificate.h" +#include "TlsCertificate.h" -#include "Fingerprint.h" +#include "TlsFingerprint.h" #include #include @@ -89,14 +89,14 @@ QString openSslWindowsBinary() { using namespace synergy::gui; -SslCertificate::SslCertificate(QObject *parent) : QObject(parent) { +TlsCertificate::TlsCertificate(QObject *parent) : QObject(parent) { m_ProfileDir = m_CoreInterface.getProfileDir(); if (m_ProfileDir.isEmpty()) { emit error(tr("Failed to get profile directory.")); } } -bool SslCertificate::runTool(const QStringList &args) { +bool TlsCertificate::runTool(const QStringList &args) { QString program; #if defined(Q_OS_WIN) program = openSslWindowsBinary(); @@ -134,7 +134,7 @@ bool SslCertificate::runTool(const QStringList &args) { return true; } -void SslCertificate::generateCertificate( +void TlsCertificate::generateCertificate( const QString &path, const QString &keyLength, bool forceGen) { QString sslDirPath = QString("%1%2%3").arg(m_ProfileDir).arg(QDir::separator()).arg(kSslDir); @@ -193,7 +193,7 @@ void SslCertificate::generateCertificate( emit generateFinished(); } -void SslCertificate::generateFingerprint(const QString &certificateFilename) { +void TlsCertificate::generateFingerprint(const QString &certificateFilename) { QStringList arguments; arguments.append("x509"); arguments.append("-fingerprint"); @@ -212,14 +212,14 @@ void SslCertificate::generateFingerprint(const QString &certificateFilename) { i++; QString fingerprint = m_ToolOutput.mid(i, m_ToolOutput.size() - i); - Fingerprint::local().trust(fingerprint, false); + TlsFingerprint::local().trust(fingerprint, false); emit info(tr("SSL fingerprint generated.")); } else { emit error(tr("Failed to find SSL fingerprint.")); } } -QString SslCertificate::getCertKeyLength(const QString &path) { +QString TlsCertificate::getCertKeyLength(const QString &path) { QStringList arguments; arguments.append("rsa"); diff --git a/src/gui/src/SslCertificate.h b/src/lib/gui/TlsCertificate.h similarity index 90% rename from src/gui/src/SslCertificate.h rename to src/lib/gui/TlsCertificate.h index 9efea6db5..15de65b70 100644 --- a/src/gui/src/SslCertificate.h +++ b/src/lib/gui/TlsCertificate.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-2016 Symless Ltd. + * Copyright (C) 2015 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -17,16 +17,15 @@ #pragma once -#include "CoreInterface.h" +#include "gui/CoreInterface.h" #include -#include -class SslCertificate : public QObject { +class TlsCertificate : public QObject { Q_OBJECT public: - explicit SslCertificate(QObject *parent = 0); + explicit TlsCertificate(QObject *parent = nullptr); public slots: /// @brief Generates a TLS cert and private key diff --git a/src/gui/src/Fingerprint.cpp b/src/lib/gui/TlsFingerprint.cpp similarity index 65% rename from src/gui/src/Fingerprint.cpp rename to src/lib/gui/TlsFingerprint.cpp index e5aac71e3..1d19ea3a4 100644 --- a/src/gui/src/Fingerprint.cpp +++ b/src/lib/gui/TlsFingerprint.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-2016 Symless Ltd. + * Copyright (C) 2015 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -15,9 +15,9 @@ * along with this program. If not, see . */ -#include "Fingerprint.h" +#include "TlsFingerprint.h" -#include "CoreInterface.h" +#include "gui/CoreInterface.h" #include #include @@ -27,10 +27,12 @@ static const char kLocalFilename[] = "Local.txt"; static const char kTrustedServersFilename[] = "TrustedServers.txt"; static const char kTrustedClientsFilename[] = "TrustedClients.txt"; -Fingerprint::Fingerprint(const QString &filename) { m_Filename = filename; } +TlsFingerprint::TlsFingerprint(const QString &filename) { + m_Filename = filename; +} -void Fingerprint::trust(const QString &fingerprintText, bool append) { - Fingerprint::persistDirectory(); +void TlsFingerprint::trust(const QString &fingerprintText, bool append) { + TlsFingerprint::persistDirectory(); QIODevice::OpenMode openMode; if (append) { @@ -47,8 +49,8 @@ void Fingerprint::trust(const QString &fingerprintText, bool append) { } } -bool Fingerprint::fileExists() const { - QString dirName = Fingerprint::directoryPath(); +bool TlsFingerprint::fileExists() const { + QString dirName = TlsFingerprint::directoryPath(); if (!QDir(dirName).exists()) { return false; } @@ -57,7 +59,7 @@ bool Fingerprint::fileExists() const { return file.exists(); } -bool Fingerprint::isTrusted(const QString &fingerprintText) { +bool TlsFingerprint::isTrusted(const QString &fingerprintText) { QStringList list = readList(); foreach (QString trusted, list) { if (trusted == fingerprintText) { @@ -67,10 +69,10 @@ bool Fingerprint::isTrusted(const QString &fingerprintText) { return false; } -QStringList Fingerprint::readList(const int readTo) { +QStringList TlsFingerprint::readList(const int readTo) { QStringList list; - QString dirName = Fingerprint::directoryPath(); + QString dirName = TlsFingerprint::directoryPath(); if (!QDir(dirName).exists()) { return list; } @@ -91,36 +93,38 @@ QStringList Fingerprint::readList(const int readTo) { return list; } -QString Fingerprint::readFirst() { +QString TlsFingerprint::readFirst() { QStringList list = readList(1); return list.at(0); } -QString Fingerprint::filePath() const { - QString dir = Fingerprint::directoryPath(); +QString TlsFingerprint::filePath() const { + QString dir = TlsFingerprint::directoryPath(); return QString("%1/%2").arg(dir).arg(m_Filename); } -void Fingerprint::persistDirectory() { - QDir dir(Fingerprint::directoryPath()); +void TlsFingerprint::persistDirectory() { + QDir dir(TlsFingerprint::directoryPath()); if (!dir.exists()) { dir.mkpath("."); } } -QString Fingerprint::directoryPath() { +QString TlsFingerprint::directoryPath() { CoreInterface coreInterface; QString profileDir = coreInterface.getProfileDir(); return QString("%1/%2").arg(profileDir).arg(kDirName); } -Fingerprint Fingerprint::local() { return Fingerprint(kLocalFilename); } - -Fingerprint Fingerprint::trustedServers() { - return Fingerprint(kTrustedServersFilename); +TlsFingerprint TlsFingerprint::local() { + return TlsFingerprint(kLocalFilename); } -Fingerprint Fingerprint::trustedClients() { - return Fingerprint(kTrustedClientsFilename); +TlsFingerprint TlsFingerprint::trustedServers() { + return TlsFingerprint(kTrustedServersFilename); +} + +TlsFingerprint TlsFingerprint::trustedClients() { + return TlsFingerprint(kTrustedClientsFilename); } diff --git a/src/gui/src/Fingerprint.h b/src/lib/gui/TlsFingerprint.h similarity index 83% rename from src/gui/src/Fingerprint.h rename to src/lib/gui/TlsFingerprint.h index 563967a4d..a4f1a4275 100644 --- a/src/gui/src/Fingerprint.h +++ b/src/lib/gui/TlsFingerprint.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-2016 Symless Ltd. + * Copyright (C) 2015 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,9 +19,9 @@ #include -class Fingerprint { +class TlsFingerprint { private: - Fingerprint(const QString &filename); + TlsFingerprint(const QString &filename); public: void trust(const QString &fingerprintText, bool append = true); @@ -32,9 +32,9 @@ public: bool fileExists() const; public: - static Fingerprint local(); - static Fingerprint trustedServers(); - static Fingerprint trustedClients(); + static TlsFingerprint local(); + static TlsFingerprint trustedServers(); + static TlsFingerprint trustedClients(); static QString directoryPath(); static QString localFingerprint(); static bool localFingerprintExists(); diff --git a/src/lib/gui/TlsUtility.cpp b/src/lib/gui/TlsUtility.cpp new file mode 100644 index 000000000..b9b5afea2 --- /dev/null +++ b/src/lib/gui/TlsUtility.cpp @@ -0,0 +1,47 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "TlsUtility.h" + +#include "TlsCertificate.h" +#include "constants.h" + +namespace synergy::gui { + +bool TlsUtility::isAvailable() const { + return !kLicensingEnabled || m_license.isTlsAvailable(); +} + +bool TlsUtility::isAvailableAndEnabled() const { + const auto &config = m_appConfig; + return isAvailable() && config.tlsEnabled(); +} + +void TlsUtility::generateCertificate(bool replace) const { + if (!isAvailableAndEnabled()) { + qFatal("unable to generate tls certificate, " + "tls is either not available or not enabled"); + } + + auto path = m_appConfig.tlsCertPath(); + auto length = m_appConfig.tlsKeyLength(); + + TlsCertificate cert; + cert.generateCertificate(path, length, replace); +} + +} // namespace synergy::gui diff --git a/src/lib/gui/TlsUtility.h b/src/lib/gui/TlsUtility.h new file mode 100644 index 000000000..c49372345 --- /dev/null +++ b/src/lib/gui/TlsUtility.h @@ -0,0 +1,58 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "IAppConfig.h" + +#include "license/License.h" +#include + +namespace synergy::gui { + +class TlsUtility { +public: + explicit TlsUtility( + const IAppConfig &appConfig, const license::License &license) + : m_appConfig(appConfig), + m_license(license) {} + + /** + * @brief Combines the availability and the enabled status of TLS. + * + * @return Given that the app setting for TLS is enabled: + * If licensing is enabled, it checks whether the product has TLS + * available, and if licensing is not enabled, true is returned. + */ + bool isAvailableAndEnabled() const; + + /** + * @return true if TLS is available, regardless of whether it is enabled. + */ + bool isAvailable() const; + + /** + * @param replace Replace certificate on disk (default: false). + */ + void generateCertificate(bool replace = false) const; + +private: + const IAppConfig &m_appConfig; + const license::License &m_license; +}; + +} // namespace synergy::gui diff --git a/src/lib/gui/VersionChecker.cpp b/src/lib/gui/VersionChecker.cpp index 26d176262..785061638 100644 --- a/src/lib/gui/VersionChecker.cpp +++ b/src/lib/gui/VersionChecker.cpp @@ -1,7 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 2012 Nick Bolton + * Copyright (C) 2012 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -26,23 +25,28 @@ #include #include -VersionChecker::VersionChecker(std::shared_ptr nam) - : m_manager(nam ? nam : std::make_shared(this)) { +const char *const kVersion = SYNERGY_VERSION; + +VersionChecker::VersionChecker(std::shared_ptr network) + : m_network( + network ? network : std::make_shared(this)) { connect( - m_manager.get(), SIGNAL(finished(QNetworkReply *)), this, + m_network.get(), SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *))); } -void VersionChecker::checkLatest() { - auto request = QNetworkRequest(QUrl(SYNERGY_VERSION_URL)); - request.setHeader( - QNetworkRequest::UserAgentHeader, QString("Synergy (") + SYNERGY_VERSION + - ") " + - QSysInfo::prettyProductName()); - request.setRawHeader("X-Synergy-Version", SYNERGY_VERSION); +void VersionChecker::checkLatest() const { + const QString url = + qEnvironmentVariable("SYNERGY_VERSION_URL", SYNERGY_VERSION_URL); + auto request = QNetworkRequest(url); + auto userAgent = QString("Synergy %1 on %2") + .arg(kVersion) + .arg(QSysInfo::prettyProductName()); + request.setHeader(QNetworkRequest::UserAgentHeader, userAgent); + request.setRawHeader("X-Synergy-Version", kVersion); request.setRawHeader( "X-Synergy-Language", QLocale::system().name().toStdString().c_str()); - m_manager->get(request); + m_network->get(request); } void VersionChecker::replyFinished(QNetworkReply *reply) { @@ -53,7 +57,7 @@ void VersionChecker::replyFinished(QNetworkReply *reply) { } } -int VersionChecker::getStageVersion(QString stage) const { +int VersionChecker::getStageVersion(QString stage) { const char *stableName = "stable"; const char *rcName = "rc"; const char *betaName = "beta"; diff --git a/src/lib/gui/VersionChecker.h b/src/lib/gui/VersionChecker.h index 32695d7ee..3ad1e3472 100644 --- a/src/lib/gui/VersionChecker.h +++ b/src/lib/gui/VersionChecker.h @@ -1,7 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2019 Symless Ltd. - * Copyright (C) 2012 Nick Bolton + * Copyright (C) 2012 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -24,23 +23,24 @@ class QNetworkAccessManager; class QNetworkReply; +class VersionCheckerTests; class VersionChecker : public QObject { Q_OBJECT + + friend class VersionCheckerTests; + public: - VersionChecker(std::shared_ptr manager = nullptr); - virtual ~VersionChecker() {} - void checkLatest(); - void setApp(const QString &app) { m_app = app; } - int compareVersions(const QString &left, const QString &right); + explicit VersionChecker( + std::shared_ptr network = nullptr); + void checkLatest() const; public slots: void replyFinished(QNetworkReply *reply); signals: void updateFound(const QString &version); private: - std::shared_ptr m_manager; - QString m_app; + static int compareVersions(const QString &left, const QString &right); /** * \brief Converts a string stage to a integer value @@ -48,5 +48,7 @@ private: * \return An integer representation of the stage, the higher the number the * more recent the version */ - int getStageVersion(QString stage) const; + static int getStageVersion(QString stage); + + std::shared_ptr m_network; }; diff --git a/src/lib/gui/constants.h b/src/lib/gui/constants.h new file mode 100644 index 000000000..0f175b800 --- /dev/null +++ b/src/lib/gui/constants.h @@ -0,0 +1,50 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2014 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#ifdef SYNERGY_ENABLE_LICENSING +const bool kLicensingEnabled = true; +#else +const bool kLicensingEnabled = false; +#endif // SYNERGY_ENABLE_LICENSING + +const auto kColorWhite = "#ffffff"; +const auto kColorPrimary = "#ff7c00"; +const auto kColorSecondary = "#4285f4"; + +const auto kLinkStyleWhite = QString("color: %1").arg(kColorWhite); +const auto kLinkStylePrimary = QString("color: %1").arg(kColorPrimary); +const auto kLinkStyleSecondary = QString("color: %1").arg(kColorSecondary); + +const auto kLinkBuy = R"(Buy now)"; +const auto kLinkRenew = R"(Renew now)"; +const auto kLinkDownload = R"(Download now)"; + +const auto kUrlSourceQuery = "source=gui"; +const auto kUrlWebsite = "https://symless.com"; +const auto kUrlProduct = QString("%1/synergy").arg(kUrlWebsite); +const auto kUrlPurchase = + QString("%1/purchase?%2").arg(kUrlProduct).arg(kUrlSourceQuery); +const auto kUrlContact = + QString("%1/contact?%2").arg(kUrlProduct).arg(kUrlSourceQuery); +const auto kUrlHelp = + QString("%1/help?%2").arg(kUrlProduct).arg(kUrlSourceQuery); +const auto kUrlDownload = + QString("%1/download?%2").arg(kUrlProduct).arg(kUrlSourceQuery); diff --git a/src/lib/gui/dotenv.cpp b/src/lib/gui/dotenv.cpp new file mode 100644 index 000000000..66becf958 --- /dev/null +++ b/src/lib/gui/dotenv.cpp @@ -0,0 +1,91 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "dotenv.h" + +#include +#include +#include +#include +#include +#include + +namespace synergy::gui { + +QPair getPair(const QString &line); + +/** + * @brief A _very_ basic Qt .env file parser. + * + * This function re-invents the wheel to save adding a library dependency. + * It does not support many things that a proper .env parser would, such as + * trailing comments, escaping characters, multiline values, etc. + * + * If this function is not sufficient, replace it with a library such as: + * https://github.com/adeharo9/cpp-dotenv + */ +void dotenv(const QString &filePath) { + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug("no .env file in current dir"); + return; + } + + qDebug("loading env vars from: %s", filePath.toUtf8().constData()); + + QTextStream in(&file); + while (!in.atEnd()) { + QString line = in.readLine().trimmed(); + if (line.startsWith('#')) + continue; + + auto [key, value] = getPair(line); + if (key.isEmpty() || value.isEmpty()) + continue; + + const auto keyBytes = key.toUtf8(); + const auto valueBytes = value.toUtf8(); + + const auto &key_c = keyBytes.constData(); + const auto &value_c = valueBytes.constData(); + + qDebug("%s=%s", key_c, value_c); + qputenv(keyBytes, valueBytes); + } +} + +QString stripQuotes(const QString &value) { + QString result = value; + if (result.startsWith('"') && result.endsWith('"')) { + result = result.mid(1, result.length() - 2); + } + return result; +} + +QPair getPair(const QString &line) { + auto pos = line.indexOf('='); + if (pos == -1) { + return QPair("", ""); + } + + QString key = line.left(pos); + QString value = line.mid(pos + 1); + + return QPair(key.trimmed(), stripQuotes(value.trimmed())); +} + +} // namespace synergy::gui diff --git a/src/lib/gui/dotenv.h b/src/lib/gui/dotenv.h new file mode 100644 index 000000000..8fa4ee083 --- /dev/null +++ b/src/lib/gui/dotenv.h @@ -0,0 +1,30 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +namespace synergy::gui { + +/** + * @param filePath Path to the .env file. + * @param envVars Somewhere to save strings for the lifetime of the app. + */ +void dotenv(const QString &filePath); + +} // namespace synergy::gui diff --git a/src/lib/gui/license_notices.cpp b/src/lib/gui/license_notices.cpp new file mode 100644 index 000000000..a399a0d25 --- /dev/null +++ b/src/lib/gui/license_notices.cpp @@ -0,0 +1,76 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Synergy Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "license_notices.h" + +#include "constants.h" +#include "license/License.h" + +using License = synergy::license::License; + +namespace synergy::gui { + +QString trialLicenseNotice(const License &license); +QString subscriptionLicenseNotice(const License &license); + +QString licenseNotice(const License &license) { + if (license.isTrial()) { + return trialLicenseNotice(license); + } else if (license.isSubscription()) { + return subscriptionLicenseNotice(license); + } else { + qFatal("license notice only for time limited licenses"); + } +} + +QString trialLicenseNotice(const License &license) { + const QString buyLink = + QString(kLinkBuy).arg(kUrlPurchase).arg(kLinkStyleWhite); + if (license.isExpired()) { + return QString("

Your trial has expired. %1

").arg(buyLink); + } else { + auto daysLeft = license.daysLeft().count(); + if (daysLeft <= 0) { + return QString("

Your trial expires today. %1

").arg(buyLink); + } else { + return QString("

Your trial expires in %1 %2. %3

") + .arg(daysLeft) + .arg((daysLeft == 1) ? "day" : "days") + .arg(buyLink); + } + } +} + +QString subscriptionLicenseNotice(const License &license) { + const QString renewLink = + QString(kLinkRenew).arg(kUrlPurchase).arg(kLinkStyleWhite); + if (license.isExpired()) { + return QString("

Your license has expired. %1

").arg(renewLink); + } else { + auto daysLeft = license.daysLeft().count(); + if (daysLeft <= 0) { + return QString("

Your license expires today. %1

").arg(renewLink); + } else { + return QString("

Your license expires in %1 %2. %3

") + .arg(daysLeft) + .arg((daysLeft == 1) ? "day" : "days") + .arg(renewLink); + } + } +} + +} // namespace synergy::gui diff --git a/src/gui/src/ActivationNotifier.h b/src/lib/gui/license_notices.h similarity index 52% rename from src/gui/src/ActivationNotifier.h rename to src/lib/gui/license_notices.h index 554fe536f..153623b0e 100644 --- a/src/gui/src/ActivationNotifier.h +++ b/src/lib/gui/license_notices.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Ltd. + * Copyright (C) 2024 Synergy Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -15,33 +15,14 @@ * along with this program. If not, see . */ -#ifndef ACTIVATIONNOTIFIER_H -#define ACTIVATIONNOTIFIER_H +#pragma once -#include +#include "license/License.h" -class ActivationNotifier : public QObject { - Q_OBJECT -public: - explicit ActivationNotifier(QObject *parent = 0); +#include - void setIdentity(QString identity); - void setUpdateInfo( - QString const &fromVersion, QString const &toVersion, - QString const &serialKey); +namespace synergy::gui { -public slots: - void notify(); - void notifyUpdate(); +QString licenseNotice(const synergy::license::License &license); -signals: - void finished(); - -private: - QString m_Identity; - QString m_fromVersion; - QString m_toVersion; - QString m_serialKey; -}; - -#endif // ACTIVATIONNOTIFIER_H +} // namespace synergy::gui diff --git a/src/lib/ipc/IpcClient.cpp b/src/lib/ipc/IpcClient.cpp index c9bd18797..1cf83634b 100644 --- a/src/lib/ipc/IpcClient.cpp +++ b/src/lib/ipc/IpcClient.cpp @@ -17,9 +17,9 @@ #include "ipc/IpcClient.h" #include "base/TMethodEventJob.h" +#include "global/Ipc.h" #include "ipc/IpcMessage.h" #include "ipc/IpcServerProxy.h" -#include "shared/Ipc.h" // // IpcClient diff --git a/src/lib/ipc/IpcClientProxy.cpp b/src/lib/ipc/IpcClientProxy.cpp index 2c9949e90..3ee3843d8 100644 --- a/src/lib/ipc/IpcClientProxy.cpp +++ b/src/lib/ipc/IpcClientProxy.cpp @@ -19,10 +19,10 @@ #include "base/Log.h" #include "base/TMethodEventJob.h" +#include "global/Ipc.h" #include "io/IStream.h" #include "ipc/IpcMessage.h" #include "ipc/IpcSettingMessage.h" -#include "shared/Ipc.h" #include "synergy/ProtocolUtil.h" // diff --git a/src/lib/ipc/IpcClientProxy.h b/src/lib/ipc/IpcClientProxy.h index 7362d16a2..a41fda093 100644 --- a/src/lib/ipc/IpcClientProxy.h +++ b/src/lib/ipc/IpcClientProxy.h @@ -21,7 +21,7 @@ #include "arch/IArchMultithread.h" #include "base/Event.h" #include "base/EventTypes.h" -#include "shared/Ipc.h" +#include "global/Ipc.h" namespace synergy { class IStream; diff --git a/src/lib/ipc/IpcLogOutputter.cpp b/src/lib/ipc/IpcLogOutputter.cpp index 2b4213100..bc322e972 100644 --- a/src/lib/ipc/IpcLogOutputter.cpp +++ b/src/lib/ipc/IpcLogOutputter.cpp @@ -24,11 +24,11 @@ #include "base/EventQueue.h" #include "base/TMethodEventJob.h" #include "base/TMethodJob.h" +#include "global/Ipc.h" #include "ipc/IpcClientProxy.h" #include "ipc/IpcMessage.h" #include "ipc/IpcServer.h" #include "mt/Thread.h" -#include "shared/Ipc.h" enum EIpcLogOutputter { kBufferMaxSize = 1000, diff --git a/src/lib/ipc/IpcLogOutputter.h b/src/lib/ipc/IpcLogOutputter.h index a38a3380e..06deeee66 100644 --- a/src/lib/ipc/IpcLogOutputter.h +++ b/src/lib/ipc/IpcLogOutputter.h @@ -21,7 +21,7 @@ #include "arch/Arch.h" #include "arch/IArchMultithread.h" #include "base/ILogOutputter.h" -#include "shared/Ipc.h" +#include "global/Ipc.h" #include diff --git a/src/lib/ipc/IpcMessage.cpp b/src/lib/ipc/IpcMessage.cpp index 955564b39..dc51f3518 100644 --- a/src/lib/ipc/IpcMessage.cpp +++ b/src/lib/ipc/IpcMessage.cpp @@ -17,7 +17,7 @@ */ #include "ipc/IpcMessage.h" -#include "shared/Ipc.h" +#include "global/Ipc.h" IpcMessage::IpcMessage(IpcMessageType type) : m_type(type) {} diff --git a/src/lib/ipc/IpcMessage.h b/src/lib/ipc/IpcMessage.h index 594d6c666..dcd417043 100644 --- a/src/lib/ipc/IpcMessage.h +++ b/src/lib/ipc/IpcMessage.h @@ -19,7 +19,7 @@ #include "base/Event.h" #include "base/String.h" -#include "shared/Ipc.h" +#include "global/Ipc.h" class IpcMessage : public EventData { public: diff --git a/src/lib/ipc/IpcServer.cpp b/src/lib/ipc/IpcServer.cpp index 0c3f6f730..bf1dc87d4 100644 --- a/src/lib/ipc/IpcServer.cpp +++ b/src/lib/ipc/IpcServer.cpp @@ -22,11 +22,11 @@ #include "base/IEventQueue.h" #include "base/Log.h" #include "base/TMethodEventJob.h" +#include "global/Ipc.h" #include "io/IStream.h" #include "ipc/IpcClientProxy.h" #include "ipc/IpcMessage.h" #include "net/IDataSocket.h" -#include "shared/Ipc.h" // // IpcServer diff --git a/src/lib/ipc/IpcServer.h b/src/lib/ipc/IpcServer.h index 453a9abe9..c21998599 100644 --- a/src/lib/ipc/IpcServer.h +++ b/src/lib/ipc/IpcServer.h @@ -20,9 +20,9 @@ #include "arch/Arch.h" #include "base/EventTypes.h" +#include "global/Ipc.h" #include "net/NetworkAddress.h" #include "net/TCPListenSocket.h" -#include "shared/Ipc.h" #include diff --git a/src/lib/ipc/IpcServerProxy.cpp b/src/lib/ipc/IpcServerProxy.cpp index 25578e93f..2e34f5879 100644 --- a/src/lib/ipc/IpcServerProxy.cpp +++ b/src/lib/ipc/IpcServerProxy.cpp @@ -20,9 +20,9 @@ #include "base/Log.h" #include "base/TMethodEventJob.h" +#include "global/Ipc.h" #include "io/IStream.h" #include "ipc/IpcMessage.h" -#include "shared/Ipc.h" #include "synergy/ProtocolUtil.h" // diff --git a/src/lib/license/CMakeLists.txt b/src/lib/license/CMakeLists.txt new file mode 100644 index 000000000..024ecda9e --- /dev/null +++ b/src/lib/license/CMakeLists.txt @@ -0,0 +1,26 @@ +# Synergy -- mouse and keyboard sharing utility +# Copyright (C) 2012-2024 Symless Ltd. +# Copyright (C) 2009-2012 Nick Bolton +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file LICENSE that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +file(GLOB headers "*.h") +file(GLOB sources "*.cpp") + +if(ADD_HEADERS_TO_SOURCES) + list(APPEND sources ${headers}) +endif() + +add_library(license STATIC ${sources}) + +target_link_libraries(license arch base) diff --git a/src/lib/license/License.cpp b/src/lib/license/License.cpp new file mode 100644 index 000000000..cc10c07bc --- /dev/null +++ b/src/lib/license/License.cpp @@ -0,0 +1,99 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "License.h" + +#include "Product.h" +#include "license/SerialKey.h" +#include "license/parse_serial_key.h" + +#include +#include + +using namespace std::chrono; + +namespace synergy::license { + +License::License(const std::string &hexString) + : m_serialKey(parseSerialKey(hexString)) {} + +License::License(const SerialKey &serialKey) : m_serialKey(serialKey) { + if (!m_serialKey.isValid) { + throw InvalidSerialKey(); + } +} + +bool License::isTrial() const { return m_serialKey.type.isTrial(); } + +bool License::isSubscription() const { + return m_serialKey.type.isSubscription(); +} + +bool License::isTimeLimited() const { return m_serialKey.type.isTimeLimited(); } + +bool License::isTlsAvailable() const { + return m_serialKey.product.isTlsAvailable(); +} + +Edition License::productEdition() const { + return m_serialKey.product.edition(); +} + +bool License::isExpiring() const { + if (!isTimeLimited()) { + return false; + } + + if (!m_serialKey.warnTime.has_value()) { + throw NoTimeLimitError(); + } + + return m_nowFunc() >= m_serialKey.warnTime.value(); +} + +bool License::isExpired() const { + if (!isTimeLimited()) { + return false; + } + + if (!m_serialKey.expireTime.has_value()) { + throw NoTimeLimitError(); + } + + return m_nowFunc() >= m_serialKey.expireTime.value(); +} + +days License::daysLeft() const { + if (!m_serialKey.expireTime.has_value()) { + throw NoTimeLimitError(); + } + + auto expireTime = m_serialKey.expireTime.value(); + + auto timeLeft = expireTime - m_nowFunc(); + return duration_cast(timeLeft); +} + +std::string License::productName() const { + auto name = m_serialKey.product.name(); + if (m_serialKey.type.isTrial()) { + name += " (Trial)"; + } + return name; +} + +} // namespace synergy::license diff --git a/src/lib/license/License.h b/src/lib/license/License.h new file mode 100644 index 000000000..c278a7d96 --- /dev/null +++ b/src/lib/license/License.h @@ -0,0 +1,93 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "SerialKey.h" + +#include +#include +#include +#include + +class Server; +class LicenseHandler; +class LicenseTests; + +namespace synergy::license { + +class License { + friend class ::Server; + friend class ::LicenseHandler; + friend class ::LicenseTests; + + using days = std::chrono::days; + using system_clock = std::chrono::system_clock; + using time_point = system_clock::time_point; + using NowFunc = std::function; + using LicenseError = std::runtime_error; + +public: + explicit License(const SerialKey &serialKey); + explicit License(const std::string &hexString); + + friend bool operator==(License const &lhs, License const &rhs) { + return lhs.m_serialKey == rhs.m_serialKey; + } + + bool isValid() const { return m_serialKey.isValid; } + bool isExpiring() const; + bool isExpired() const; + bool isTrial() const; + bool isSubscription() const; + bool isTimeLimited() const; + bool isTlsAvailable() const; + days daysLeft() const; + Edition productEdition() const; + std::string productName() const; + const SerialKey &serialKey() const { return m_serialKey; } + void invalidate() { m_serialKey = SerialKey::invalid(); } + + class InvalidSerialKey : public LicenseError { + public: + explicit InvalidSerialKey() : LicenseError("invalid serial key") {} + }; + + class NoTimeLimitError : public LicenseError { + public: + explicit NoTimeLimitError() + : LicenseError("serial key has no time limit") {} + }; + +protected: + void setNowFunc(const NowFunc &nowFunc) { m_nowFunc = nowFunc; } + +private: + // for intentionality, force use of `invalid()` static function. + License() = default; + + // prevent copy, so that changes can be reflected in one instance. + License(const License &) = default; + License &operator=(const License &) = default; + + static License invalid() { return License(); } + + SerialKey m_serialKey = SerialKey::invalid(); + NowFunc m_nowFunc = []() { return system_clock::now(); }; +}; + +} // namespace synergy::license diff --git a/src/lib/license/Product.cpp b/src/lib/license/Product.cpp new file mode 100644 index 000000000..c4116de0e --- /dev/null +++ b/src/lib/license/Product.cpp @@ -0,0 +1,121 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "Product.h" + +using SKE = Product::SerialKeyEditionID; + +const char *const kLicensedProductName = "Synergy 1"; + +const std::string SKE::Pro = "pro"; +const std::string SKE::Basic = "basic"; +const std::string SKE::Buisiness = "business"; + +using Edition = Product::Edition; + +const std::map> kSerialKeyEditions{ + {SKE::Basic, Edition::kBasic}, + {SKE::Pro, Edition::kPro}, + {SKE::Buisiness, Edition::kBusiness}, +}; + +Product::Product(Edition edition) : m_edition(edition) {} + +Product::Product(const std::string &serialKeyEditionID) { + setEdition(serialKeyEditionID); +} + +Edition Product::edition() const { return m_edition; } + +std::string Product::serialKeyId() const { + switch (edition()) { + using enum Edition; + + case kPro: + return SKE::Pro; + + case kBasic: + return SKE::Basic; + + case kBusiness: + return SKE::Buisiness; + + default: + throw InvalidType(); + } +} + +std::string Product::name() const { + + const std::string nameBase = kLicensedProductName; + switch (edition()) { + using enum Edition; + + case kUnregistered: + return nameBase + " (unregistered)"; + + case kBasic: + return nameBase + " Basic"; + + case kPro: + return nameBase + " Pro"; + + case kBusiness: + return nameBase + " Business"; + + default: + throw InvalidType(); + } +} + +void Product::setEdition(Edition edition) { m_edition = edition; } + +void Product::setEdition(const std::string &name) { + const auto &pType = kSerialKeyEditions.find(name); + + if (pType != kSerialKeyEditions.end()) { + m_edition = pType->second; + } else { + throw InvalidType(); + } +} + +bool Product::isValid() const { + if (m_edition == Edition::kUnregistered) { + return false; + } + return kSerialKeyEditions.contains(serialKeyId()); +} + +bool Product::isTlsAvailable() const { + switch (edition()) { + using enum Edition; + + case kPro: + case kBusiness: + return true; + + case kBasic: + case kUnregistered: + return false; + + default: + throw InvalidType(); + } +} diff --git a/src/lib/license/Product.h b/src/lib/license/Product.h new file mode 100644 index 000000000..17183857d --- /dev/null +++ b/src/lib/license/Product.h @@ -0,0 +1,64 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +class Product { + friend bool operator==(Product const &, Product const &) = default; + +public: + class InvalidType : public std::runtime_error { + public: + explicit InvalidType() : std::runtime_error("invalid product type") {} + }; + + enum class Edition { + kUnregistered = -1, + kBasic = 0, + kPro = 1, + kBusiness = 4, + }; + + /** + * @brief Product edition IDs found in a decoded serial key. + */ + class SerialKeyEditionID { + public: + static const std::string Basic; + static const std::string Pro; + static const std::string Buisiness; + }; + + Product() = default; + explicit Product(Edition edition); + explicit Product(const std::string &serialKeyEditionID); + + bool isValid() const; + Edition edition() const; + std::string serialKeyId() const; + std::string name() const; + bool isTlsAvailable() const; + + void setEdition(Edition type); + void setEdition(const std::string &serialKeyId); + +private: + Edition m_edition = Edition::kUnregistered; +}; diff --git a/src/lib/shared/EditionType.h b/src/lib/license/ProductEdition.h similarity index 75% rename from src/lib/shared/EditionType.h rename to src/lib/license/ProductEdition.h index 016d767a9..7ea23798f 100644 --- a/src/lib/shared/EditionType.h +++ b/src/lib/license/ProductEdition.h @@ -15,21 +15,9 @@ * along with this program. If not, see . */ -#ifndef EDITIONTYPE_H -#define EDITIONTYPE_H +#pragma once -/* Do not reorder these! */ +#include "Product.h" -enum Edition { - kBasic, - kPro, - kValueNotUsed, - kUnregistered, - kBusiness, - kBasicChina, - kProChina, - kLite, - kUltimate -}; - -#endif // EDITIONTYPE_H +/// @deprecated Use `Product::Edition` instead +using Edition = Product::Edition; diff --git a/src/lib/license/SerialKey.h b/src/lib/license/SerialKey.h new file mode 100644 index 000000000..55b6fdae4 --- /dev/null +++ b/src/lib/license/SerialKey.h @@ -0,0 +1,60 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2022 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "ProductEdition.h" +#include "SerialKeyType.h" +#include "license/Product.h" +#include "license/ProductEdition.h" + +#include +#include +#include +#include + +namespace synergy::license { + +struct SerialKey { + using time_point = std::chrono::system_clock::time_point; + + friend bool operator==(const SerialKey &lhs, const SerialKey &rhs) { + return (lhs.hexString == rhs.hexString) && (lhs.warnTime == rhs.warnTime) && + (lhs.expireTime == rhs.expireTime) && (lhs.product == rhs.product) && + (lhs.type == rhs.type); + } + + explicit SerialKey(const std::string &key) : hexString(key) {} + + static SerialKey invalid() { + return SerialKey(Product::Edition::kUnregistered); + } + + const std::string &toString() const { return hexString; } + + bool isValid = false; + std::string hexString = ""; + Product product; + SerialKeyType type; + std::optional warnTime = std::nullopt; + std::optional expireTime = std::nullopt; + +private: + explicit SerialKey(Edition edition) : product(edition) {} +}; + +} // namespace synergy::license diff --git a/src/lib/shared/SerialKeyType.cpp b/src/lib/license/SerialKeyType.cpp similarity index 65% rename from src/lib/shared/SerialKeyType.cpp rename to src/lib/license/SerialKeyType.cpp index 60ce249c8..0eff9ea1c 100644 --- a/src/lib/shared/SerialKeyType.cpp +++ b/src/lib/license/SerialKeyType.cpp @@ -19,18 +19,16 @@ const std::string SerialKeyType::Trial = "trial"; const std::string SerialKeyType::Subscription = "subscription"; -const std::string SerialKeyType::Maintenance = "maintenance"; -SerialKeyType::SerialKeyType() {} - -void SerialKeyType::setKeyType(const std::string &Type) { - m_isTrial = (Type == SerialKeyType::Trial); - m_isTemporary = (m_isTrial || (Type == SerialKeyType::Subscription)); - m_isMaintenance = (Type == SerialKeyType::Maintenance); +void SerialKeyType::setType(const std::string_view &type) { + m_isTrial = (type == SerialKeyType::Trial); + m_isSubscription = (type == SerialKeyType::Subscription); } bool SerialKeyType::isTrial() const { return m_isTrial; } -bool SerialKeyType::isTemporary() const { return m_isTemporary; } +bool SerialKeyType::isSubscription() const { return m_isSubscription; } -bool SerialKeyType::isMaintenance() const { return m_isMaintenance; } +bool SerialKeyType::isTimeLimited() const { + return m_isTrial || m_isSubscription; +} diff --git a/src/lib/shared/SerialKeyType.h b/src/lib/license/SerialKeyType.h similarity index 60% rename from src/lib/shared/SerialKeyType.h rename to src/lib/license/SerialKeyType.h index 8fd3eef20..9d34403bb 100644 --- a/src/lib/shared/SerialKeyType.h +++ b/src/lib/license/SerialKeyType.h @@ -21,31 +21,22 @@ class SerialKeyType { private: - friend bool operator==(SerialKeyType const &, SerialKeyType const &); + friend bool + operator==(SerialKeyType const &lhs, SerialKeyType const &rhs) = default; public: static const std::string Trial; static const std::string Subscription; - static const std::string Maintenance; - SerialKeyType(); + explicit SerialKeyType() = default; + explicit SerialKeyType(const std::string_view &type) { setType(type); } - void setKeyType(const std::string &type); + void setType(const std::string_view &type); bool isTrial() const; - bool isTemporary() const; - bool isMaintenance() const; + bool isSubscription() const; + bool isTimeLimited() const; private: bool m_isTrial = false; - bool m_isTemporary = false; - bool m_isMaintenance = false; + bool m_isSubscription = false; }; - -inline bool operator==(SerialKeyType const &lhs, SerialKeyType const &rhs) { - return (lhs.m_isTrial == rhs.m_isTrial) && - (lhs.m_isTemporary == rhs.m_isTemporary); -} - -inline bool operator!=(SerialKeyType const &lhs, SerialKeyType const &rhs) { - return !(lhs == rhs); -} diff --git a/src/lib/license/parse_serial_key.cpp b/src/lib/license/parse_serial_key.cpp new file mode 100644 index 000000000..64776b8ba --- /dev/null +++ b/src/lib/license/parse_serial_key.cpp @@ -0,0 +1,143 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "parse_serial_key.h" + +#include "SerialKey.h" +#include "SerialKeyType.h" +#include "utils/trim.h" + +#include +#include +#include +#include +#include + +using Parts = std::vector; +using system_clock = std::chrono::system_clock; +using time_point = system_clock::time_point; + +namespace synergy::license { + +std::string decode(const std::string &hexString); +Parts tokenize(const std::string &plainText); +SerialKey parseV1(const std::string &hexString, const Parts &parts); +SerialKey parseV2(const std::string &hexString, const Parts &parts); +std::optional parseDate(const std::string &unixTimeString); + +SerialKey parseSerialKey(const std::string &hexString) { + const auto &plainText = decode(hexString); + const auto &parts = tokenize(plainText); + const auto &version = parts.at(0); + + if (version == "v1") { + return parseV1(hexString, parts); + } else if (version == "v2") { + return parseV2(hexString, parts); + } else { + throw InvalidSerialKeyVersion(version); + } +} + +std::string decode(const std::string &hexString) { + std::string trimmed = utils::trim(hexString); + + if (trimmed.length() % 2 != 0) { + throw InvalidHexString(); + } + + std::string plainText; + plainText.reserve(trimmed.length() / 2); + + for (size_t i = 0; i < trimmed.length(); i += 2) { + std::string byteString = trimmed.substr(i, 2); + auto byte = static_cast(std::stoi(byteString, nullptr, 16)); + plainText.push_back(byte); + } + + return plainText; +} + +SerialKey parseV1(const std::string &hexString, const Parts &parts) { + if (parts.size() < 8) { + throw InvalidSerialKeyFormat(); + } + + // e.g.: {v1;basic;name;1;email;company name;1398297600;1398384000} + SerialKey serialKey(hexString); + serialKey.product = Product(parts.at(1)); + serialKey.warnTime = parseDate(parts.at(6)); + serialKey.expireTime = parseDate(parts.at(7)); + serialKey.isValid = true; + return serialKey; +} + +SerialKey parseV2(const std::string &hexString, const Parts &parts) { + if (parts.size() < 9) { + throw InvalidSerialKeyFormat(); + } + // e.g.: {v2;trial;basic;name;1;email;company name;1398297600;1398384000} + SerialKey serialKey(hexString); + serialKey.type = SerialKeyType(parts.at(1)); + serialKey.product = Product(parts.at(2)); + serialKey.warnTime = parseDate(parts.at(7)); + serialKey.expireTime = parseDate(parts.at(8)); + serialKey.isValid = true; + return serialKey; +} + +Parts tokenize(const std::string &plainText) { + if (plainText.front() != '{' || plainText.back() != '}') { + throw InvalidSerialKeyFormat(); + } + + const auto serialData = plainText.substr(1, plainText.length() - 2); + + Parts parts; + std::stringstream ss(serialData); + std::string item; + + while (std::getline(ss, item, ';')) { + parts.push_back(item); + } + + // it's possible that the last character is a delimiter, so add an empty part + if (!serialData.empty() && serialData.back() == ';') { + parts.emplace_back(""); + } + + return parts; +} + +std::optional parseDate(const std::string &unixTimeString) { + auto clean = utils::trim(unixTimeString); + if (clean.empty()) { + return std::nullopt; + } + + try { + auto seconds = std::stoi(clean); + if (seconds < 0) { + throw InvalidSerialKeyDate(unixTimeString); + } + return time_point{std::chrono::seconds{seconds}}; + } catch (const std::invalid_argument &) { + throw InvalidSerialKeyDate(unixTimeString); + } +} + +} // namespace synergy::license diff --git a/src/lib/license/parse_serial_key.h b/src/lib/license/parse_serial_key.h new file mode 100644 index 000000000..289aa250d --- /dev/null +++ b/src/lib/license/parse_serial_key.h @@ -0,0 +1,57 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "SerialKey.h" + +#include +#include + +namespace synergy::license { + +class SerialKeyParseError : public std::runtime_error { +public: + explicit SerialKeyParseError(const std::string &message) + : std::runtime_error(message) {} +}; + +class InvalidHexString : public SerialKeyParseError { +public: + explicit InvalidHexString() + : SerialKeyParseError("invalid hex string length") {} +}; + +class InvalidSerialKeyFormat : public SerialKeyParseError { +public: + explicit InvalidSerialKeyFormat() + : SerialKeyParseError("invalid serial key format") {} +}; + +class InvalidSerialKeyDate : public SerialKeyParseError { +public: + explicit InvalidSerialKeyDate(const std::string &date) + : SerialKeyParseError("invalid serial key date: " + date) {} +}; + +class InvalidSerialKeyVersion : public SerialKeyParseError { +public: + explicit InvalidSerialKeyVersion(const std::string &version) + : SerialKeyParseError("invalid serial key version: " + version) {} +}; + +SerialKey parseSerialKey(const std::string &hexString); + +} // namespace synergy::license diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 0ed92f5ad..b6c77ff00 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -93,7 +93,7 @@ MSWindowsScreen *MSWindowsScreen::s_screen = NULL; MSWindowsScreen::MSWindowsScreen( bool isPrimary, bool noHooks, bool stopOnDeskSwitch, IEventQueue *events, - bool enableLangSync, lib::synergy::ClientScrollDirection scrollDirection) + bool enableLangSync, synergy::ClientScrollDirection scrollDirection) : PlatformScreen(events, scrollDirection), m_isPrimary(isPrimary), m_noHooks(noHooks), diff --git a/src/lib/platform/MSWindowsScreen.h b/src/lib/platform/MSWindowsScreen.h index 27889082c..0996151cb 100644 --- a/src/lib/platform/MSWindowsScreen.h +++ b/src/lib/platform/MSWindowsScreen.h @@ -44,8 +44,8 @@ public: MSWindowsScreen( bool isPrimary, bool noHooks, bool stopOnDeskSwitch, IEventQueue *events, bool enableLangSync = false, - lib::synergy::ClientScrollDirection scrollDirection = - lib::synergy::ClientScrollDirection::SERVER); + synergy::ClientScrollDirection scrollDirection = + synergy::ClientScrollDirection::SERVER); virtual ~MSWindowsScreen(); //! @name manipulators diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index 2b4e783db..dc5409e5e 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -23,11 +23,11 @@ #include "base/Log.h" #include "base/TMethodJob.h" #include "base/log_outputters.h" +#include "global/Ipc.h" #include "ipc/IpcLogOutputter.h" #include "ipc/IpcMessage.h" #include "ipc/IpcServer.h" #include "mt/Thread.h" -#include "shared/Ipc.h" #include "synergy/App.h" #include "synergy/ArgsBase.h" diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h index adde71eff..a93035386 100644 --- a/src/lib/platform/OSXScreen.h +++ b/src/lib/platform/OSXScreen.h @@ -56,8 +56,8 @@ class OSXScreen : public PlatformScreen { public: OSXScreen( IEventQueue *events, bool isPrimary, bool enableLangSync = false, - lib::synergy::ClientScrollDirection scrollDirection = - lib::synergy::ClientScrollDirection::SERVER); + synergy::ClientScrollDirection scrollDirection = + synergy::ClientScrollDirection::SERVER); virtual ~OSXScreen(); diff --git a/src/lib/platform/OSXScreen.mm b/src/lib/platform/OSXScreen.mm index a8a258d46..6cddd1835 100644 --- a/src/lib/platform/OSXScreen.mm +++ b/src/lib/platform/OSXScreen.mm @@ -86,7 +86,7 @@ bool OSXScreen::s_hasGHOM = false; OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool enableLangSync, - lib::synergy::ClientScrollDirection scrollDirection) : + synergy::ClientScrollDirection scrollDirection) : PlatformScreen(events, scrollDirection), m_isPrimary(isPrimary), m_isOnScreen(m_isPrimary), diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index 89392b31f..2ad5e31b4 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -98,7 +98,7 @@ XWindowsScreen *XWindowsScreen::s_screen = NULL; XWindowsScreen::XWindowsScreen( const char *displayName, bool isPrimary, bool disableXInitThreads, int mouseScrollDelta, IEventQueue *events, - lib::synergy::ClientScrollDirection scrollDirection) + synergy::ClientScrollDirection scrollDirection) : PlatformScreen(events, scrollDirection), m_isPrimary(isPrimary), m_mouseScrollDelta(mouseScrollDelta), diff --git a/src/lib/platform/XWindowsScreen.h b/src/lib/platform/XWindowsScreen.h index 1dae62c08..5053ce147 100644 --- a/src/lib/platform/XWindowsScreen.h +++ b/src/lib/platform/XWindowsScreen.h @@ -42,8 +42,8 @@ public: XWindowsScreen( const char *displayName, bool isPrimary, bool disableXInitThreads, int mouseScrollDelta, IEventQueue *events, - lib::synergy::ClientScrollDirection m_clientScrollDirection = - lib::synergy::ClientScrollDirection::SERVER); + synergy::ClientScrollDirection m_clientScrollDirection = + synergy::ClientScrollDirection::SERVER); virtual ~XWindowsScreen(); //! @name manipulators diff --git a/src/lib/server/CMakeLists.txt b/src/lib/server/CMakeLists.txt index f0adc6324..a17e5d9e2 100644 --- a/src/lib/server/CMakeLists.txt +++ b/src/lib/server/CMakeLists.txt @@ -23,7 +23,7 @@ endif() add_library(server STATIC ${sources}) -target_link_libraries(server shared) +target_link_libraries(server global license) if(UNIX) target_link_libraries(server synlib) diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 875db9822..c3057fcff 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -24,6 +24,7 @@ #include "base/TMethodEventJob.h" #include "base/TMethodJob.h" #include "common/stdexcept.h" +#include "license/License.h" #include "mt/Thread.h" #include "net/IDataSocket.h" #include "net/IListenSocket.h" @@ -33,7 +34,6 @@ #include "server/ClientProxy.h" #include "server/ClientProxyUnknown.h" #include "server/PrimaryClient.h" -#include "shared/SerialKey.h" #include "synergy/AppUtil.h" #include "synergy/DropHelper.h" #include "synergy/FileChunk.h" @@ -54,13 +54,15 @@ #include #include +using namespace synergy::license; + // // Server // Server::Server( Config &config, PrimaryClient *primaryClient, synergy::Screen *screen, - IEventQueue *events, lib::synergy::ServerArgs const &args) + IEventQueue *events, synergy::ServerArgs const &args) : m_mock(false), m_primaryClient(primaryClient), m_active(primaryClient), @@ -429,7 +431,8 @@ void Server::switchScreen( assert(dst != NULL); // if trial is expired, exit the process - if (m_args.m_serial.isExpired(std::time(0))) { + License license(m_args.m_serialKey); + if (license.isExpired()) { LOG((CLOG_ERR "trial has expired, aborting server")); exit(kExitSuccess); } diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index bb3d2ab37..d0beafcc2 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -111,7 +111,7 @@ public: */ Server( Config &config, PrimaryClient *primaryClient, synergy::Screen *screen, - IEventQueue *events, lib::synergy::ServerArgs const &args); + IEventQueue *events, synergy::ServerArgs const &args); Server(Server const &) = delete; Server(Server &&) = delete; ~Server(); @@ -490,5 +490,5 @@ private: bool m_waitDragInfoThread; ClientListener *m_clientListener; - lib::synergy::ServerArgs m_args; + synergy::ServerArgs m_args; }; diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp deleted file mode 100644 index 9017936ca..000000000 --- a/src/lib/shared/SerialKey.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "SerialKey.h" - -#include "SerialKeyEdition.h" -#include "SerialKeyParser.h" -#include -#include -#include - -SerialKey::SerialKey(Edition edition) : m_data(edition) {} - -SerialKey::SerialKey(const std::string &serial) : m_data(serial) { - SerialKeyParser parser; - - if (parser.parse(serial)) { - m_data = parser.getData(); - } else { - throw std::runtime_error("Invalid serial key"); - } -} - -bool SerialKey::isExpiring(time_t currentTime) const { - bool result = false; - - if (isTemporary()) { - unsigned long long currentTimeAsLL = - static_cast(currentTime); - if ((m_data.warnTime <= currentTimeAsLL) && - (currentTimeAsLL < m_data.expireTime)) { - result = true; - } - } - - return result; -} - -bool SerialKey::isExpired(time_t currentTime) const { - bool result = false; - - if (isTemporary()) { - unsigned long long currentTimeAsLL = - static_cast(currentTime); - if (m_data.expireTime <= currentTimeAsLL) { - result = true; - } - } - - return result; -} - -bool SerialKey::isTrial() const { return m_data.keyType.isTrial(); } - -bool SerialKey::isTemporary() const { - return (m_data.keyType.isTemporary() && !m_data.edition.isChina()); -} - -bool SerialKey::isMaintenance() const { - return (m_data.keyType.isMaintenance() || m_data.edition.isChina()); -} - -bool SerialKey::isValid() const { - bool Valid = true; - - if (!m_data.edition.isValid() || isExpired(::time(nullptr))) { - Valid = false; - } - - return Valid; -} - -Edition SerialKey::edition() const { return m_data.edition.getType(); } - -const std::string &SerialKey::toString() const { return m_data.key; } - -time_t SerialKey::daysLeft(time_t currentTime) const { - unsigned long long timeLeft = 0; - unsigned long long const day = 60 * 60 * 24; - - unsigned long long currentTimeAsLL = - static_cast(currentTime); - if (currentTimeAsLL < m_data.expireTime) { - timeLeft = m_data.expireTime - currentTimeAsLL; - } - - unsigned long long daysLeft = 0; - daysLeft = timeLeft % day != 0 ? 1 : 0; - - return timeLeft / day + daysLeft; -} - -time_t SerialKey::getExpiration() const { return m_data.expireTime; } - -int SerialKey::getSpanLeft(time_t time) const { - int result{-1}; - - if (isTemporary() && !isExpired(time)) { - auto timeLeft = (m_data.expireTime - time) * 1000; - - if (timeLeft < INT_MAX) { - result = static_cast(timeLeft); - } else { - result = INT_MAX; - } - } - - return result; -} diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h deleted file mode 100644 index f43d58cb5..000000000 --- a/src/lib/shared/SerialKey.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "SerialKeyData.h" -#include "SerialKeyEdition.h" -#include -#include - -class SerialKey { - friend bool operator==(SerialKey const &, SerialKey const &); - -public: - explicit SerialKey(Edition edition = kUnregistered); - explicit SerialKey(const std::string &serial); - - bool isExpiring(time_t currentTime) const; - bool isExpired(time_t currentTime) const; - bool isTrial() const; - bool isTemporary() const; - bool isMaintenance() const; - bool isValid() const; - time_t daysLeft(time_t currentTime) const; - time_t getExpiration() const; - int getSpanLeft(time_t time = ::time(0)) const; - Edition edition() const; - const std::string &toString() const; - -private: - SerialKeyData m_data; -}; - -inline bool operator==(SerialKey const &lhs, SerialKey const &rhs) { - return (lhs.m_data == rhs.m_data); -} - -inline bool operator!=(SerialKey const &lhs, SerialKey const &rhs) { - return !(lhs == rhs); -} diff --git a/src/lib/shared/SerialKeyData.h b/src/lib/shared/SerialKeyData.h deleted file mode 100644 index d40065972..000000000 --- a/src/lib/shared/SerialKeyData.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2022 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "SerialKeyEdition.h" -#include "SerialKeyType.h" -#include - -/** - * @brief The SerialKeyData struct - * This is DTO which stores key data - */ -struct SerialKeyData { - std::string key; // Encoded serial key as a string - SerialKeyEdition edition; // Serial key edition - SerialKeyType keyType; // Serial key type - unsigned long long warnTime = 0; // Warning time - unsigned long long expireTime = 0; // Expiration time - - /** - * @brief SerialKeyData constructor - * @param key encoded key - */ - explicit SerialKeyData(const std::string &key) : key(key) {} - - /** - * @brief SerialKeyData default constructor - * @param key edition. Unregistered by default - */ - explicit SerialKeyData(Edition edition = kUnregistered) : edition(edition) {} -}; - -inline bool operator==(SerialKeyData const &lhs, SerialKeyData const &rhs) { - return (lhs.key == rhs.key) && (lhs.warnTime == rhs.warnTime) && - (lhs.expireTime == rhs.expireTime) && (lhs.edition == rhs.edition) && - (lhs.keyType == rhs.keyType); -} diff --git a/src/lib/shared/SerialKeyEdition.cpp b/src/lib/shared/SerialKeyEdition.cpp deleted file mode 100644 index 4919dc41f..000000000 --- a/src/lib/shared/SerialKeyEdition.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include - -#include "SerialKeyEdition.h" - -const std::string SerialKeyEdition::Pro = "pro"; -const std::string SerialKeyEdition::ProChina = "pro_china"; -const std::string SerialKeyEdition::Basic = "basic"; -const std::string SerialKeyEdition::BasicChina = "basic_china"; -const std::string SerialKeyEdition::Buisiness = "business"; -const std::string SerialKeyEdition::Unregistered = "community"; -const std::string SerialKeyEdition::Ultimate = "ultimate"; -const std::string SerialKeyEdition::Lite = "lite"; - -const std::map> serialTypes{ - {SerialKeyEdition::Basic, kBasic}, - {SerialKeyEdition::Pro, kPro}, - {SerialKeyEdition::BasicChina, kBasicChina}, - {SerialKeyEdition::ProChina, kProChina}, - {SerialKeyEdition::Buisiness, kBusiness}, - {SerialKeyEdition::Lite, kLite}, - {SerialKeyEdition::Ultimate, kUltimate}}; - -SerialKeyEdition::SerialKeyEdition(Edition type) : m_type(type) {} - -SerialKeyEdition::SerialKeyEdition(const std::string &type) { setType(type); } - -Edition SerialKeyEdition::getType() const { return m_type; } - -std::string SerialKeyEdition::getName() const { - switch (getType()) { - case kLite: - return Lite; - - case kUltimate: - return Ultimate; - - case kPro: - return Pro; - - case kBasic: - return Basic; - - case kBusiness: - return Buisiness; - - case kBasicChina: - return BasicChina; - - case kProChina: - return ProChina; - - default: - return Unregistered; - } -} - -std::string SerialKeyEdition::getProductName() const { - const std::string nameBase = "Synergy 1"; - - switch (getType()) { - - case kLite: - return nameBase; - - case kUltimate: - return nameBase + " Ultimate"; - - case kPro: - return nameBase + " Pro"; - - case kBasic: - return nameBase + " Basic"; - - case kBusiness: - return nameBase + " Business"; - - case kBasicChina: - return nameBase + " 中文版"; - - case kProChina: - return nameBase + " Pro 中文版"; - - default: - return nameBase + " (Unregistered)"; - } -} - -void SerialKeyEdition::setType(Edition type) { - m_type = type; - setType(getName()); -} - -void SerialKeyEdition::setType(const std::string &name) { - const auto &pType = serialTypes.find(name); - - if (pType != serialTypes.end()) { - m_type = pType->second; - } else { - m_type = kUnregistered; - } -} - -bool SerialKeyEdition::isValid() const { - return serialTypes.contains(getName()); -} - -bool SerialKeyEdition::isChina() const { - return ((m_type == kBasicChina) || (m_type == kProChina)); -} diff --git a/src/lib/shared/SerialKeyEdition.h b/src/lib/shared/SerialKeyEdition.h deleted file mode 100644 index 703b1f614..000000000 --- a/src/lib/shared/SerialKeyEdition.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "EditionType.h" -#include - -class SerialKeyEdition { - friend bool - operator==(SerialKeyEdition const &, SerialKeyEdition const &) = default; - -public: - SerialKeyEdition() = default; - explicit SerialKeyEdition(Edition type); - explicit SerialKeyEdition(const std::string &name); - - Edition getType() const; - std::string getName() const; - std::string getProductName() const; - - void setType(Edition type); - void setType(const std::string &type); - - bool isValid() const; - bool isChina() const; - - static const std::string Pro; - static const std::string ProChina; - static const std::string Basic; - static const std::string BasicChina; - static const std::string Buisiness; - static const std::string Unregistered; - static const std::string Ultimate; - static const std::string Lite; - -private: - Edition m_type = kUnregistered; -}; diff --git a/src/lib/shared/SerialKeyParser.cpp b/src/lib/shared/SerialKeyParser.cpp deleted file mode 100644 index c94dcf145..000000000 --- a/src/lib/shared/SerialKeyParser.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2022 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "SerialKeyParser.h" - -void SerialKeyParser::setKey(const std::string &key) { m_data.key = key; } - -void SerialKeyParser::setType(const std::string &type) { - m_data.keyType.setKeyType(type); -} - -void SerialKeyParser::setEdition(const std::string &edition) { - m_data.edition.setType(edition); - if (m_data.keyType.isMaintenance() && - m_data.edition.getType() == Edition::kBasic) { - m_data.edition.setType(kLite); - } else if ( - m_data.keyType.isMaintenance() && - m_data.edition.getType() == Edition::kPro) { - m_data.edition.setType(kUltimate); - } -} - -void SerialKeyParser::setWarningTime(const std::string &warnTime) { - sscanf(warnTime.c_str(), "%lld", &m_data.warnTime); -} - -void SerialKeyParser::setExpirationTime(const std::string &expTime) { - sscanf(expTime.c_str(), "%lld", &m_data.expireTime); -} - -std::string SerialKeyParser::decode(const std::string &serial) const { - static const char *const lut = "0123456789ABCDEF"; - std::string output; - size_t len = serial.length(); - if (len & 1) { - return output; - } - - output.reserve(len / 2); - for (size_t i = 0; i < len; i += 2) { - - char a = serial[i]; - char b = serial[i + 1]; - - const auto p = std::lower_bound(lut, lut + 16, a); - const auto q = std::lower_bound(lut, lut + 16, b); - - if (*q != b || *p != a) { - return output; - } - - output.push_back(static_cast(((p - lut) << 4) | (q - lut))); - } - - return output; -} - -bool SerialKeyParser::parse(const std::string &plainSerial) { - bool valid = false; - auto key = decode(plainSerial); - const auto parts = splitToParts(key); - - if ((parts.size() == 8) && (parts.at(0).find("v1") != std::string::npos)) { - setKey(plainSerial); - parseV1(parts); - valid = true; - } else if ( - (parts.size() == 9) && (parts.at(0).find("v2") != std::string::npos)) { - setKey(plainSerial); - parseV2(parts); - valid = true; - } - - return valid; -} - -const SerialKeyData &SerialKeyParser::getData() const { return m_data; } - -void SerialKeyParser::parseV1(const std::vector &parts) { - // e.g.: {v1;basic;Bob;1;email;company name;1398297600;1398384000} - setEdition(parts.at(1)); - setWarningTime(parts.at(6)); - setExpirationTime(parts.at(7)); -} - -void SerialKeyParser::parseV2(const std::vector &parts) { - // e.g.: {v2;trial;basic;Bob;1;email;company name;1398297600;1398384000} - setType(parts.at(1)); - setEdition(parts.at(2)); - setWarningTime(parts.at(7)); - setExpirationTime(parts.at(8)); -} - -std::vector -SerialKeyParser::splitToParts(const std::string &plainSerial) const { - // tokenize serialised subscription. - std::vector parts; - - if (!plainSerial.empty()) { - std::string parityStart = plainSerial.substr(0, 1); - std::string parityEnd = plainSerial.substr(plainSerial.length() - 1, 1); - - // check for parity chars { and }, record parity result, then remove them. - if (parityStart == "{" && parityEnd == "}") { - const auto serialData = plainSerial.substr(1, plainSerial.length() - 2); - - std::string::size_type pos = 0; - bool look = true; - while (look) { - std::string::size_type start = pos; - pos = serialData.find(";", pos); - if (pos == std::string::npos) { - pos = serialData.length(); - look = false; - } - parts.push_back(serialData.substr(start, pos - start)); - pos += 1; - } - } - } - - return parts; -} diff --git a/src/lib/shared/SerialKeyParser.h b/src/lib/shared/SerialKeyParser.h deleted file mode 100644 index 0f1c033f1..000000000 --- a/src/lib/shared/SerialKeyParser.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2022 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#pragma once - -#include "SerialKeyData.h" -#include -#include - -class SerialKeyParser { -public: - /** - * @brief ~SerialKeyParser destructor - */ - virtual ~SerialKeyParser() {} - /** - * @brief parse serial key - * @param plainSerial encoded serial key - * @return true if parsed - */ - virtual bool parse(const std::string &plainSerial); - - /** - * @brief getData getter for serial key data - * @return serial key data as DTO - */ - const SerialKeyData &getData() const; - - /** - * @brief setKey setter to set encoded key - * @param key encoded serial key - */ - void setKey(const std::string &key); - - /** - * @brief setType setter for serial type - * @param type serial key type - */ - void setType(const std::string &type); - - /** - * @brief setEdition setter for serial edition - * @param edition serial key edition - */ - void setEdition(const std::string &edition); - - /** - * @brief setWarningTime setter for warning time - * @param warnTime warning time for key as a string - */ - void setWarningTime(const std::string &warnTime); - - /** - * @brief setExpirationTime setter for expiration date - * @param expTime expiration data as a string - */ - void setExpirationTime(const std::string &expTime); - -private: - /** - * @brief decode serial key from encoded string - * @param encoded serial key as a string - * @return decoded serial key as a string - */ - std::string decode(const std::string &serial) const; - - /** - * @brief parseV1 parse serial key version 1 - * @param parts of serial key version 1 - */ - void parseV1(const std::vector &parts); - - /** - * @brief parseV2 parse serial key version 2 - * @param parts parts of serial key version 2 - */ - void parseV2(const std::vector &parts); - - /** - * @brief splitToParts splits decoded serial key to parts - * @param plainSerial decoded serial key as a string - * @return splitted serial key - */ - std::vector splitToParts(const std::string &plainSerial) const; - - SerialKeyData m_data{}; // serial key data -}; diff --git a/src/lib/synergy/App.cpp b/src/lib/synergy/App.cpp index efc904e6b..c8df2dd17 100644 --- a/src/lib/synergy/App.cpp +++ b/src/lib/synergy/App.cpp @@ -27,9 +27,9 @@ #include "base/XBase.h" #include "base/log_outputters.h" #include "common/Version.h" +#include "global/Ipc.h" #include "ipc/IpcMessage.h" #include "ipc/IpcServerProxy.h" -#include "shared/Ipc.h" #include "synergy/ArgsBase.h" #include "synergy/XSynergy.h" #include "synergy/protocol_types.h" @@ -60,7 +60,7 @@ App *App::s_instance = nullptr; App::App( IEventQueue *events, CreateTaskBarReceiverFunc createTaskBarReceiver, - lib::synergy::ArgsBase *args) + synergy::ArgsBase *args) : m_bye(&exit), m_taskBarReceiver(NULL), m_suspended(false), @@ -250,7 +250,7 @@ void App::runEventsLoop(void *) { // MinimalApp // -MinimalApp::MinimalApp() : App(NULL, NULL, new lib::synergy::ArgsBase()) { +MinimalApp::MinimalApp() : App(NULL, NULL, new synergy::ArgsBase()) { m_arch.init(); setEvents(m_events); } diff --git a/src/lib/synergy/App.h b/src/lib/synergy/App.h index 31493054e..d7a4b4d22 100644 --- a/src/lib/synergy/App.h +++ b/src/lib/synergy/App.h @@ -47,7 +47,7 @@ typedef IArchTaskBarReceiver *(*CreateTaskBarReceiverFunc)( class App : public IApp { public: App(IEventQueue *events, CreateTaskBarReceiverFunc createTaskBarReceiver, - lib::synergy::ArgsBase *args); + synergy::ArgsBase *args); App(App const &) = delete; App(App &&) = delete; virtual ~App(); @@ -56,7 +56,7 @@ public: App &operator=(App &&) = delete; // Returns args that are common between server and client. - lib::synergy::ArgsBase &argsBase() const { return *m_args; } + synergy::ArgsBase &argsBase() const { return *m_args; } // Prints the current compiled version. virtual void version(); @@ -129,7 +129,7 @@ protected: IEventQueue *m_events; private: - lib::synergy::ArgsBase *m_args; + synergy::ArgsBase *m_args; static App *s_instance; FileLogOutputter *m_fileLog; CreateTaskBarReceiverFunc m_createTaskBarReceiver; diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 0ad625d3e..116e55099 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -19,23 +19,25 @@ #include "base/Log.h" #include "base/String.h" +#include "license/parse_serial_key.h" #include "synergy/App.h" #include "synergy/ArgsBase.h" #include "synergy/ClientArgs.h" #include "synergy/ServerArgs.h" -#include "synergy/StreamChunker.h" #include "synergy/ToolArgs.h" #ifdef WINAPI_MSWINDOWS #include #endif -lib::synergy::ArgsBase *ArgParser::m_argsBase = NULL; +using namespace synergy::license; + +synergy::ArgsBase *ArgParser::m_argsBase = nullptr; ArgParser::ArgParser(App *app) : m_app(app) {} bool ArgParser::parseServerArgs( - lib::synergy::ServerArgs &args, int argc, const char *const *argv) { + synergy::ServerArgs &args, int argc, const char *const *argv) { setArgsBase(args); updateCommonArgs(argv); int i = 1; @@ -53,7 +55,7 @@ bool ArgParser::parseServerArgs( // save configuration file path args.m_configFile = argv[++i]; } else if (isArg(i, argc, argv, "", "--serial-key", 1)) { - args.m_serial = SerialKey(argv[++i]); + args.m_serialKey = parseSerialKey(argv[++i]); } else if (isArg(i, argc, argv, nullptr, "server")) { ++i; continue; @@ -74,7 +76,7 @@ bool ArgParser::parseServerArgs( } bool ArgParser::parseClientArgs( - lib::synergy::ClientArgs &args, int argc, const char *const *argv) { + synergy::ClientArgs &args, int argc, const char *const *argv) { setArgsBase(args); updateCommonArgs(argv); @@ -100,7 +102,7 @@ bool ArgParser::parseClientArgs( args.m_enableLangSync = true; } else if (isArg(i, argc, argv, nullptr, "--invert-scroll")) { args.m_clientScrollDirection = - lib::synergy::ClientScrollDirection::INVERT_SERVER; + synergy::ClientScrollDirection::INVERT_SERVER; } else if (isArg(i, argc, argv, nullptr, "--host")) { args.m_hostMode = true; } else if (isArg(i, argc, argv, nullptr, "client")) { @@ -136,7 +138,7 @@ bool ArgParser::parseClientArgs( } bool ArgParser::parsePlatformArg( - lib::synergy::ArgsBase &argsBase, const int &argc, const char *const *argv, + synergy::ArgsBase &argsBase, const int &argc, const char *const *argv, int &i) { #if WINAPI_MSWINDOWS if (isArg(i, argc, argv, nullptr, "--service")) { diff --git a/src/lib/synergy/ArgParser.h b/src/lib/synergy/ArgParser.h index f73e49a58..a6bd34613 100644 --- a/src/lib/synergy/ArgParser.h +++ b/src/lib/synergy/ArgParser.h @@ -20,13 +20,11 @@ #include "base/String.h" #include "common/stdvector.h" -namespace lib { namespace synergy { class ArgsBase; class ServerArgs; class ClientArgs; } // namespace synergy -} // namespace lib class ToolArgs; class App; @@ -36,17 +34,17 @@ class ArgParser { public: ArgParser(App *app); - bool parseServerArgs( - lib::synergy::ServerArgs &args, int argc, const char *const *argv); - bool parseClientArgs( - lib::synergy::ClientArgs &args, int argc, const char *const *argv); + bool + parseServerArgs(synergy::ServerArgs &args, int argc, const char *const *argv); + bool + parseClientArgs(synergy::ClientArgs &args, int argc, const char *const *argv); bool parsePlatformArg( - lib::synergy::ArgsBase &argsBase, const int &argc, - const char *const *argv, int &i); + synergy::ArgsBase &argsBase, const int &argc, const char *const *argv, + int &i); bool parseToolArgs(ToolArgs &args, int argc, const char *const *argv); bool parseGenericArgs(int argc, const char *const *argv, int &i); bool parseDeprecatedArgs(int argc, const char *const *argv, int &i); - void setArgsBase(lib::synergy::ArgsBase &argsBase) { m_argsBase = &argsBase; } + void setArgsBase(synergy::ArgsBase &argsBase) { m_argsBase = &argsBase; } static bool isArg( int argi, int argc, const char *const *argv, const char *name1, @@ -60,7 +58,7 @@ public: std::vector &argsArray, String ignoreArg = "", int parametersRequired = 0); - static lib::synergy::ArgsBase &argsBase() { return *m_argsBase; } + static synergy::ArgsBase &argsBase() { return *m_argsBase; } private: void updateCommonArgs(const char *const *argv); @@ -69,5 +67,5 @@ private: private: App *m_app; - static lib::synergy::ArgsBase *m_argsBase; + static synergy::ArgsBase *m_argsBase; }; diff --git a/src/lib/synergy/ArgsBase.cpp b/src/lib/synergy/ArgsBase.cpp index f7b055577..240d057af 100644 --- a/src/lib/synergy/ArgsBase.cpp +++ b/src/lib/synergy/ArgsBase.cpp @@ -18,10 +18,8 @@ #include "ArgsBase.h" -namespace lib { namespace synergy { ArgsBase::~ArgsBase() { // } } // namespace synergy -} // namespace lib \ No newline at end of file diff --git a/src/lib/synergy/ArgsBase.h b/src/lib/synergy/ArgsBase.h index 026661773..fbdccca31 100644 --- a/src/lib/synergy/ArgsBase.h +++ b/src/lib/synergy/ArgsBase.h @@ -21,7 +21,6 @@ #include "base/String.h" -namespace lib { namespace synergy { /** * @brief This is the base Argument class that will store the generic @@ -113,6 +112,5 @@ protected: } }; } // namespace synergy -} // namespace lib #endif // SYNERGY_CORE_ARGSBASE_H diff --git a/src/lib/synergy/ClientApp.cpp b/src/lib/synergy/ClientApp.cpp index ce9c7ab0e..8d64cc909 100644 --- a/src/lib/synergy/ClientApp.cpp +++ b/src/lib/synergy/ClientApp.cpp @@ -66,7 +66,7 @@ ClientApp::ClientApp( IEventQueue *events, CreateTaskBarReceiverFunc createTaskBarReceiver) - : App(events, createTaskBarReceiver, new lib::synergy::ClientArgs()), + : App(events, createTaskBarReceiver, new synergy::ClientArgs()), m_client(NULL), m_clientScreen(NULL), m_serverAddress(NULL) {} diff --git a/src/lib/synergy/ClientApp.h b/src/lib/synergy/ClientApp.h index b987d77de..8a9293717 100644 --- a/src/lib/synergy/ClientApp.h +++ b/src/lib/synergy/ClientApp.h @@ -28,11 +28,10 @@ class Client; class NetworkAddress; class Thread; class ISocketFactory; -namespace lib { + namespace synergy { class ClientArgs; } -} // namespace lib class ClientApp : public App { public: @@ -47,8 +46,8 @@ public: void help(); // Returns arguments that are common and for client. - lib::synergy::ClientArgs &args() const { - return (lib::synergy::ClientArgs &)argsBase(); + synergy::ClientArgs &args() const { + return (synergy::ClientArgs &)argsBase(); } const char *daemonName() const; diff --git a/src/lib/synergy/ClientArgs.cpp b/src/lib/synergy/ClientArgs.cpp index 71b28bed7..fc079eda9 100644 --- a/src/lib/synergy/ClientArgs.cpp +++ b/src/lib/synergy/ClientArgs.cpp @@ -17,11 +17,9 @@ #include "ClientArgs.h" -namespace lib { namespace synergy { ClientArgs::~ClientArgs() {} ClientArgs::ClientArgs() { m_classType = kClient; } } // namespace synergy -} // namespace lib \ No newline at end of file diff --git a/src/lib/synergy/ClientArgs.h b/src/lib/synergy/ClientArgs.h index 5c8fe8750..23e82fe9f 100644 --- a/src/lib/synergy/ClientArgs.h +++ b/src/lib/synergy/ClientArgs.h @@ -20,7 +20,6 @@ #include "ArgsBase.h" -namespace lib { namespace synergy { enum ClientScrollDirection { SERVER = 1, INVERT_SERVER = -1 }; @@ -55,5 +54,5 @@ public: std::string m_serverAddress; }; } // namespace synergy -} // namespace lib + #endif // SYNERGY_CORE_CLIENTARGS_H diff --git a/src/lib/synergy/DaemonApp.cpp b/src/lib/synergy/DaemonApp.cpp index 0c73186e1..a30e813c7 100644 --- a/src/lib/synergy/DaemonApp.cpp +++ b/src/lib/synergy/DaemonApp.cpp @@ -25,12 +25,12 @@ #include "base/Log.h" #include "base/TMethodEventJob.h" #include "base/log_outputters.h" +#include "global/Ipc.h" #include "ipc/IpcClientProxy.h" #include "ipc/IpcLogOutputter.h" #include "ipc/IpcMessage.h" #include "ipc/IpcSettingMessage.h" #include "net/SocketMultiplexer.h" -#include "shared/Ipc.h" #include "synergy/App.h" #include "synergy/ArgParser.h" #include "synergy/ClientArgs.h" @@ -307,10 +307,10 @@ void DaemonApp::handleIpcMessage(const Event &e, void *) { int argc = static_cast(argsArray.size()); if (isServerCommandLine(argsArray)) { - auto serverArgs = new lib::synergy::ServerArgs(); + auto serverArgs = new synergy::ServerArgs(); argParser.parseServerArgs(*serverArgs, argc, argv); } else { - auto clientArgs = new lib::synergy::ClientArgs(); + auto clientArgs = new synergy::ClientArgs(); argParser.parseClientArgs(*clientArgs, argc, argv); } diff --git a/src/lib/synergy/IApp.h b/src/lib/synergy/IApp.h index 520b2e9db..917df352f 100644 --- a/src/lib/synergy/IApp.h +++ b/src/lib/synergy/IApp.h @@ -22,11 +22,9 @@ typedef int (*StartupFunc)(int, char **); -namespace lib { namespace synergy { class ArgsBase; } -} // namespace lib class ILogOutputter; class IArchTaskBarReceiver; @@ -38,7 +36,7 @@ class IEventQueue; class IApp : public IInterface { public: virtual void setByeFunc(void (*bye)(int)) = 0; - virtual lib::synergy::ArgsBase &argsBase() const = 0; + virtual synergy::ArgsBase &argsBase() const = 0; virtual int standardStartup(int argc, char **argv) = 0; virtual int runInner( int argc, char **argv, ILogOutputter *outputter, StartupFunc startup) = 0; diff --git a/src/lib/synergy/PlatformScreen.cpp b/src/lib/synergy/PlatformScreen.cpp index dcc1e5730..804bfd535 100644 --- a/src/lib/synergy/PlatformScreen.cpp +++ b/src/lib/synergy/PlatformScreen.cpp @@ -21,7 +21,7 @@ #include "synergy/ArgsBase.h" PlatformScreen::PlatformScreen( - IEventQueue *events, lib::synergy::ClientScrollDirection scrollDirection) + IEventQueue *events, synergy::ClientScrollDirection scrollDirection) : IPlatformScreen(events), m_draggingStarted(false), m_fakeDraggingStarted(false), diff --git a/src/lib/synergy/PlatformScreen.h b/src/lib/synergy/PlatformScreen.h index b2abf6006..60be8a2dc 100644 --- a/src/lib/synergy/PlatformScreen.h +++ b/src/lib/synergy/PlatformScreen.h @@ -32,8 +32,8 @@ subclasses to implement the rest. class PlatformScreen : public IPlatformScreen { public: PlatformScreen( - IEventQueue *events, lib::synergy::ClientScrollDirection scrollDirection = - lib::synergy::ClientScrollDirection::SERVER); + IEventQueue *events, synergy::ClientScrollDirection scrollDirection = + synergy::ClientScrollDirection::SERVER); virtual ~PlatformScreen(); // IScreen overrides @@ -142,6 +142,6 @@ private: * This member contains client scroll direction. * This member is used only on client side. */ - lib::synergy::ClientScrollDirection m_clientScrollDirection = - lib::synergy::ClientScrollDirection::SERVER; + synergy::ClientScrollDirection m_clientScrollDirection = + synergy::ClientScrollDirection::SERVER; }; diff --git a/src/lib/synergy/ServerApp.cpp b/src/lib/synergy/ServerApp.cpp index 8c4e3f455..6eec869bf 100644 --- a/src/lib/synergy/ServerApp.cpp +++ b/src/lib/synergy/ServerApp.cpp @@ -68,7 +68,7 @@ ServerApp::ServerApp( IEventQueue *events, CreateTaskBarReceiverFunc createTaskBarReceiver) - : App(events, createTaskBarReceiver, new lib::synergy::ServerArgs()), + : App(events, createTaskBarReceiver, new synergy::ServerArgs()), m_server(NULL), m_serverState(kUninitialized), m_serverScreen(NULL), diff --git a/src/lib/synergy/ServerApp.h b/src/lib/synergy/ServerApp.h index 8162610bb..3fcd750df 100644 --- a/src/lib/synergy/ServerApp.h +++ b/src/lib/synergy/ServerApp.h @@ -47,11 +47,10 @@ class EventQueueTimer; class ILogOutputter; class IEventQueue; class ISocketFactory; -namespace lib { + namespace synergy { class ServerArgs; } -} // namespace lib class ServerApp : public App { public: @@ -66,8 +65,8 @@ public: void help(); // Returns arguments that are common and for server. - lib::synergy::ServerArgs &args() const { - return (lib::synergy::ServerArgs &)argsBase(); + synergy::ServerArgs &args() const { + return (synergy::ServerArgs &)argsBase(); } const char *daemonName() const; diff --git a/src/lib/synergy/ServerArgs.cpp b/src/lib/synergy/ServerArgs.cpp index 69c9400f4..71936a40d 100644 --- a/src/lib/synergy/ServerArgs.cpp +++ b/src/lib/synergy/ServerArgs.cpp @@ -17,11 +17,9 @@ #include "ServerArgs.h" -namespace lib { namespace synergy { ServerArgs::~ServerArgs() {} ServerArgs::ServerArgs() { m_classType = kServer; } } // namespace synergy -} // namespace lib diff --git a/src/lib/synergy/ServerArgs.h b/src/lib/synergy/ServerArgs.h index cc3526c77..5438ea34c 100644 --- a/src/lib/synergy/ServerArgs.h +++ b/src/lib/synergy/ServerArgs.h @@ -15,18 +15,19 @@ * along with this program. If not, see . */ -#ifndef SYNERGY_CORE_SERVERARGS_H -#define SYNERGY_CORE_SERVERARGS_H +#pragma once #include "ArgsBase.h" +#include "license/License.h" #include "server/Config.h" -#include "shared/SerialKey.h" + #include -namespace lib { namespace synergy { + class ServerArgs : public ArgsBase { - /// Public Functions + using License = synergy::license::License; + public: ServerArgs(); ServerArgs(ServerArgs const &src) = default; @@ -36,16 +37,10 @@ public: ServerArgs &operator=(ServerArgs const &) = default; ServerArgs &operator=(ServerArgs &&) = default; - /// Public variables public: - String m_configFile = ""; /// @brief Contains the path to the config file - SerialKey m_serial; /// @brief Contains the serial number and license info - std::shared_ptr - m_config; /// @brief Contains the Parsed Configuration settings - - /// Private Functions -private: + String m_configFile = ""; + license::SerialKey m_serialKey = license::SerialKey::invalid(); + std::shared_ptr m_config; }; + } // namespace synergy -} // namespace lib -#endif // SYNERGY_CORE_SERVERARGS_H \ No newline at end of file diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 5af72edf8..d6d9ddea4 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -23,7 +23,6 @@ #include "synergy/ArgParser.h" #include -#include #if SYSAPI_WIN32 #include "platform/MSWindowsSession.h" diff --git a/src/lib/synergy/ToolApp.h b/src/lib/synergy/ToolApp.h index 693cdaeb2..f38b556ec 100644 --- a/src/lib/synergy/ToolApp.h +++ b/src/lib/synergy/ToolApp.h @@ -26,11 +26,6 @@ public: UInt32 run(int argc, char **argv); void help(); -private: - void loginAuth(); - void notifyActivation(); - void notifyUpdate(); - private: ToolArgs m_args; }; diff --git a/src/lib/synergy/win32/AppUtilWindows.cpp b/src/lib/synergy/win32/AppUtilWindows.cpp index e209bce28..442940646 100644 --- a/src/lib/synergy/win32/AppUtilWindows.cpp +++ b/src/lib/synergy/win32/AppUtilWindows.cpp @@ -109,7 +109,8 @@ void AppUtilWindows::beforeAppExit() { int AppUtilWindows::run(int argc, char **argv) { if (!IsWindowsXPSP3OrGreater()) { - throw std::runtime_error("Synergy only supports Windows XP SP3 and above."); + throw std::runtime_error( + "unsupported os version, xp sp3 or greater required"); } // record window instance for tray icon, etc diff --git a/src/gui/src/ActivationNotifier.cpp b/src/lib/utils/trim.h similarity index 50% rename from src/gui/src/ActivationNotifier.cpp rename to src/lib/utils/trim.h index ede3910fb..fa63153b2 100644 --- a/src/gui/src/ActivationNotifier.cpp +++ b/src/lib/utils/trim.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Ltd. + * Copyright (C) 2024 Symless Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -15,28 +15,25 @@ * along with this program. If not, see . */ -#include "ActivationNotifier.h" +#pragma once -#include "CoreInterface.h" +#include +#include -ActivationNotifier::ActivationNotifier(QObject *parent) : QObject(parent) {} +const auto isNotSpace = [](unsigned char ch) { return !std::isspace(ch); }; -void ActivationNotifier::setIdentity(QString identity) { - m_Identity = identity; +namespace synergy::utils { + +/** + * @brief A _very_ basic whitespace trimer. + * + * Ideally C++20 would have a std::trim function, but until then, this will do. + */ +inline std::string trim(const std::string &str) { + auto front = std::ranges::find_if(str, isNotSpace); + auto back = + std::ranges::find_if(str | std::views::reverse, isNotSpace).base(); + return (front < back ? std::string(front, back) : std::string{}); } -void ActivationNotifier::setUpdateInfo( - QString const &fromVersion, QString const &toVersion, - QString const &serialKey) { - m_fromVersion = fromVersion; - m_toVersion = toVersion; - m_serialKey = serialKey; -} - -void ActivationNotifier::notify() { - // TODO: use something other than CURL -} - -void ActivationNotifier::notifyUpdate() { - // TODO: use something other than CURL -} +} // namespace synergy::utils diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index b6220b400..3f528e18f 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -101,16 +101,6 @@ endmacro() macro(config_test_deps) - set(CMAKE_AUTOMOC ON) - set(CMAKE_AUTORCC ON) - set(CMAKE_AUTOUIC ON) - set(CMAKE_INCLUDE_CURRENT_DIR ON) - - find_package( - Qt6 - COMPONENTS Core Widgets Network Test - REQUIRED) - set(gtest_dir ${gtest_base_dir}/googletest) set(gmock_dir ${gtest_base_dir}/googlemock) @@ -127,6 +117,9 @@ macro(config_test_deps) endif() set(test_libs + gtest + gmock + global arch base client @@ -139,9 +132,7 @@ macro(config_test_deps) synlib mt ipc - gtest - gmock - shared + license gui ${libs}) diff --git a/src/test/integtests/gui/CommandProcessTests.cpp b/src/test/integtests/gui/CommandProcessTests.cpp new file mode 100644 index 000000000..edfe95346 --- /dev/null +++ b/src/test/integtests/gui/CommandProcessTests.cpp @@ -0,0 +1,32 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gui/CommandProcess.h" + +#include + +TEST(CommandProcessTests, run_commandSucceeds_returnsOutput) { + const QString command = "echo"; + const QStringList arguments = {"Hello, World!"}; + const QString input = ""; + + CommandProcess commandProcess(command, arguments, input); + + const QString actual = commandProcess.run(); + + EXPECT_EQ("Hello, World!", actual.toStdString()); +} diff --git a/src/test/integtests/gui/MainWindowTests.cpp b/src/test/integtests/gui/MainWindowTests.cpp index 1d5aee6ed..c531b7a87 100644 --- a/src/test/integtests/gui/MainWindowTests.cpp +++ b/src/test/integtests/gui/MainWindowTests.cpp @@ -39,13 +39,7 @@ class TestMainWindow { public: class MainWindowProxy : public MainWindow { public: -#ifdef SYNERGY_ENABLE_LICENSING - explicit MainWindowProxy( - AppConfig &appConfig, LicenseManager &licenseManager) - : MainWindow(appConfig, licenseManager) {} -#else explicit MainWindowProxy(AppConfig &appConfig) : MainWindow(appConfig) {} -#endif bool _checkSecureSocket(const char *test) { return MainWindow::checkSecureSocket(test); @@ -53,17 +47,10 @@ public: }; TestMainWindow() { -#ifdef SYNERGY_ENABLE_LICENSING - m_licenseManager = std::make_shared(&m_appConfig); - m_mainWindow = - std::make_shared(m_appConfig, *m_licenseManager); -#else m_mainWindow = std::make_shared(m_appConfig); -#endif } AppConfig m_appConfig; - std::shared_ptr m_licenseManager; std::shared_ptr m_mainWindow; }; diff --git a/src/test/integtests/gui/dotenv_tests.cpp b/src/test/integtests/gui/dotenv_tests.cpp new file mode 100644 index 000000000..fd70854ed --- /dev/null +++ b/src/test/integtests/gui/dotenv_tests.cpp @@ -0,0 +1,49 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gui/dotenv.h" + +#include +#include + +#include + +TEST(dotenv_tests, dotenv_envFileWithEntry_loadsEnvVar) { + // Arrange + const QString envFile = "tmp/test/.env"; + QFile file(envFile); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + FAIL() << "Failed to create: " << envFile.toStdString(); + } + + const QString key = "TEST_ENV_VAR"; + const QString value = R"("test value")"; + const QString entry = key + " = " + value; + + QTextStream out(&file); + out << " # Comment" << Qt::endl; + out << "FOOBAR" << Qt::endl; + out << entry << Qt::endl; + file.close(); + + synergy::gui::dotenv(envFile); + + const QString actualValue = qgetenv(key.toUtf8().constData()); + EXPECT_EQ("test value", actualValue.toStdString()); + + QFile::remove(envFile); +} diff --git a/src/test/integtests/ipc/IpcTests.cpp b/src/test/integtests/ipc/IpcTests.cpp index 679e2366e..10b4c71ac 100644 --- a/src/test/integtests/ipc/IpcTests.cpp +++ b/src/test/integtests/ipc/IpcTests.cpp @@ -27,6 +27,7 @@ #include "base/String.h" #include "base/TMethodEventJob.h" #include "base/TMethodJob.h" +#include "global/Ipc.h" #include "ipc/IpcClient.h" #include "ipc/IpcClientProxy.h" #include "ipc/IpcMessage.h" @@ -34,7 +35,6 @@ #include "ipc/IpcServerProxy.h" #include "mt/Thread.h" #include "net/SocketMultiplexer.h" -#include "shared/Ipc.h" #include "test/shared/TestEventQueue.h" #include diff --git a/src/test/integtests/net/NetworkTests.cpp b/src/test/integtests/net/NetworkTests.cpp index 8acdcb204..4e6ff6ae5 100644 --- a/src/test/integtests/net/NetworkTests.cpp +++ b/src/test/integtests/net/NetworkTests.cpp @@ -128,7 +128,7 @@ TEST_F(NetworkTests, sendToClient_mockData) { ON_CALL(serverConfig, getInputFilter()) .WillByDefault(Return(&serverInputFilter)); - lib::synergy::ServerArgs serverArgs; + synergy::ServerArgs serverArgs; serverArgs.m_enableDragDrop = true; Server server( serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs); @@ -145,7 +145,7 @@ TEST_F(NetworkTests, sendToClient_mockData) { .WillByDefault(Invoke(getScreenShape)); ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); - lib::synergy::ClientArgs clientArgs; + synergy::ClientArgs clientArgs; clientArgs.m_enableDragDrop = true; clientArgs.m_enableCrypto = false; Client client( @@ -192,7 +192,7 @@ TEST_F(NetworkTests, sendToClient_mockFile) { ON_CALL(serverConfig, getInputFilter()) .WillByDefault(Return(&serverInputFilter)); - lib::synergy::ServerArgs serverArgs; + synergy::ServerArgs serverArgs; serverArgs.m_enableDragDrop = true; Server server( serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs); @@ -209,7 +209,7 @@ TEST_F(NetworkTests, sendToClient_mockFile) { .WillByDefault(Invoke(getScreenShape)); ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); - lib::synergy::ClientArgs clientArgs; + synergy::ClientArgs clientArgs; clientArgs.m_enableDragDrop = true; clientArgs.m_enableCrypto = false; Client client( @@ -249,7 +249,7 @@ TEST_F(NetworkTests, sendToServer_mockData) { ON_CALL(serverConfig, getInputFilter()) .WillByDefault(Return(&serverInputFilter)); - lib::synergy::ServerArgs serverArgs; + synergy::ServerArgs serverArgs; serverArgs.m_enableDragDrop = true; Server server( serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs); @@ -266,7 +266,7 @@ TEST_F(NetworkTests, sendToServer_mockData) { .WillByDefault(Invoke(getScreenShape)); ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); - lib::synergy::ClientArgs clientArgs; + synergy::ClientArgs clientArgs; clientArgs.m_enableDragDrop = true; clientArgs.m_enableCrypto = false; Client client( @@ -313,7 +313,7 @@ TEST_F(NetworkTests, sendToServer_mockFile) { ON_CALL(serverConfig, getInputFilter()) .WillByDefault(Return(&serverInputFilter)); - lib::synergy::ServerArgs serverArgs; + synergy::ServerArgs serverArgs; serverArgs.m_enableDragDrop = true; Server server( serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs); @@ -330,7 +330,7 @@ TEST_F(NetworkTests, sendToServer_mockFile) { .WillByDefault(Invoke(getScreenShape)); ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); - lib::synergy::ClientArgs clientArgs; + synergy::ClientArgs clientArgs; clientArgs.m_enableDragDrop = true; clientArgs.m_enableCrypto = false; Client client( diff --git a/src/test/shared/gui/QtCoreTest.h b/src/test/shared/gui/QtCoreTest.h index 1f18d6be2..be17bef16 100644 --- a/src/test/shared/gui/QtCoreTest.h +++ b/src/test/shared/gui/QtCoreTest.h @@ -17,8 +17,8 @@ #pragma once +#include #include -#include class QtCoreTest : public ::testing::Test { public: diff --git a/src/test/shared/gui/QtTest.h b/src/test/shared/gui/QtTest.h index b8906dc1d..717efd16a 100644 --- a/src/test/shared/gui/QtTest.h +++ b/src/test/shared/gui/QtTest.h @@ -17,8 +17,8 @@ #pragma once +#include #include -#include class QtTest : public ::testing::Test { public: diff --git a/src/test/unittests/gui/LicenseHandlerTests.cpp b/src/test/unittests/gui/LicenseHandlerTests.cpp new file mode 100644 index 000000000..9abf35229 --- /dev/null +++ b/src/test/unittests/gui/LicenseHandlerTests.cpp @@ -0,0 +1,38 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gui/LicenseHandler.h" + +#include +#include + +using namespace synergy::license; +using namespace std::chrono; + +const auto kPast = system_clock::now() - hours(1); +const auto kFuture = system_clock::now() + hours(1); + +TEST(LicenseHandlerTests, changeSerialKey_validExpiredLicense_returnsTrue) { + LicenseHandler licenseHandler; + auto hexString = // + "7B76313B70726F3B6E69636B20626F6C746F6E3B313B6" + "E69636B4073796D6C6573732E636F6D3B203B303B307D"; + + auto result = licenseHandler.changeSerialKey(hexString); + + ASSERT_EQ(LicenseHandler::ChangeSerialKeyResult::kSuccess, result); +} diff --git a/src/test/unittests/gui/VersionCheckerTests.cpp b/src/test/unittests/gui/VersionCheckerTests.cpp index 64c935fa2..ed22c4ed1 100644 --- a/src/test/unittests/gui/VersionCheckerTests.cpp +++ b/src/test/unittests/gui/VersionCheckerTests.cpp @@ -17,38 +17,29 @@ #include "gui/VersionChecker.h" -#include "shared/gui/QtCoreTest.h" - -#include #include -class VersionCheckerTests : public QtCoreTest {}; - -class QNetworkAccessManagerMock : public QNetworkAccessManager {}; +class VersionCheckerTests : public ::testing::Test { +protected: + int compareVersions(const QString &left, const QString &right) { + return VersionChecker::compareVersions(left, right); + } +}; TEST_F(VersionCheckerTests, compareVersions_major_isValid) { - auto nam = std::make_shared(); - VersionChecker versionChecker(nam); - - EXPECT_EQ(versionChecker.compareVersions("1.0.0", "2.0.0"), 1); - EXPECT_EQ(versionChecker.compareVersions("2.0.0", "1.0.0"), -1); - EXPECT_EQ(versionChecker.compareVersions("1.0.0", "1.0.0"), 0); + EXPECT_EQ(compareVersions("1.0.0", "2.0.0"), 1); + EXPECT_EQ(compareVersions("2.0.0", "1.0.0"), -1); + EXPECT_EQ(compareVersions("1.0.0", "1.0.0"), 0); } TEST_F(VersionCheckerTests, compareVersions_minor_isValid) { - auto nam = std::make_shared(); - VersionChecker versionChecker(nam); - - EXPECT_EQ(versionChecker.compareVersions("1.1.0", "1.2.0"), 1); - EXPECT_EQ(versionChecker.compareVersions("1.2.0", "1.1.0"), -1); - EXPECT_EQ(versionChecker.compareVersions("1.1.0", "1.1.0"), 0); + EXPECT_EQ(compareVersions("1.1.0", "1.2.0"), 1); + EXPECT_EQ(compareVersions("1.2.0", "1.1.0"), -1); + EXPECT_EQ(compareVersions("1.1.0", "1.1.0"), 0); } TEST_F(VersionCheckerTests, compareVersions_patch_isValid) { - auto nam = std::make_shared(); - VersionChecker versionChecker(nam); - - EXPECT_EQ(versionChecker.compareVersions("1.0.1", "1.0.2"), 1); - EXPECT_EQ(versionChecker.compareVersions("1.0.2", "1.0.1"), -1); - EXPECT_EQ(versionChecker.compareVersions("1.0.1", "1.0.1"), 0); + EXPECT_EQ(compareVersions("1.0.1", "1.0.2"), 1); + EXPECT_EQ(compareVersions("1.0.2", "1.0.1"), -1); + EXPECT_EQ(compareVersions("1.0.1", "1.0.1"), 0); } diff --git a/src/test/unittests/gui/license_notices_tests.cpp b/src/test/unittests/gui/license_notices_tests.cpp new file mode 100644 index 000000000..0b701990d --- /dev/null +++ b/src/test/unittests/gui/license_notices_tests.cpp @@ -0,0 +1,143 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "gui/license_notices.h" + +#include +#include +#include + +using namespace std::chrono; +using namespace synergy::license; +using namespace synergy::gui; +using ::testing::HasSubstr; + +const auto kPast = system_clock::now() - hours(1); +const auto kFutureOneHour = system_clock::now() + hours(1); +const auto kFutureOneDay = system_clock::now() + days(1) + hours(1); +const auto kFutureOneWeek = system_clock::now() + days(7) + hours(1); + +TEST(license_notices_tests, licenseNotice_trialExpired_correctText) { + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kPast; + serialKey.expireTime = kPast; + serialKey.type.setType("trial"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your trial has expired")); +} + +TEST(license_notices_tests, licenseNotice_trialExpiringInOneHour_correctText) { + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kFutureOneHour; + serialKey.expireTime = kFutureOneHour; + serialKey.type.setType("trial"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your trial expires today")); +} + +TEST(license_notices_tests, licenseNotice_trialExpiringInOneDay_correctText) { + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kFutureOneDay; + serialKey.expireTime = kFutureOneDay; + serialKey.type.setType("trial"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your trial expires in 1 day")); +} + +TEST(license_notices_tests, licenseNotice_trialExpiringInOneWeek_correctText) { + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kFutureOneWeek; + serialKey.expireTime = kFutureOneWeek; + serialKey.type.setType("trial"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your trial expires in 7 days")); +} + +TEST(license_notices_tests, licenseNotice_subscriptionExpired_correctText) { + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kPast; + serialKey.expireTime = kPast; + serialKey.type.setType("subscription"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your license has expired")); +} + +TEST( + license_notices_tests, + licenseNotice_subscriptionExpiringInOneHour_correctText) { + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kFutureOneHour; + serialKey.expireTime = kFutureOneHour; + serialKey.type.setType("subscription"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your license expires today")); +} + +TEST( + license_notices_tests, + licenseNotice_subscriptionExpiringInOneDay_correctText) { + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kFutureOneDay; + serialKey.expireTime = kFutureOneDay; + serialKey.type.setType("subscription"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your license expires in 1 day")); +} + +TEST( + license_notices_tests, + licenseNotice_subscriptionExpiringInOneWeek_correctText) { + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kFutureOneWeek; + serialKey.expireTime = kFutureOneWeek; + serialKey.type.setType("subscription"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT( + notice.toStdString(), HasSubstr("Your license expires in 7 days")); +} diff --git a/src/test/unittests/shared/LicenseTests.cpp b/src/test/unittests/shared/LicenseTests.cpp new file mode 100644 index 000000000..9cb82a32e --- /dev/null +++ b/src/test/unittests/shared/LicenseTests.cpp @@ -0,0 +1,182 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#define TEST_ENV + +#include "license/ProductEdition.h" + +#include "license/License.h" + +#include +#include + +using enum Edition; +using namespace synergy::license; +using time_point = std::chrono::system_clock::time_point; +using seconds = std::chrono::seconds; + +class LicenseTests : public ::testing::Test { +protected: + void setNow(License &license, int unixTime) const { + license.setNowFunc([unixTime]() { return time_point{seconds{unixTime}}; }); + } +}; + +TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isTrial) { + // {v2;trial;basic;Bob;1;email;company name;1;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B313B38363430307D"); + setNow(license, 0); + + EXPECT_TRUE(license.isTrial()); +} + +TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isTimeLimited) { + // {v2;trial;basic;Bob;1;email;company name;1;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B313B38363430307D"); + setNow(license, 0); + + EXPECT_TRUE(license.isTimeLimited()); +} + +TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isNotSubscription) { + // {v2;trial;basic;Bob;1;email;company name;1;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B313B38363430307D"); + setNow(license, 0); + + EXPECT_FALSE(license.isSubscription()); +} + +TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isExpiring) { + // {v2;trial;basic;Bob;1;email;company name;1;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B313B38363430307D"); + setNow(license, 0); + + EXPECT_FALSE(license.isExpiring()); +} + +TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isBasicEdition) { + // {v2;trial;basic;Bob;1;email;company name;1;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B313B38363430307D"); + setNow(license, 0); + + EXPECT_EQ(kBasic, license.productEdition()); +} + +TEST_F(LicenseTests, isExpiring_expiringV2TrialBasicSerial_returnTrue) { + // {v2;trial;basic;Bob;1;email;company name;0;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 1); + + EXPECT_TRUE(license.isTrial()); + EXPECT_TRUE(license.isExpiring()); +} + +TEST_F(LicenseTests, isExpired_validV2TrialBasicSerial_returnFalse) { + // {v2;trial;basic;Bob;1;email;company name;0;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 0); + + EXPECT_TRUE(license.isTrial()); + EXPECT_FALSE(license.isExpired()); +} + +TEST_F(LicenseTests, isExpired_expiringV2TrialBasicSerial_returnFalse) { + // {v2;trial;basic;Bob;1;email;company name;0;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 1); + + EXPECT_TRUE(license.isTrial()); + EXPECT_FALSE(license.isExpired()); +} + +TEST_F(LicenseTests, isExpired_expiredV2TrialBasicSerial_returnTrue) { + // {v2;trial;basic;Bob;1;email;company name;0;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 86401); + + EXPECT_TRUE(license.isTrial()); + EXPECT_TRUE(license.isExpired()); +} + +TEST_F(LicenseTests, daysLeft_validExactlyOneDayV2TrialBasicSerial_returnOne) { + // {v2;trial;basic;Bob;1;email;company name;0;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 0); + + EXPECT_EQ(1, license.daysLeft().count()); +} + +TEST_F(LicenseTests, daysLeft_validWithinOneDayV2TrialBasicSerial_returnOne) { + // {v2;trial;basic;Bob;1;email;company name;0;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 0); + + EXPECT_EQ(1, license.daysLeft().count()); +} + +TEST_F(LicenseTests, daysLeft_expiredV2TrialBasicSerial_returnZero) { + // {v2;trial;basic;Bob;1;email;company name;0;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 86401); + + EXPECT_EQ(0, license.daysLeft().count()); +} + +// Subscription license tests +TEST_F(LicenseTests, isExpiring_validV2SubscriptionBasicSerial_returnFalse) { + // {v2;subscription;basic;Bob;1;email;company name;1;86400} + License license("7B76323B737562736372697074696F6E3B62617369633B426F623B313B6" + "56D61696C3B636F6D70616E79206E616D653B313B38363430307D"); + setNow(license, 0); + + EXPECT_TRUE(license.isSubscription()); + EXPECT_FALSE(license.isExpiring()); + EXPECT_EQ(kBasic, license.productEdition()); +} + +TEST_F(LicenseTests, isExpiring_expiringV2SubscriptionBasicSerial_returnTrue) { + // {v2;subscription;basic;Bob;1;email;company name;0;86400} + License license("7B76323B737562736372697074696F6E3B62617369633B426F623B313B6" + "56D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 1); + + EXPECT_TRUE(license.isSubscription()); + EXPECT_TRUE(license.isExpiring()); +} + +TEST_F(LicenseTests, isExpired_expiredV2SubscriptionBasicSerial_returnTrue) { + // {v2;subscription;basic;Bob;1;email;company name;0;86400} + License license("7B76323B737562736372697074696F6E3B62617369633B426F623B313B6" + "56D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 86401); + + EXPECT_TRUE(license.isSubscription()); + EXPECT_TRUE(license.isExpired()); +} diff --git a/src/test/unittests/shared/ProductTests.cpp b/src/test/unittests/shared/ProductTests.cpp new file mode 100644 index 000000000..b978a5080 --- /dev/null +++ b/src/test/unittests/shared/ProductTests.cpp @@ -0,0 +1,81 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define TEST_ENV + +#include "license/Product.h" + +#include + +using enum Product::Edition; + +TEST(ProductTests, equal_operator) { + Product product1(kPro); + Product product2(kPro); + + EXPECT_EQ(product1, product2); +} + +TEST(ProductTests, ctor_businessName_isValid) { + Product product(Product::SerialKeyEditionID::Buisiness); + + EXPECT_EQ(kBusiness, product.edition()); + EXPECT_TRUE(product.isValid()); +} + +TEST(ProductTests, ctor_basicType_isValid) { + Product product(kBasic); + + EXPECT_TRUE(product.isValid()); +} + +TEST(ProductTests, setEdition_invalidType_throws) { + Product product; + + EXPECT_THROW(product.setEdition("test"), Product::InvalidType); +} + +TEST(ProductTests, setEdition_pro_isValid) { + Product product; + + product.setEdition(kPro); + + EXPECT_EQ(kPro, product.edition()); + EXPECT_EQ(Product::SerialKeyEditionID::Pro, product.serialKeyId()); + EXPECT_EQ("Synergy 1 Pro", product.name()); + EXPECT_TRUE(product.isValid()); +} + +TEST(ProductTests, setEdition_basic_isValid) { + Product product; + + product.setEdition(kBasic); + + EXPECT_EQ(kBasic, product.edition()); + EXPECT_EQ(Product::SerialKeyEditionID::Basic, product.serialKeyId()); + EXPECT_EQ("Synergy 1 Basic", product.name()); +} + +TEST(ProductTests, setEdition_business_isValid) { + Product product; + + product.setEdition(kBusiness); + + EXPECT_EQ(kBusiness, product.edition()); + EXPECT_EQ(Product::SerialKeyEditionID::Buisiness, product.serialKeyId()); + EXPECT_EQ("Synergy 1 Business", product.name()); +} diff --git a/src/test/unittests/shared/SerialKeyEditionTests.cpp b/src/test/unittests/shared/SerialKeyEditionTests.cpp deleted file mode 100644 index 72d6e7ce0..000000000 --- a/src/test/unittests/shared/SerialKeyEditionTests.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "shared/EditionType.h" -#define TEST_ENV - -#include "shared/SerialKeyEdition.h" - -#include - -TEST(SerialKeyEditionTests, equal_operator) { - SerialKeyEdition edition1(kPro); - SerialKeyEdition edition2(kPro); - - EXPECT_EQ(edition1, edition2); -} - -TEST(SerialKeyEditionTests, ctor_default_unregistered) { - SerialKeyEdition edition; - - EXPECT_EQ(kUnregistered, edition.getType()); - EXPECT_EQ(SerialKeyEdition::Unregistered, edition.getName()); - EXPECT_EQ("Synergy 1 (Unregistered)", edition.getProductName()); - EXPECT_FALSE(edition.isValid()); -} - -TEST(SerialKeyEditionTests, ctor_businessName_isValid) { - SerialKeyEdition edition(SerialKeyEdition::Buisiness); - - EXPECT_EQ(kBusiness, edition.getType()); - EXPECT_TRUE(edition.isValid()); -} - -TEST(SerialKeyEditionTests, ctor_basicType_isValid) { - SerialKeyEdition edition(kBasic); - - EXPECT_TRUE(edition.isValid()); -} - -TEST(SerialKeyEditionTests, setType_invalidType_unregistered) { - SerialKeyEdition edition; - - edition.setType("stub"); - - EXPECT_EQ(kUnregistered, edition.getType()); -} - -TEST(SerialKeyEditionTests, setType_lite_isValid) { - SerialKeyEdition edition; - - edition.setType(kLite); - - EXPECT_EQ(kLite, edition.getType()); - EXPECT_EQ(SerialKeyEdition::Lite, edition.getName()); - EXPECT_EQ("Synergy 1", edition.getProductName()); - EXPECT_TRUE(edition.isValid()); -} - -TEST(SerialKeyEditionTests, setType_ultimate_isValid) { - SerialKeyEdition edition; - - edition.setType(SerialKeyEdition::Ultimate); - - EXPECT_EQ(kUltimate, edition.getType()); - EXPECT_EQ(SerialKeyEdition::Ultimate, edition.getName()); - EXPECT_EQ("Synergy 1 Ultimate", edition.getProductName()); -} - -TEST(SerialKeyEditionTests, setType_pro_isValid) { - SerialKeyEdition edition; - - edition.setType(kPro); - - EXPECT_EQ(kPro, edition.getType()); - EXPECT_EQ(SerialKeyEdition::Pro, edition.getName()); - EXPECT_EQ("Synergy 1 Pro", edition.getProductName()); - EXPECT_TRUE(edition.isValid()); -} - -TEST(SerialKeyEditionTests, setType_basic_isValid) { - SerialKeyEdition edition; - - edition.setType(kBasic); - - EXPECT_EQ(kBasic, edition.getType()); - EXPECT_EQ(SerialKeyEdition::Basic, edition.getName()); - EXPECT_EQ("Synergy 1 Basic", edition.getProductName()); -} - -TEST(SerialKeyEditionTests, setType_business_isValid) { - SerialKeyEdition edition; - - edition.setType(kBusiness); - - EXPECT_EQ(kBusiness, edition.getType()); - EXPECT_EQ(SerialKeyEdition::Buisiness, edition.getName()); - EXPECT_EQ("Synergy 1 Business", edition.getProductName()); -} - -TEST(SerialKeyEditionTests, setType_basicChina_isValid) { - SerialKeyEdition edition; - - edition.setType(kBasicChina); - - EXPECT_EQ(kBasicChina, edition.getType()); - EXPECT_EQ(SerialKeyEdition::BasicChina, edition.getName()); - EXPECT_EQ("Synergy 1 中文版", edition.getProductName()); - EXPECT_TRUE(edition.isChina()); -} - -TEST(SerialKeyEditionTests, setType_proChina_isValid) { - SerialKeyEdition edition; - - edition.setType(kProChina); - - EXPECT_EQ(kProChina, edition.getType()); - EXPECT_EQ(SerialKeyEdition::ProChina, edition.getName()); - EXPECT_EQ("Synergy 1 Pro 中文版", edition.getProductName()); - EXPECT_TRUE(edition.isChina()); -} diff --git a/src/test/unittests/shared/SerialKeyTests.cpp b/src/test/unittests/shared/SerialKeyTests.cpp deleted file mode 100644 index c4baa153e..000000000 --- a/src/test/unittests/shared/SerialKeyTests.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#define TEST_ENV - -#include "shared/SerialKey.h" - -#include -#include - -TEST(SerialKeyTests, isExpiring_validV2TrialBasicSerial_returnFalse) { - // {v2;trial;basic;Bob;1;email;company name;1;86400} - SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B313B38363430307D"); - EXPECT_EQ(true, serial.isTrial()); - EXPECT_EQ(true, serial.isTemporary()); - EXPECT_FALSE(serial.isExpiring(0)); - EXPECT_EQ(kBasic, serial.edition()); -} - -TEST(SerialKeyTests, isExpiring_expiringV2TrialBasicSerial_returnTrue) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(true, serial.isTrial()); - EXPECT_EQ(true, serial.isTemporary()); - EXPECT_EQ(true, serial.isExpiring(1)); -} - -TEST(SerialKeyTests, isExpiring_expiredV2TrialBasicSerial_returnFalse) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(true, serial.isTrial()); - EXPECT_EQ(true, serial.isTemporary()); - EXPECT_FALSE(serial.isExpiring(86401)); -} - -TEST(SerialKeyTests, isExpired_validV2TrialBasicSerial_returnFalse) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(true, serial.isTrial()); - EXPECT_EQ(true, serial.isTemporary()); - EXPECT_FALSE(serial.isExpired(0)); -} - -TEST(SerialKeyTests, isExpired_expiringV2TrialBasicSerial_returnFalse) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(true, serial.isTrial()); - EXPECT_EQ(true, serial.isTemporary()); - EXPECT_FALSE(serial.isExpired(1)); -} - -TEST(SerialKeyTests, isExpired_expiredV2TrialBasicSerial_returnTrue) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(true, serial.isTrial()); - EXPECT_EQ(true, serial.isTemporary()); - EXPECT_EQ(true, serial.isExpired(86401)); -} - -TEST(SerialKeyTests, daysLeft_validExactlyOneDayV2TrialBasicSerial_returnOne) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(1, serial.daysLeft(0)); -} - -TEST(SerialKeyTests, daysLeft_validWithinOneDayV2TrialBasicSerial_returnOne) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(1, serial.daysLeft(1)); -} - -TEST(SerialKeyTests, daysLeft_expiredV2TrialBasicSerial_returnZero) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(0, serial.daysLeft(86401)); -} - -// Subscription license tests -TEST(SerialKeyTests, isExpiring_validV2SubscriptionBasicSerial_returnFalse) { - // {v2;subscription;basic;Bob;1;email;company name;1;86400} - SerialKey serial("7B76323B737562736372697074696F6E3B62617369633B426F623B313B6" - "56D61696C3B636F6D70616E79206E616D653B313B38363430307D"); - EXPECT_EQ(false, serial.isTrial()); - EXPECT_EQ(true, serial.isTemporary()); - EXPECT_FALSE(serial.isExpiring(0)); - EXPECT_EQ(kBasic, serial.edition()); -} - -TEST(SerialKeyTests, isExpiring_expiringV2SubscriptionBasicSerial_returnTrue) { - // {v2;subscription;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B737562736372697074696F6E3B62617369633B426F623B313B6" - "56D61696C3B636F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(false, serial.isTrial()); - EXPECT_EQ(true, serial.isTemporary()); - EXPECT_EQ(true, serial.isExpiring(1)); - EXPECT_FALSE(serial.isMaintenance()); -} - -TEST(SerialKeyTests, isExpiring_expiringV2MentenanceSerial_returnTrue) { - // {v2;maintenance;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B6D61696E74656E616E63653B70726F3B7365726869692068616" - "47A68696C6F763B313B7365726869694073796D6C6573732E636F6D3B20" - "3B303B313635353132343139307D"); - EXPECT_FALSE(serial.isTrial()); - EXPECT_FALSE(serial.isTemporary()); - EXPECT_TRUE(serial.isMaintenance()); -} - -TEST(SerialKeyTests, isExpired_expiredV2SubscriptionBasicSerial_returnTrue) { - // {v2;subscription;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B737562736372697074696F6E3B62617369633B426F623B313B6" - "56D61696C3B636F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(false, serial.isTrial()); - EXPECT_EQ(true, serial.isTemporary()); - EXPECT_EQ(true, serial.isExpired(86401)); -} - -// toString method tests -TEST(SerialKeyTests, toStringV2SubscriptionBasicSerialKey) { - //{v2;subscription;basic;Bob;1;email;company name;0;86400} - const std::string Expected = - "7B76323B737562736372697074696F6E3B62617369633B426F623B313B656D61696C3B63" - "6F6D70616E79206E616D653B303B38363430307D"; - SerialKey serial(Expected); - EXPECT_EQ(Expected, serial.toString()); -} - -TEST(SerialKeyTests, toStringV2TrialBasicSerialKey) { - //{v2;trial;basic;Bob;1;email;company name;0;86400} - const std::string Expected = - "7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E7920" - "6E616D653B303B38363430307D"; - SerialKey serial(Expected); - EXPECT_EQ(Expected, serial.toString()); -} - -TEST(SerialKeyTests, toStringV1BasicSerialKey) { - //{v1;basic;Bob;1;email;company name;0;0} - const std::string Expected = "7B76313B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B307D"; - SerialKey serial(Expected); - EXPECT_EQ(Expected, serial.toString()); -} - -TEST(SerialKeyTests, IsValidKey_false) { - //{v1;basic;Bob;1;email;company name;0;0} - SerialKey serial(kUnregistered); - EXPECT_EQ(false, serial.isValid()); -} - -TEST(SerialKeyTests, IsValidKey_true) { - SerialKey serial(kBasic); - EXPECT_EQ(true, serial.isValid()); -} - -TEST(SerialKeyTests, IsValidExpiredKey_false) { - SerialKey serial("7B76323B737562736372697074696F6E3B62617369633B426F623B313B6" - "56D61696C3B636F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(false, serial.isValid()); -} - -TEST(SerialKeyTests, test_getSpanLeft) { - SerialKey key; - int expected{-1}; - EXPECT_EQ(expected, key.getSpanLeft()); -} - -TEST(SerialKeyTests, test_getSpanLeft_subscription) { - // {v2;subscription;basic;Bob;1;email;company name;0;86400} - SerialKey key("7B76323B737562736372697074696F6E3B62617369633B426F623B313B656D" - "61696C3B636F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(1000, key.getSpanLeft(86399)); - EXPECT_EQ(-1, key.getSpanLeft(86401)); -} - -TEST(SerialKeyTests, test_getSpanLeft_max_int) { - //"{v2;subscription;basic;Bob;1;email;company name;0;3147483647}" - SerialKey key("7B76323B737562736372697074696F6E3B62617369633B426F623B313B656D" - "61696C3B636F6D70616E79206E616D653B303B333134373438333634377D"); - EXPECT_EQ(INT_MAX, key.getSpanLeft(1)); -} diff --git a/src/test/unittests/shared/SerialKeyTypeTests.cpp b/src/test/unittests/shared/SerialKeyTypeTests.cpp index f2743c3cb..08a6a89fd 100644 --- a/src/test/unittests/shared/SerialKeyTypeTests.cpp +++ b/src/test/unittests/shared/SerialKeyTypeTests.cpp @@ -17,37 +17,26 @@ #define TEST_ENV -#include "shared/SerialKeyType.h" +#include "license/SerialKeyType.h" #include TEST(SerialKeyTypeTests, TrialTemporaryKeyType_false) { SerialKeyType KeyType; - EXPECT_EQ(false, KeyType.isTrial()); - EXPECT_EQ(false, KeyType.isTemporary()); - EXPECT_FALSE(KeyType.isMaintenance()); + EXPECT_FALSE(KeyType.isTrial()); + EXPECT_FALSE(KeyType.isSubscription()); } TEST(SerialKeyTypeTests, TrialTemporaryKeyType_true) { SerialKeyType KeyType; - KeyType.setKeyType("trial"); - EXPECT_EQ(true, KeyType.isTrial()); - EXPECT_EQ(true, KeyType.isTemporary()); - EXPECT_FALSE(KeyType.isMaintenance()); + KeyType.setType("trial"); + EXPECT_TRUE(KeyType.isTrial()); + EXPECT_FALSE(KeyType.isSubscription()); } -TEST(SerialKeyTypeTests, TemporaryKeyType_true) { +TEST(SerialKeyTypeTests, TimeLimitedKeyType_true) { SerialKeyType KeyType; - KeyType.setKeyType("subscription"); - EXPECT_EQ(false, KeyType.isTrial()); - EXPECT_EQ(true, KeyType.isTemporary()); - EXPECT_FALSE(KeyType.isMaintenance()); -} - -TEST(SerialKeyTypeTests, MaintanenceKeyType_true) { - SerialKeyType KeyType; - KeyType.setKeyType("maintenance"); - EXPECT_EQ(false, KeyType.isTrial()); - EXPECT_EQ(false, KeyType.isTemporary()); - EXPECT_TRUE(KeyType.isMaintenance()); + KeyType.setType("subscription"); + EXPECT_FALSE(KeyType.isTrial()); + EXPECT_TRUE(KeyType.isSubscription()); } diff --git a/src/test/unittests/synergy/ArgParserTests.cpp b/src/test/unittests/synergy/ArgParserTests.cpp index adf1330d2..af25e5111 100644 --- a/src/test/unittests/synergy/ArgParserTests.cpp +++ b/src/test/unittests/synergy/ArgParserTests.cpp @@ -48,7 +48,7 @@ TEST(ArgParserTests, isArg_missingArgs_returnFalse) { int i = 1; const int argc = 2; const char *argv[argc] = {"stub", "-t"}; - static lib::synergy::ArgsBase argsBase; + static synergy::ArgsBase argsBase; ArgParser argParser(NULL); argParser.setArgsBase(argsBase); @@ -222,7 +222,7 @@ TEST(ArgParserTests, parseToolArgs_matches_correspondingly) { TEST(ArgParserTests, parseServerArgs_parses_each_category) { ArgParser parser(nullptr); - lib::synergy::ServerArgs args; + synergy::ServerArgs args; args.m_daemon = false; char const *argv[] = { "synergy", "--help" @@ -242,7 +242,7 @@ TEST(ArgParserTests, parseServerArgs_parses_each_category) { TEST(ArgParserTests, parseClientArgs_parses_single_help) { ArgParser parser(nullptr); - lib::synergy::ClientArgs args; + synergy::ClientArgs args; args.m_daemon = false; char const *argv[] = { "synergy", diff --git a/src/test/unittests/synergy/ClientArgsParsingTests.cpp b/src/test/unittests/synergy/ClientArgsParsingTests.cpp index 42621b476..90cc687fd 100644 --- a/src/test/unittests/synergy/ClientArgsParsingTests.cpp +++ b/src/test/unittests/synergy/ClientArgsParsingTests.cpp @@ -39,7 +39,7 @@ TEST(ClientArgsParsingTests, parseClientArgs_yScrollArg_setYScroll) { .WillByDefault(Invoke(client_stubParseGenericArgs)); ON_CALL(argParser, checkUnexpectedArgs()) .WillByDefault(Invoke(client_stubCheckUnexpectedArgs)); - lib::synergy::ClientArgs clientArgs; + synergy::ClientArgs clientArgs; const int argc = 3; const char *kYScrollCmd[argc] = {"stub", "--yscroll", "1"}; @@ -54,7 +54,7 @@ TEST(ClientArgsParsingTests, parseClientArgs_setLangSync) { .WillByDefault(Invoke(client_stubParseGenericArgs)); ON_CALL(argParser, checkUnexpectedArgs()) .WillByDefault(Invoke(client_stubCheckUnexpectedArgs)); - lib::synergy::ClientArgs clientArgs; + synergy::ClientArgs clientArgs; clientArgs.m_enableLangSync = false; const int argc = 2; std::array kLangCmd = {"stub", "--sync-language"}; @@ -66,14 +66,14 @@ TEST(ClientArgsParsingTests, parseClientArgs_setLangSync) { TEST(ClientArgsParsingTests, parseClientArgs_setInvertScroll) { NiceMock argParser; - lib::synergy::ClientArgs clientArgs; + synergy::ClientArgs clientArgs; const int argc = 2; std::array kLangCmd = {"stub", "--invert-scroll"}; argParser.parseClientArgs(clientArgs, argc, kLangCmd.data()); EXPECT_EQ( clientArgs.m_clientScrollDirection, - lib::synergy::ClientScrollDirection::INVERT_SERVER); + synergy::ClientScrollDirection::INVERT_SERVER); } TEST(ClientArgsParsingTests, parseClientArgs_setCommonArgs) { @@ -82,7 +82,7 @@ TEST(ClientArgsParsingTests, parseClientArgs_setCommonArgs) { .WillByDefault(Invoke(client_stubParseGenericArgs)); ON_CALL(argParser, checkUnexpectedArgs()) .WillByDefault(Invoke(client_stubCheckUnexpectedArgs)); - lib::synergy::ClientArgs clientArgs; + synergy::ClientArgs clientArgs; clientArgs.m_enableLangSync = false; const int argc = 9; std::array kLangCmd = { @@ -105,7 +105,7 @@ TEST(ClientArgsParsingTests, parseClientArgs_addressArg_setSynergyAddress) { .WillByDefault(Invoke(client_stubParseGenericArgs)); ON_CALL(argParser, checkUnexpectedArgs()) .WillByDefault(Invoke(client_stubCheckUnexpectedArgs)); - lib::synergy::ClientArgs clientArgs; + synergy::ClientArgs clientArgs; const int argc = 2; const char *kAddressCmd[argc] = {"stub", "mock_address"}; @@ -121,7 +121,7 @@ TEST(ClientArgsParsingTests, parseClientArgs_noAddressArg_returnFalse) { .WillByDefault(Invoke(client_stubParseGenericArgs)); ON_CALL(argParser, checkUnexpectedArgs()) .WillByDefault(Invoke(client_stubCheckUnexpectedArgs)); - lib::synergy::ClientArgs clientArgs; + synergy::ClientArgs clientArgs; const int argc = 1; const char *kNoAddressCmd[argc] = {"stub"}; @@ -136,7 +136,7 @@ TEST(ClientArgsParsingTests, parseClientArgs_unrecognizedArg_returnFalse) { .WillByDefault(Invoke(client_stubParseGenericArgs)); ON_CALL(argParser, checkUnexpectedArgs()) .WillByDefault(Invoke(client_stubCheckUnexpectedArgs)); - lib::synergy::ClientArgs clientArgs; + synergy::ClientArgs clientArgs; const int argc = 3; const char *kUnrecognizedCmd[argc] = {"stub", "mock_arg", "mock_address"}; diff --git a/src/test/unittests/synergy/GenericArgsParsingTests.cpp b/src/test/unittests/synergy/GenericArgsParsingTests.cpp index 59fa0fa98..79d325033 100644 --- a/src/test/unittests/synergy/GenericArgsParsingTests.cpp +++ b/src/test/unittests/synergy/GenericArgsParsingTests.cpp @@ -42,11 +42,11 @@ public: void TearDown() { delete m_argParser; } - static lib::synergy::ArgsBase argsBase; + static synergy::ArgsBase argsBase; ArgParser *m_argParser = nullptr; }; -lib::synergy::ArgsBase GenericArgsParsingTests::argsBase; +synergy::ArgsBase GenericArgsParsingTests::argsBase; TEST_F(GenericArgsParsingTests, parseGenericArgs_logLevelCmd_setLogLevel) { int i = 1; diff --git a/src/test/unittests/synergy/ServerArgsParsingTests.cpp b/src/test/unittests/synergy/ServerArgsParsingTests.cpp index d7251772c..1c9e7ad4d 100644 --- a/src/test/unittests/synergy/ServerArgsParsingTests.cpp +++ b/src/test/unittests/synergy/ServerArgsParsingTests.cpp @@ -33,9 +33,9 @@ bool server_stubParseGenericArgs(int, const char *const *, int &) { bool server_stubCheckUnexpectedArgs() { return false; } TEST(ServerArgs, ServerArgs_will_construct_from_copy) { - lib::synergy::ServerArgs serverArgs; + synergy::ServerArgs serverArgs; serverArgs.m_display = "display0"; - lib::synergy::ServerArgs serverArgs2{serverArgs}; + synergy::ServerArgs serverArgs2{serverArgs}; EXPECT_EQ(serverArgs.m_display, serverArgs2.m_display); } @@ -45,7 +45,7 @@ TEST(ServerArgsParsingTests, parseServerArgs_addressArg_setSynergyAddress) { .WillByDefault(Invoke(server_stubParseGenericArgs)); ON_CALL(argParser, checkUnexpectedArgs()) .WillByDefault(Invoke(server_stubCheckUnexpectedArgs)); - lib::synergy::ServerArgs serverArgs; + synergy::ServerArgs serverArgs; const int argc = 3; const char *kAddressCmd[argc] = {"stub", "--address", "mock_address"}; @@ -60,7 +60,7 @@ TEST(ServerArgsParsingTests, parseServerArgs_configArg_setConfigFile) { .WillByDefault(Invoke(server_stubParseGenericArgs)); ON_CALL(argParser, checkUnexpectedArgs()) .WillByDefault(Invoke(server_stubCheckUnexpectedArgs)); - lib::synergy::ServerArgs serverArgs; + synergy::ServerArgs serverArgs; const int argc = 3; const char *kConfigCmd[argc] = {"stub", "--config", "mock_configFile"}; @@ -69,13 +69,13 @@ TEST(ServerArgsParsingTests, parseServerArgs_configArg_setConfigFile) { EXPECT_EQ("mock_configFile", serverArgs.m_configFile); } -TEST(ServerArgsParsingTests, parseServerArgs_checkSerialKeyParams) { +TEST(ServerArgsParsingTests, parseServerArgs_serialKey_isSet) { NiceMock argParser; ON_CALL(argParser, parseGenericArgs(_, _, _)) .WillByDefault(Invoke(server_stubParseGenericArgs)); ON_CALL(argParser, checkUnexpectedArgs()) .WillByDefault(Invoke(server_stubCheckUnexpectedArgs)); - lib::synergy::ServerArgs serverArgs; + synergy::ServerArgs serverArgs; const int argc = 3; const char *serial = "7B76323B737562736372697074696F6E3B62617369633B426F623B313B656D61696C3B63" @@ -83,7 +83,7 @@ TEST(ServerArgsParsingTests, parseServerArgs_checkSerialKeyParams) { std::array kSerialCmd = {"stub", "--serial-key", serial}; argParser.parseServerArgs(serverArgs, argc, kSerialCmd.data()); - EXPECT_EQ(serial, serverArgs.m_serial.toString()); + EXPECT_EQ(serial, serverArgs.m_serialKey.toString()); } TEST(ServerArgsParsingTests, parseServerArgs_checkUnexpectedParams) { @@ -92,7 +92,7 @@ TEST(ServerArgsParsingTests, parseServerArgs_checkUnexpectedParams) { .WillByDefault(Invoke(server_stubParseGenericArgs)); ON_CALL(argParser, checkUnexpectedArgs()) .WillByDefault(Invoke(server_stubCheckUnexpectedArgs)); - lib::synergy::ServerArgs serverArgs; + synergy::ServerArgs serverArgs; const int argc = 2; std::array kUnknownCmd = {"stub", "--unknown"};