diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 5ada72ff..73368b84 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -62,7 +62,8 @@ SOURCES += src/main.cpp \ src/Plugin.cpp \ src/WebClient.cpp \ ../lib/common/PluginVersion.cpp \ - src/SubscriptionManager.cpp + src/SubscriptionManager.cpp \ + src/ActivationNotifier.cpp HEADERS += src/MainWindow.h \ src/AboutDialog.h \ src/ServerConfig.h \ @@ -110,7 +111,7 @@ HEADERS += src/MainWindow.h \ src/WebClient.h \ ../lib/common/PluginVersion.h \ src/SubscriptionManager.h \ - src/SubscriptionState.h + src/ActivationNotifier.h RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc macx { diff --git a/src/gui/src/SubscriptionState.h b/src/gui/src/ActivationNotifier.cpp similarity index 67% rename from src/gui/src/SubscriptionState.h rename to src/gui/src/ActivationNotifier.cpp index e52a6df5..7efe4e82 100644 --- a/src/gui/src/SubscriptionState.h +++ b/src/gui/src/ActivationNotifier.cpp @@ -15,14 +15,22 @@ * along with this program. If not, see . */ -#ifndef SUBSCRIPTIONSTATE_H -#define SUBSCRIPTIONSTATE_H +#include "ActivationNotifier.h" -enum qSubscriptionState { - kValid, - kInvalid, - kExpiredSoon, - kExpired -}; +#include "CoreInterface.h" -#endif // SUBSCRIPTIONSTATE_H +ActivationNotifier::ActivationNotifier(QObject *parent) : + QObject(parent) +{ +} + +void ActivationNotifier::setIdentity(QString identity) +{ + m_Identity = identity; +} + +void ActivationNotifier::notify() +{ + CoreInterface coreInterface; + coreInterface.notifyActivation(m_Identity); +} diff --git a/src/gui/src/ActivationNotifier.h b/src/gui/src/ActivationNotifier.h new file mode 100644 index 00000000..d245cd27 --- /dev/null +++ b/src/gui/src/ActivationNotifier.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Seamless Inc. + * + * 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 . + */ + +#ifndef ACTIVATIONNOTIFIER_H +#define ACTIVATIONNOTIFIER_H + +#include + +class ActivationNotifier : public QObject +{ +Q_OBJECT +public: + explicit ActivationNotifier(QObject *parent = 0); + + void setIdentity(QString identity); + +public slots: + void notify(); + +signals: + void finished(); + +private: + QString m_Identity; +}; + +#endif // ACTIVATIONNOTIFIER_H diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 6bbf7323..69142d1d 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -58,7 +58,8 @@ AppConfig::AppConfig(QSettings* settings) : m_ElevateMode(false), m_AutoConfigPrompted(false), m_CryptoEnabled(false), - m_AutoHide(false) + m_AutoHide(false), + m_LastExpiringWarningTime(0) { Q_ASSERT(m_pSettings); @@ -129,10 +130,10 @@ void AppConfig::loadSettings() m_AutoConfigPrompted = settings().value("autoConfigPrompted", false).toBool(); m_Edition = settings().value("edition", Unknown).toInt(); m_ActivateEmail = settings().value("activateEmail", "").toString(); - m_UserToken = settings().value("userToken", "").toString(); m_CryptoEnabled = settings().value("cryptoEnabled", false).toBool(); m_AutoHide = settings().value("autoHide", false).toBool(); m_Serialkey = settings().value("serialKey", "").toString(); + m_LastExpiringWarningTime = settings().value("lastExpiringWarningTime", 0).toInt(); } void AppConfig::saveSettings() @@ -151,10 +152,10 @@ void AppConfig::saveSettings() settings().setValue("autoConfigPrompted", m_AutoConfigPrompted); settings().setValue("edition", m_Edition); settings().setValue("activateEmail", m_ActivateEmail); - settings().setValue("userToken", m_UserToken); settings().setValue("cryptoEnabled", m_CryptoEnabled); settings().setValue("autoHide", m_AutoHide); settings().setValue("serialKey", m_Serialkey); + settings().setValue("lastExpiringWarningTime", m_LastExpiringWarningTime); } void AppConfig::setAutoConfig(bool autoConfig) diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 80de53f6..684c0015 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -76,10 +76,10 @@ class AppConfig int edition() { return m_Edition; } void setActivateEmail(QString e) { m_ActivateEmail = e; } QString activateEmail() { return m_ActivateEmail; } - void setUserToken(QString t) { m_UserToken = t; } - QString userToken() { return m_UserToken; } void setSerialKey(QString serial) { m_Serialkey = serial; } QString serialKey() { return m_Serialkey; } + int lastExpiringWarningTime() const { return m_LastExpiringWarningTime; } + void setLastExpiringWarningTime(int t) { m_LastExpiringWarningTime = t; } QString synergysName() const { return m_SynergysName; } QString synergycName() const { return m_SynergycName; } @@ -95,6 +95,8 @@ class AppConfig void setAutoHide(bool b) { m_AutoHide = b; } bool getAutoHide() { return m_AutoHide; } + void saveSettings(); + protected: QSettings& settings() { return *m_pSettings; } void setScreenName(const QString& s) { m_ScreenName = s; } @@ -109,7 +111,6 @@ class AppConfig void setElevateMode(bool b) { m_ElevateMode = b; } void loadSettings(); - void saveSettings(); private: QSettings* m_pSettings; @@ -128,10 +129,10 @@ class AppConfig bool m_AutoConfigPrompted; int m_Edition; QString m_ActivateEmail; - QString m_UserToken; bool m_CryptoEnabled; bool m_AutoHide; QString m_Serialkey; + int m_LastExpiringWarningTime; static const char m_SynergysName[]; static const char m_SynergycName[]; diff --git a/src/gui/src/CommandProcess.cpp b/src/gui/src/CommandProcess.cpp index 763a7058..e1b4a7e4 100644 --- a/src/gui/src/CommandProcess.cpp +++ b/src/gui/src/CommandProcess.cpp @@ -18,17 +18,46 @@ #include "CommandProcess.h" #include +#include -CommandProcess::CommandProcess(QString cmd, QStringList arguments) : +CommandProcess::CommandProcess(QString cmd, QStringList arguments, QString input) : m_Command(cmd), - m_Arguments(arguments) + m_Arguments(arguments), + m_Input(input) { } -void CommandProcess::run() +QString CommandProcess::run() { QProcess process; + process.setReadChannel(QProcess::StandardOutput); process.start(m_Command, m_Arguments); - process.waitForFinished(); + bool success = process.waitForStarted(); + + QString output, error; + if (success) + { + if (!m_Input.isEmpty()) { + process.write(m_Input.toStdString().c_str()); + } + + if (process.waitForFinished()) { + output = process.readAllStandardOutput().trimmed(); + error = process.readAllStandardError().trimmed(); + } + } + + int code = process.exitCode(); + if (!error.isEmpty() || !success || code != 0) + { + throw std::runtime_error( + QString("Code: %1\nError: %2") + .arg(process.exitCode()) + .arg(error.isEmpty() ? "Unknown" : error) + .toStdString()); + } + emit finished(); + + return output; } diff --git a/src/gui/src/CommandProcess.h b/src/gui/src/CommandProcess.h index 62e89bfb..508552da 100644 --- a/src/gui/src/CommandProcess.h +++ b/src/gui/src/CommandProcess.h @@ -25,17 +25,18 @@ class CommandProcess : public QObject Q_OBJECT public: - CommandProcess(QString cmd, QStringList arguments); + CommandProcess(QString cmd, QStringList arguments, QString input = ""); signals: void finished(); public slots: - void run(); + QString run(); private: QString m_Command; QStringList m_Arguments; + QString m_Input; }; #endif // COMMANDTHREAD_H diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index 9bc8d132..13560c1b 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -17,6 +17,9 @@ #include "CoreInterface.h" +#include "CommandProcess.h" +#include "QUtility.h" + #include #include #include @@ -71,39 +74,26 @@ QString CoreInterface::checkSubscription() return run(args); } +QString CoreInterface::notifyActivation(const QString& identity) +{ + QStringList args("--notify-activation"); + + QString input(identity + ":" + hash(getFirstMacAddress())); + QString os= getOSInformation(); + if (!os.isEmpty()) { + input.append(":").append(os); + } + input.append("\n"); + + return run(args, input); +} + QString CoreInterface::run(const QStringList& args, const QString& input) { QString program( QCoreApplication::applicationDirPath() + "/" + kCoreBinary); - QProcess process; - process.setReadChannel(QProcess::StandardOutput); - process.start(program, args); - bool success = process.waitForStarted(); - - QString output, error; - if (success) - { - if (!input.isEmpty()) { - process.write(input.toStdString().c_str()); - } - - if (process.waitForFinished()) { - output = process.readAllStandardOutput().trimmed(); - error = process.readAllStandardError().trimmed(); - } - } - - int code = process.exitCode(); - if (!error.isEmpty() || !success || code != 0) - { - throw std::runtime_error( - QString("Code: %1\nError: %2") - .arg(process.exitCode()) - .arg(error.isEmpty() ? "Unknown" : error) - .toStdString()); - } - - return output; + CommandProcess commandProcess(program, args, input); + return commandProcess.run(); } diff --git a/src/gui/src/CoreInterface.h b/src/gui/src/CoreInterface.h index dc2f9fb6..f65348af 100644 --- a/src/gui/src/CoreInterface.h +++ b/src/gui/src/CoreInterface.h @@ -31,5 +31,6 @@ public: QString getSubscriptionFilename(); QString activateSerial(const QString& serial); QString checkSubscription(); + QString notifyActivation(const QString& identity); QString run(const QStringList& args, const QString& input = ""); }; diff --git a/src/gui/src/EditionType.h b/src/gui/src/EditionType.h index ec465a6c..2229cd23 100644 --- a/src/gui/src/EditionType.h +++ b/src/gui/src/EditionType.h @@ -21,6 +21,7 @@ enum qEditionType { Basic, Pro, + Trial, Unknown }; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 76ed7ecd..a05210d9 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -32,7 +32,6 @@ #include "DataDownloader.h" #include "CommandProcess.h" #include "SubscriptionManager.h" -#include "SubscriptionState.h" #include "EditionType.h" #include "QUtility.h" #include "ProcessorArch.h" @@ -135,7 +134,7 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : m_pComboServerList->hide(); - updateEdition(); + setEdition(m_AppConfig.edition()); m_pLabelPadlock->hide(); @@ -698,22 +697,19 @@ QString MainWindow::appPath(const QString& name) bool MainWindow::serverArgs(QStringList& args, QString& app) { - SubscriptionManager subscriptionManager; - if (subscriptionManager.checkSubscriptionExist()) + int edition; + SubscriptionManager subscriptionManager(this, appConfig(), edition); + if (subscriptionManager.fileExists()) { - int edition; - int state = subscriptionManager.checkSubscription(edition); - - if (state == kInvalid) { + if (!subscriptionManager.checkSubscription()) { return false; } - else if (state == kExpired) { - QMessageBox::warning(this, tr("Subscription is expired"), - tr("Your subscription is expired. Please purchase.")); - return false; + else { + setEdition(edition); } } + app = appPath(appConfig().synergysName()); if (!QFile::exists(app)) @@ -962,7 +958,7 @@ void MainWindow::changeEvent(QEvent* event) retranslateUi(this); retranslateMenuBar(); - updateEdition(); + setEdition(m_AppConfig.edition()); break; } @@ -1011,6 +1007,9 @@ void MainWindow::setEdition(int type) else if (type == Pro) { title = "Synergy Pro"; } + else if (type == Trial) { + title = "Synergy Trial"; + } else { title = "Synergy (UNREGISTERED)"; } @@ -1305,20 +1304,6 @@ void MainWindow::promptAutoConfig() m_AppConfig.setAutoConfigPrompted(true); } -void MainWindow::updateEdition() -{ - QString mac = getFirstMacAddress(); - QString hashSrc = m_AppConfig.activateEmail() + mac; - QString hashResult = hash(hashSrc); - - if (hashResult == m_AppConfig.userToken()) { - setEdition(m_AppConfig.edition()); - } - else { - setEdition(Unknown); - } -} - void MainWindow::on_m_pComboServerList_currentIndexChanged(QString ) { if (m_pComboServerList->count() != 0) { diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index c159747a..9c7aeebc 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -172,7 +172,6 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase bool isBonjourRunning(); void downloadBonjour(); void promptAutoConfig(); - void updateEdition(); QString getProfileRootForArg(); void checkConnected(const QString& line); void checkFingerprint(const QString& line); diff --git a/src/gui/src/PluginManager.cpp b/src/gui/src/PluginManager.cpp index a39fef0b..f4d16ff0 100644 --- a/src/gui/src/PluginManager.cpp +++ b/src/gui/src/PluginManager.cpp @@ -18,7 +18,6 @@ #include "PluginManager.h" #include "CoreInterface.h" -#include "CommandProcess.h" #include "DataDownloader.h" #include "QUtility.h" #include "ProcessorArch.h" diff --git a/src/gui/src/PluginWizardPage.cpp b/src/gui/src/PluginWizardPage.cpp index ecbc26e4..ace607da 100644 --- a/src/gui/src/PluginWizardPage.cpp +++ b/src/gui/src/PluginWizardPage.cpp @@ -64,8 +64,7 @@ void PluginWizardPage::initializePage() { QWizardPage::initializePage(); - if (m_Edition == Unknown || - m_Edition == Basic) { + if (m_Edition != Pro) { updateStatus(tr("Setup complete.")); showFinished(); return; diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index 14d1d1e1..dcc8f5eb 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -18,6 +18,7 @@ #include "QUtility.h" #include "ProcessorArch.h" +#include "CommandProcess.h" #if defined(Q_OS_LINUX) #include @@ -90,3 +91,23 @@ qProcessorArch getProcessorArch() return kProcessorArchUnknown; } + +QString getOSInformation() +{ + QString result; + +#if defined(Q_OS_LINUX) + QStringList arguments; + arguments.append("/etc/os-release"); + CommandProcess cp("/bin/cat", arguments); + QString output = cp.run(); + + QRegExp resultRegex(".*PRETTY_NAME=\"([^\"]+)\".*"); + if (resultRegex.exactMatch(output)) { + QString OSInfo = resultRegex.cap(1); + result = OSInfo; + } +#endif + + return result; +} diff --git a/src/gui/src/QUtility.h b/src/gui/src/QUtility.h index 89861dda..01a42c03 100644 --- a/src/gui/src/QUtility.h +++ b/src/gui/src/QUtility.h @@ -28,3 +28,4 @@ void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData); QString hash(const QString& string); QString getFirstMacAddress(); qProcessorArch getProcessorArch(); +QString getOSInformation(); diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index cc8fd9ca..a3a081fc 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -18,9 +18,9 @@ #include "SetupWizard.h" #include "MainWindow.h" #include "WebClient.h" +#include "ActivationNotifier.h" #include "SubscriptionManager.h" #include "EditionType.h" -#include "SubscriptionState.h" #include "QSynergyApplication.h" #include "QUtility.h" @@ -103,7 +103,7 @@ bool SetupWizard::validateCurrentPage() QMessageBox::StandardButton reply = QMessageBox::information( this, tr("Setup Synergy"), - tr("Would you like to use serial key to activate?"), + tr("Would you like to use your serial key instead?"), QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { @@ -127,15 +127,9 @@ bool SetupWizard::validateCurrentPage() } else { // create subscription file in profile directory - SubscriptionManager subscriptionManager; - bool r = subscriptionManager.activateSerial(m_pLineEditSerialKey->text(), m_Edition); - if (!r) { - message.setText(tr("An error occurred while trying to activate using a serial key. " - "Please contact the helpdesk, and provide the " - "following details.\n\n%1").arg(subscriptionManager.getLastError())); - message.exec(); - - return r; + SubscriptionManager subscriptionManager(this, m_MainWindow.appConfig(), m_Edition); + if (!subscriptionManager.activateSerial(m_pLineEditSerialKey->text())) { + return false; } m_pPluginPage->setEdition(m_Edition); @@ -206,21 +200,27 @@ void SetupWizard::accept() if (m_pRadioButtonActivate->isChecked()) { appConfig.setActivateEmail(m_pLineEditEmail->text()); - QString mac = getFirstMacAddress(); - QString hashSrc = m_pLineEditEmail->text() + mac; - QString hashResult = hash(hashSrc); - appConfig.setUserToken(hashResult); - appConfig.setEdition(m_Edition); + + notifyActivation("login:" + m_pLineEditEmail->text()); } if (m_pRadioButtonSubscription->isChecked()) { appConfig.setSerialKey(m_pLineEditSerialKey->text()); + + notifyActivation("serial:" + m_pLineEditSerialKey->text()); } + if (m_pRadioButtonSkip->isChecked()) + { + notifyActivation("skip:unknown"); + } + + appConfig.setEdition(m_Edition); m_MainWindow.setEdition(m_Edition); m_MainWindow.updateLocalFingerprint(); + appConfig.saveSettings(); settings.sync(); QWizard::accept(); @@ -242,9 +242,28 @@ void SetupWizard::reject() m_MainWindow.open(); } + // treat cancel as skip + CoreInterface coreInterface; + coreInterface.notifyActivation("skip:unknown"); + QWizard::reject(); } +void SetupWizard::notifyActivation(QString identity) +{ + 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); +} + void SetupWizard::on_m_pComboLanguage_currentIndexChanged(int index) { QString ietfCode = m_pComboLanguage->itemData(index).toString(); diff --git a/src/gui/src/SetupWizard.h b/src/gui/src/SetupWizard.h index 253ac97f..328de0ee 100644 --- a/src/gui/src/SetupWizard.h +++ b/src/gui/src/SetupWizard.h @@ -43,6 +43,7 @@ protected: void changeEvent(QEvent* event); void accept(); void reject(); + void notifyActivation(QString identity); private: MainWindow& m_MainWindow; diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index a0c10916..f3f55611 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -17,20 +17,30 @@ #include "SubscriptionManager.h" + #include "CoreInterface.h" #include "EditionType.h" -#include "SubscriptionState.h" +#include "AppConfig.h" #include +#include #include +#include +#include -SubscriptionManager::SubscriptionManager() +static const char purchaseURL[] = "https://synergy-project.org/account/"; + +SubscriptionManager::SubscriptionManager(QWidget* parent, AppConfig& appConfig, int& edition) : + m_pParent(parent), + m_AppConfig(appConfig), + m_Edition(edition) { } -bool SubscriptionManager::activateSerial(const QString& serial, int& edition) +bool SubscriptionManager::activateSerial(const QString& serial) { - edition = Unknown; + m_Edition = Unknown; + persistDirectory(); CoreInterface coreInterface; QString output; @@ -41,17 +51,19 @@ bool SubscriptionManager::activateSerial(const QString& serial, int& edition) catch (std::exception& e) { m_ErrorMessage = e.what(); + checkError(m_ErrorMessage); return false; } - edition = getEditionType(output); + checkOutput(output); return true; } -int SubscriptionManager::checkSubscription(int& edition) +bool SubscriptionManager::checkSubscription() { - edition = Unknown; + m_Edition = Unknown; + persistDirectory(); CoreInterface coreInterface; QString output; try @@ -61,24 +73,16 @@ int SubscriptionManager::checkSubscription(int& edition) catch (std::exception& e) { m_ErrorMessage = e.what(); - - if (m_ErrorMessage.contains("subscription has expired")) { - return kExpired; - } - - return kInvalid; + checkError(m_ErrorMessage); + return false; } - if (output.contains("subscription will expire soon")) { - return kExpiredSoon; - } + checkOutput(output); - edition = getEditionType(output); - - return kValid; + return true; } -bool SubscriptionManager::checkSubscriptionExist() +bool SubscriptionManager::fileExists() { CoreInterface coreInterface; QString subscriptionFilename = coreInterface.getSubscriptionFilename(); @@ -86,11 +90,78 @@ bool SubscriptionManager::checkSubscriptionExist() return QFile::exists(subscriptionFilename); } -int SubscriptionManager::getEditionType(QString& string) +void SubscriptionManager::checkError(QString& error) { - if (string.contains("full subscription valid")) { - return Pro; + if (error.contains("trial has expired")) { + QMessageBox::warning(m_pParent, tr("Subscription warning"), + tr("Your trial has expired. Click here to purchase").arg(purchaseURL)); + } + else { + QMessageBox::warning(m_pParent, tr("Subscription error"), + tr("An error occurred while trying to activate using a serial key. " + "Please contact the helpdesk, and provide the " + "following details.\n\n%1").arg(error)); + } +} + +void SubscriptionManager::checkOutput(QString& output) +{ + getEditionType(output); + checkExpiring(output); +} + +void SubscriptionManager::getEditionType(QString& output) +{ + if (output.contains("pro subscription valid")) { + m_Edition = Pro; + } + else if (output.contains("basic subscription valid")) { + m_Edition = Basic; + } + else if (output.contains("trial subscription valid")) { + m_Edition = Trial; + } +} + +void SubscriptionManager::checkExpiring(QString& output) +{ + if (output.contains("trial will end in") && shouldWarnExpiring()) { + QRegExp dayLeftRegex(".*trial will end in ([0-9]+) day.*"); + if (dayLeftRegex.exactMatch(output)) { + QString dayLeft = dayLeftRegex.cap(1); + + QMessageBox::warning(m_pParent, tr("Subscription warning"), + tr("Your trial will end in %1 %2. Click here to purchase") + .arg(dayLeft) + .arg(dayLeft == "1" ? "day" : "days") + .arg(purchaseURL)); + } + } +} + +bool SubscriptionManager::shouldWarnExpiring() +{ + // warn users about expiring subscription once a day + int lastExpiringWarningTime = m_AppConfig.lastExpiringWarningTime(); + QDateTime currentDateTime = QDateTime::currentDateTime(); + int currentTime = currentDateTime.toTime_t(); + const int secondPerDay = 60 * 60 * 24; + bool result = false; + if ((currentTime - lastExpiringWarningTime) > secondPerDay) { + result = true; + m_AppConfig.setLastExpiringWarningTime(currentTime); } - return Unknown; + return result; +} + +void SubscriptionManager::persistDirectory() +{ + CoreInterface coreInterface; + QString profileDir = coreInterface.getProfileDir(); + + QDir dir(profileDir); + if (!dir.exists()) { + dir.mkpath("."); + } } diff --git a/src/gui/src/SubscriptionManager.h b/src/gui/src/SubscriptionManager.h index 1af14ad6..59497352 100644 --- a/src/gui/src/SubscriptionManager.h +++ b/src/gui/src/SubscriptionManager.h @@ -19,19 +19,29 @@ #include +class AppConfig; + class SubscriptionManager : public QWidget { public: - SubscriptionManager(); + SubscriptionManager(QWidget* parent, AppConfig& appConfig, int& edition); - bool activateSerial(const QString& serial, int& edition); - int checkSubscription(int& edition); - bool checkSubscriptionExist(); + bool activateSerial(const QString& serial); + bool checkSubscription(); + bool fileExists(); QString getLastError(){ return m_ErrorMessage; } private: - int getEditionType(QString& string); + void checkError(QString& error); + void checkOutput(QString& output); + void getEditionType(QString& output); + void checkExpiring(QString& output); + bool shouldWarnExpiring(); + void persistDirectory(); private: QString m_ErrorMessage; + QWidget* m_pParent; + AppConfig& m_AppConfig; + int& m_Edition; }; diff --git a/src/gui/src/WebClient.cpp b/src/gui/src/WebClient.cpp index 6de6d9ca..451bf47f 100644 --- a/src/gui/src/WebClient.cpp +++ b/src/gui/src/WebClient.cpp @@ -95,5 +95,6 @@ QString WebClient::request( QStringList args("--login-auth"); // hash password in case it contains interesting chars. QString credentials(email + ":" + hash(password) + "\n"); + return m_CoreInterface.run(args, credentials); } diff --git a/src/lib/arch/unix/ArchSystemUnix.cpp b/src/lib/arch/unix/ArchSystemUnix.cpp index 112e15cb..07cbf6c4 100644 --- a/src/lib/arch/unix/ArchSystemUnix.cpp +++ b/src/lib/arch/unix/ArchSystemUnix.cpp @@ -44,8 +44,6 @@ ArchSystemUnix::getOSName() const msg += info.sysname; msg += " "; msg += info.release; - msg += " "; - msg += info.version; return msg; } #endif diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp index 99a2bb43..689295b6 100644 --- a/src/lib/base/String.cpp +++ b/src/lib/base/String.cpp @@ -224,6 +224,28 @@ stringToSizeType(String string) return value; } +std::vector +splitString(String string, const char c) +{ + std::vector results; + + size_t head = 0; + size_t separator = string.find(c); + while (separator != String::npos) { + if (head!=separator) { + results.push_back(string.substr(head, separator - head)); + } + head = separator + 1; + separator = string.find(c, head); + } + + if (head < string.size()) { + results.push_back(string.substr(head, string.size() - head)); + } + + return results; +} + // // CaselessCmp // diff --git a/src/lib/base/String.h b/src/lib/base/String.h index de34ad0a..df4307a6 100644 --- a/src/lib/base/String.h +++ b/src/lib/base/String.h @@ -22,6 +22,7 @@ #include "common/stdstring.h" #include +#include // use standard C++ string class for our string class typedef std::string String; @@ -100,6 +101,12 @@ Convert an a \c string to an size type */ size_t stringToSizeType(String string); +//! Split a string into substrings +/*! +Split a \c string that separated by a \c c into substrings +*/ +std::vector splitString(String string, const char c); + //! Case-insensitive comparisons /*! This class provides case-insensitve comparison functions. diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 3580ab5a..7220c583 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -225,6 +225,10 @@ ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv) args.m_checkSubscription = true; return true; } + else if (isArg(i, argc, argv, NULL, "--notify-activation", 0)) { + args.m_notifyActivation = true; + return true; + } else { return false; } diff --git a/src/lib/synergy/SubscriptionKey.h b/src/lib/synergy/SubscriptionKey.h index 0d65a17c..28744fed 100644 --- a/src/lib/synergy/SubscriptionKey.h +++ b/src/lib/synergy/SubscriptionKey.h @@ -22,6 +22,8 @@ struct SubscriptionKey { String m_name; String m_type; + String m_email; + String m_company; int m_userLimit; int m_warnTime; int m_expireTime; diff --git a/src/lib/synergy/SubscriptionManager.cpp b/src/lib/synergy/SubscriptionManager.cpp index 2b51e17d..78207dc2 100644 --- a/src/lib/synergy/SubscriptionManager.cpp +++ b/src/lib/synergy/SubscriptionManager.cpp @@ -140,24 +140,30 @@ SubscriptionManager::parsePlainSerial(const String& plainText, SubscriptionKey& pos += 1; } - // e.g.: {v1;trial;Bob;1;1398297600;1398384000} - if ((parts.size() == 6) + // e.g.: {v1;trial;Bob;1;email;company name;1398297600;1398384000} + if ((parts.size() == 8) && (parts.at(0).find("v1") != String::npos)) { key.m_type = parts.at(1); key.m_name = parts.at(2); sscanf(parts.at(3).c_str(), "%d", &key.m_userLimit); - sscanf(parts.at(4).c_str(), "%d", &key.m_warnTime); - sscanf(parts.at(5).c_str(), "%d", &key.m_expireTime); + key.m_email = parts.at(4); + key.m_company = parts.at(5); + sscanf(parts.at(6).c_str(), "%d", &key.m_warnTime); + sscanf(parts.at(7).c_str(), "%d", &key.m_expireTime); - // TODO: use Arch time - if (time(0) > key.m_expireTime) { - throw XSubscription(synergy::string::sprintf( - "%s subscription has expired", - key.m_type.c_str())); - } - else if (time(0) > key.m_warnTime) { - LOG((CLOG_WARN "%s subscription will expire soon", - key.m_type.c_str())); + // only limit to trial version + if (key.m_type == "trial") { + if (time(0) > key.m_expireTime) { + throw XSubscription("trial has expired"); + } + else if (time(0) > key.m_warnTime) { + int secLeft = key.m_expireTime - static_cast(time(0)); + const int spd = 60 * 60 * 24; + int dayLeft = secLeft / spd + 1; + LOG((CLOG_NOTE "trial will end in %d %s", + dayLeft, + dayLeft == 1 ? "day" : "days")); + } } const char* userText = (key.m_userLimit == 1) ? "user" : "users"; diff --git a/src/lib/synergy/SubscriptionManager.h b/src/lib/synergy/SubscriptionManager.h index 1f08006b..fb52701b 100644 --- a/src/lib/synergy/SubscriptionManager.h +++ b/src/lib/synergy/SubscriptionManager.h @@ -37,12 +37,14 @@ public: private: FRIEND_TEST(SubscriptionTests, decode_invalidLength_throwException); - FRIEND_TEST(SubscriptionTests, decode_unrecognizedDigit_throwException); FRIEND_TEST(SubscriptionTests, decode_invalidSerial_outputPlainText); + FRIEND_TEST(SubscriptionTests, decode_unrecognizedDigit_throwException); FRIEND_TEST(SubscriptionTests, parsePlainSerial_noParity_throwException); FRIEND_TEST(SubscriptionTests, parsePlainSerial_invalidSerial_throwException); - FRIEND_TEST(SubscriptionTests, parsePlainSerial_validSerial_throwException); - FRIEND_TEST(SubscriptionTests, parsePlainSerial_expiredSerial_throwException); + FRIEND_TEST(SubscriptionTests, parsePlainSerial_validSerial_validSubscriptionKey); + FRIEND_TEST(SubscriptionTests, parsePlainSerial_expiredTrialSerial_throwException); + FRIEND_TEST(SubscriptionTests, parsePlainSerial_expiredBasicSerial_validSubscriptionKey); + FRIEND_TEST(SubscriptionTests, parsePlainSerial_validSerialWithoutCompany_validSubscriptionKey); private: String decode(const String& input); diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 5f01cb81..048e51dd 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -30,7 +30,7 @@ #include "platform/MSWindowsSession.h" #endif -#define JSON_URL "https://synergy-project.org/premium/json/" +#define JSON_URL "http://test.synergy-project.org/premium/json/" enum { kErrorOk, @@ -117,6 +117,9 @@ ToolApp::run(int argc, char** argv) return kExitSubscription; } } + else if (m_args.m_notifyActivation) { + notifyActivation(); + } else { throw XSynergy("Nothing to do"); } @@ -149,16 +152,23 @@ ToolApp::loginAuth() String credentials; std::cin >> credentials; - size_t separator = credentials.find(':'); - String email = credentials.substr(0, separator); - String password = credentials.substr(separator + 1, credentials.length()); + std::vector parts = synergy::string::splitString(credentials, ':'); + size_t count = parts.size(); - std::stringstream ss; - ss << JSON_URL << "auth/"; - ss << "?email=" << ARCH->internet().urlEncode(email); - ss << "&password=" << password; + if (count == 2 ) { + String email = parts[0]; + String password = parts[1]; - std::cout << ARCH->internet().get(ss.str()) << std::endl; + std::stringstream ss; + ss << JSON_URL << "auth/"; + ss << "?email=" << ARCH->internet().urlEncode(email); + ss << "&password=" << password; + + std::cout << ARCH->internet().get(ss.str()) << std::endl; + } + else { + throw XSynergy("Invalid credentials."); + } } void @@ -177,4 +187,49 @@ ToolApp::getPluginList() ss << "&password=" << password; std::cout << ARCH->internet().get(ss.str()) << std::endl; -} \ No newline at end of file +} + +void +ToolApp::notifyActivation() +{ + String info; + std::cin >> info; + + std::vector parts = synergy::string::splitString(info, ':'); + size_t count = parts.size(); + + if (count == 3 || count == 4) { + String action = parts[0]; + String identity = parts[1]; + String macHash = parts[2]; + String os; + + if (count == 4) { + os = parts[3]; + } + else { + os = ARCH->getOSName(); + } + + std::stringstream ss; + ss << JSON_URL << "notify/"; + ss << "?action=" << action; + ss << "&identity=" << ARCH->internet().urlEncode(identity); + ss << "&mac=" << ARCH->internet().urlEncode(macHash); + ss << "&os=" << ARCH->internet().urlEncode(ARCH->getOSName()); + ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName()); + + try { + std::cout << ARCH->internet().get(ss.str()) << std::endl; + } + catch (std::exception& e) { + LOG((CLOG_NOTE "An error occurred during notification: %s\n", e.what())); + } + catch (...) { + LOG((CLOG_NOTE "An unknown error occurred during notification.\n")); + } + } + else { + LOG((CLOG_NOTE "notification failed")); + } +} diff --git a/src/lib/synergy/ToolApp.h b/src/lib/synergy/ToolApp.h index 08947c6e..7f0b6ea7 100644 --- a/src/lib/synergy/ToolApp.h +++ b/src/lib/synergy/ToolApp.h @@ -30,6 +30,7 @@ public: private: void loginAuth(); void getPluginList(); + void notifyActivation(); private: ToolArgs m_args; diff --git a/src/lib/synergy/ToolArgs.cpp b/src/lib/synergy/ToolArgs.cpp index 0fd68a92..23439317 100644 --- a/src/lib/synergy/ToolArgs.cpp +++ b/src/lib/synergy/ToolArgs.cpp @@ -27,6 +27,7 @@ ToolArgs::ToolArgs() : m_getArch(false), m_getSubscriptionFilename(false), m_checkSubscription(false), + m_notifyActivation(false), m_subscriptionSerial() { } diff --git a/src/lib/synergy/ToolArgs.h b/src/lib/synergy/ToolArgs.h index ffbf932e..fb0b3e78 100644 --- a/src/lib/synergy/ToolArgs.h +++ b/src/lib/synergy/ToolArgs.h @@ -33,5 +33,6 @@ public: bool m_getArch; bool m_getSubscriptionFilename; bool m_checkSubscription; + bool m_notifyActivation; String m_subscriptionSerial; }; diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp index 6381cac5..f34e917a 100644 --- a/src/test/unittests/base/StringTests.cpp +++ b/src/test/unittests/base/StringTests.cpp @@ -21,7 +21,7 @@ using namespace synergy; -TEST(StringTests, format) +TEST(StringTests, format_formatWithArguments_formatedString) { const char* format = "%%%{1}=%{2}"; const char* arg1 = "answer"; @@ -32,7 +32,7 @@ TEST(StringTests, format) EXPECT_EQ("%answer=42", result); } -TEST(StringTests, findReplaceAll) +TEST(StringTests, findReplaceAll_inputString_replacedString) { String subject = "foobar"; String find = "bar"; @@ -43,7 +43,7 @@ TEST(StringTests, findReplaceAll) EXPECT_EQ("foobaz", subject); } -TEST(StringTests, sprintf) +TEST(StringTests, sprintf_formatWithArgument_formatedString) { const char* format = "%s=%d"; const char* arg1 = "answer"; @@ -54,7 +54,7 @@ TEST(StringTests, sprintf) EXPECT_EQ("answer=42", result); } -TEST(StringTests, toHex) +TEST(StringTests, toHex_plaintext_hexString) { String subject = "foobar"; int width = 2; @@ -64,7 +64,7 @@ TEST(StringTests, toHex) EXPECT_EQ("666f6f626172", subject); } -TEST(StringTests, uppercase) +TEST(StringTests, uppercase_lowercaseInput_uppercaseOutput) { String subject = "12foo3BaR"; @@ -73,7 +73,7 @@ TEST(StringTests, uppercase) EXPECT_EQ("12FOO3BAR", subject); } -TEST(StringTests, removeChar) +TEST(StringTests, removeChar_inputString_removeAllSpecifiedCharactors) { String subject = "foobar"; const char c = 'o'; @@ -83,7 +83,7 @@ TEST(StringTests, removeChar) EXPECT_EQ("fbar", subject); } -TEST(StringTests, intToString) +TEST(StringTests, intToString_inputInt_outputString) { size_t value = 123; @@ -92,7 +92,7 @@ TEST(StringTests, intToString) EXPECT_EQ("123", number); } -TEST(StringTests, stringToUint) +TEST(StringTests, stringToUint_inputString_outputInt) { String number = "123"; @@ -100,3 +100,78 @@ TEST(StringTests, stringToUint) EXPECT_EQ(123, value); } + +TEST(StringTests, splitString_twoSeparator_returnThreeParts) +{ + String string = "stub1:stub2:stub3"; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(3, results.size()); + EXPECT_EQ("stub1", results[0]); + EXPECT_EQ("stub2", results[1]); + EXPECT_EQ("stub3", results[2]); +} + +TEST(StringTests, splitString_oneSeparator_returnTwoParts) +{ + String string = "stub1:stub2"; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(2, results.size()); + EXPECT_EQ("stub1", results[0]); + EXPECT_EQ("stub2", results[1]); +} + +TEST(StringTests, splitString_noSeparator_returnOriginalString) +{ + String string = "stub1"; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(1, results.size()); + EXPECT_EQ("stub1", results[0]); +} + +TEST(StringTests, splitString_emptyString_returnEmptyVector) +{ + String string; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(0, results.size()); +} + +TEST(StringTests, splitString_tailSeparator_returnTwoParts) +{ + String string = "stub1:stub2:"; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(2, results.size()); + EXPECT_EQ("stub1", results[0]); + EXPECT_EQ("stub2", results[1]); +} + +TEST(StringTests, splitString_headSeparator_returnTwoParts) +{ + String string = ":stub1:stub2"; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(2, results.size()); + EXPECT_EQ("stub1", results[0]); + EXPECT_EQ("stub2", results[1]); +} + +TEST(StringTests, splitString_headAndTailSeparators_returnTwoParts) +{ + String string = ":stub1:stub2:"; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(2, results.size()); + EXPECT_EQ("stub1", results[0]); + EXPECT_EQ("stub2", results[1]); +} diff --git a/src/test/unittests/synergy/SubscriptionTests.cpp b/src/test/unittests/synergy/SubscriptionTests.cpp index 3e2c9dc9..eeda3e7b 100644 --- a/src/test/unittests/synergy/SubscriptionTests.cpp +++ b/src/test/unittests/synergy/SubscriptionTests.cpp @@ -54,21 +54,41 @@ TEST(SubscriptionTests, parsePlainSerial_invalidSerial_throwException) EXPECT_THROW(subscriptionManager.parsePlainSerial(painText, key), XSubscription); } -TEST(SubscriptionTests, parsePlainSerial_validSerial_throwException) +TEST(SubscriptionTests, parsePlainSerial_validSerial_validSubscriptionKey) { + // valid until 2 March 2049 SubscriptionManager subscriptionManager; - String painText("{v1;trial;Bob;1;1498297600;1498384000}"); + String painText("{v1;trial;Bob;1;a@a.a;mock company;2147483647;2147483647}"); SubscriptionKey key; subscriptionManager.parsePlainSerial(painText, key); EXPECT_EQ("trial", key.m_type); EXPECT_EQ("Bob", key.m_name); EXPECT_EQ(1, key.m_userLimit); - EXPECT_EQ(1498297600, key.m_warnTime); - EXPECT_EQ(1498384000, key.m_expireTime); + EXPECT_EQ("a@a.a", key.m_email); + EXPECT_EQ("mock company", key.m_company); + EXPECT_EQ(2147483647, key.m_warnTime); + EXPECT_EQ(2147483647, key.m_expireTime); } -TEST(SubscriptionTests, parsePlainSerial_expiredSerial_throwException) +TEST(SubscriptionTests, parsePlainSerial_validSerialWithoutCompany_validSubscriptionKey) +{ + // valid until 2 March 2049 + SubscriptionManager subscriptionManager; + String painText("{v1;trial;Bob;1;a@a.a;;2147483647;2147483647}"); + SubscriptionKey key; + subscriptionManager.parsePlainSerial(painText, key); + + EXPECT_EQ("trial", key.m_type); + EXPECT_EQ("Bob", key.m_name); + EXPECT_EQ(1, key.m_userLimit); + EXPECT_EQ("a@a.a", key.m_email); + EXPECT_EQ("", key.m_company); + EXPECT_EQ(2147483647, key.m_warnTime); + EXPECT_EQ(2147483647, key.m_expireTime); +} + +TEST(SubscriptionTests, parsePlainSerial_expiredTrialSerial_throwException) { SubscriptionManager subscriptionManager; String painText("{v1;trial;Bob;1;1398297600;1398384000}"); @@ -76,3 +96,19 @@ TEST(SubscriptionTests, parsePlainSerial_expiredSerial_throwException) EXPECT_THROW(subscriptionManager.parsePlainSerial(painText, key), XSubscription); } + +TEST(SubscriptionTests, parsePlainSerial_expiredBasicSerial_validSubscriptionKey) +{ + SubscriptionManager subscriptionManager; + String painText("{v1;basic;Bob;1;a@a.a;mock company;1398297600;1398384000}"); + SubscriptionKey key; + subscriptionManager.parsePlainSerial(painText, key); + + EXPECT_EQ("basic", key.m_type); + EXPECT_EQ("Bob", key.m_name); + EXPECT_EQ(1, key.m_userLimit); + EXPECT_EQ("a@a.a", key.m_email); + EXPECT_EQ("mock company", key.m_company); + EXPECT_EQ(1398297600, key.m_warnTime); + EXPECT_EQ(1398384000, key.m_expireTime); +}