Merge branch 'jerry-sandbox2'

This commit is contained in:
Jerry (Xinyu Hou) 2015-11-23 09:54:05 -08:00
commit fbd2c1413e
34 changed files with 560 additions and 170 deletions

View File

@ -62,7 +62,8 @@ SOURCES += src/main.cpp \
src/Plugin.cpp \ src/Plugin.cpp \
src/WebClient.cpp \ src/WebClient.cpp \
../lib/common/PluginVersion.cpp \ ../lib/common/PluginVersion.cpp \
src/SubscriptionManager.cpp src/SubscriptionManager.cpp \
src/ActivationNotifier.cpp
HEADERS += src/MainWindow.h \ HEADERS += src/MainWindow.h \
src/AboutDialog.h \ src/AboutDialog.h \
src/ServerConfig.h \ src/ServerConfig.h \
@ -110,7 +111,7 @@ HEADERS += src/MainWindow.h \
src/WebClient.h \ src/WebClient.h \
../lib/common/PluginVersion.h \ ../lib/common/PluginVersion.h \
src/SubscriptionManager.h \ src/SubscriptionManager.h \
src/SubscriptionState.h src/ActivationNotifier.h
RESOURCES += res/Synergy.qrc RESOURCES += res/Synergy.qrc
RC_FILE = res/win/Synergy.rc RC_FILE = res/win/Synergy.rc
macx { macx {

View File

@ -15,14 +15,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef SUBSCRIPTIONSTATE_H #include "ActivationNotifier.h"
#define SUBSCRIPTIONSTATE_H
enum qSubscriptionState { #include "CoreInterface.h"
kValid,
kInvalid,
kExpiredSoon,
kExpired
};
#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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef ACTIVATIONNOTIFIER_H
#define ACTIVATIONNOTIFIER_H
#include <QObject>
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

View File

@ -58,7 +58,8 @@ AppConfig::AppConfig(QSettings* settings) :
m_ElevateMode(false), m_ElevateMode(false),
m_AutoConfigPrompted(false), m_AutoConfigPrompted(false),
m_CryptoEnabled(false), m_CryptoEnabled(false),
m_AutoHide(false) m_AutoHide(false),
m_LastExpiringWarningTime(0)
{ {
Q_ASSERT(m_pSettings); Q_ASSERT(m_pSettings);
@ -129,10 +130,10 @@ void AppConfig::loadSettings()
m_AutoConfigPrompted = settings().value("autoConfigPrompted", false).toBool(); m_AutoConfigPrompted = settings().value("autoConfigPrompted", false).toBool();
m_Edition = settings().value("edition", Unknown).toInt(); m_Edition = settings().value("edition", Unknown).toInt();
m_ActivateEmail = settings().value("activateEmail", "").toString(); m_ActivateEmail = settings().value("activateEmail", "").toString();
m_UserToken = settings().value("userToken", "").toString();
m_CryptoEnabled = settings().value("cryptoEnabled", false).toBool(); m_CryptoEnabled = settings().value("cryptoEnabled", false).toBool();
m_AutoHide = settings().value("autoHide", false).toBool(); m_AutoHide = settings().value("autoHide", false).toBool();
m_Serialkey = settings().value("serialKey", "").toString(); m_Serialkey = settings().value("serialKey", "").toString();
m_LastExpiringWarningTime = settings().value("lastExpiringWarningTime", 0).toInt();
} }
void AppConfig::saveSettings() void AppConfig::saveSettings()
@ -151,10 +152,10 @@ void AppConfig::saveSettings()
settings().setValue("autoConfigPrompted", m_AutoConfigPrompted); settings().setValue("autoConfigPrompted", m_AutoConfigPrompted);
settings().setValue("edition", m_Edition); settings().setValue("edition", m_Edition);
settings().setValue("activateEmail", m_ActivateEmail); settings().setValue("activateEmail", m_ActivateEmail);
settings().setValue("userToken", m_UserToken);
settings().setValue("cryptoEnabled", m_CryptoEnabled); settings().setValue("cryptoEnabled", m_CryptoEnabled);
settings().setValue("autoHide", m_AutoHide); settings().setValue("autoHide", m_AutoHide);
settings().setValue("serialKey", m_Serialkey); settings().setValue("serialKey", m_Serialkey);
settings().setValue("lastExpiringWarningTime", m_LastExpiringWarningTime);
} }
void AppConfig::setAutoConfig(bool autoConfig) void AppConfig::setAutoConfig(bool autoConfig)

View File

@ -76,10 +76,10 @@ class AppConfig
int edition() { return m_Edition; } int edition() { return m_Edition; }
void setActivateEmail(QString e) { m_ActivateEmail = e; } void setActivateEmail(QString e) { m_ActivateEmail = e; }
QString activateEmail() { return m_ActivateEmail; } QString activateEmail() { return m_ActivateEmail; }
void setUserToken(QString t) { m_UserToken = t; }
QString userToken() { return m_UserToken; }
void setSerialKey(QString serial) { m_Serialkey = serial; } void setSerialKey(QString serial) { m_Serialkey = serial; }
QString serialKey() { return m_Serialkey; } 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 synergysName() const { return m_SynergysName; }
QString synergycName() const { return m_SynergycName; } QString synergycName() const { return m_SynergycName; }
@ -95,6 +95,8 @@ class AppConfig
void setAutoHide(bool b) { m_AutoHide = b; } void setAutoHide(bool b) { m_AutoHide = b; }
bool getAutoHide() { return m_AutoHide; } bool getAutoHide() { return m_AutoHide; }
void saveSettings();
protected: protected:
QSettings& settings() { return *m_pSettings; } QSettings& settings() { return *m_pSettings; }
void setScreenName(const QString& s) { m_ScreenName = s; } void setScreenName(const QString& s) { m_ScreenName = s; }
@ -109,7 +111,6 @@ class AppConfig
void setElevateMode(bool b) { m_ElevateMode = b; } void setElevateMode(bool b) { m_ElevateMode = b; }
void loadSettings(); void loadSettings();
void saveSettings();
private: private:
QSettings* m_pSettings; QSettings* m_pSettings;
@ -128,10 +129,10 @@ class AppConfig
bool m_AutoConfigPrompted; bool m_AutoConfigPrompted;
int m_Edition; int m_Edition;
QString m_ActivateEmail; QString m_ActivateEmail;
QString m_UserToken;
bool m_CryptoEnabled; bool m_CryptoEnabled;
bool m_AutoHide; bool m_AutoHide;
QString m_Serialkey; QString m_Serialkey;
int m_LastExpiringWarningTime;
static const char m_SynergysName[]; static const char m_SynergysName[];
static const char m_SynergycName[]; static const char m_SynergycName[];

View File

@ -18,17 +18,46 @@
#include "CommandProcess.h" #include "CommandProcess.h"
#include <QProcess> #include <QProcess>
#include <stdexcept>
CommandProcess::CommandProcess(QString cmd, QStringList arguments) : CommandProcess::CommandProcess(QString cmd, QStringList arguments, QString input) :
m_Command(cmd), m_Command(cmd),
m_Arguments(arguments) m_Arguments(arguments),
m_Input(input)
{ {
} }
void CommandProcess::run() QString CommandProcess::run()
{ {
QProcess process; QProcess process;
process.setReadChannel(QProcess::StandardOutput);
process.start(m_Command, m_Arguments); 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(); emit finished();
return output;
} }

View File

@ -25,17 +25,18 @@ class CommandProcess : public QObject
Q_OBJECT Q_OBJECT
public: public:
CommandProcess(QString cmd, QStringList arguments); CommandProcess(QString cmd, QStringList arguments, QString input = "");
signals: signals:
void finished(); void finished();
public slots: public slots:
void run(); QString run();
private: private:
QString m_Command; QString m_Command;
QStringList m_Arguments; QStringList m_Arguments;
QString m_Input;
}; };
#endif // COMMANDTHREAD_H #endif // COMMANDTHREAD_H

View File

@ -17,6 +17,9 @@
#include "CoreInterface.h" #include "CoreInterface.h"
#include "CommandProcess.h"
#include "QUtility.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QProcess> #include <QProcess>
#include <stdexcept> #include <stdexcept>
@ -71,39 +74,26 @@ QString CoreInterface::checkSubscription()
return run(args); 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 CoreInterface::run(const QStringList& args, const QString& input)
{ {
QString program( QString program(
QCoreApplication::applicationDirPath() QCoreApplication::applicationDirPath()
+ "/" + kCoreBinary); + "/" + kCoreBinary);
QProcess process; CommandProcess commandProcess(program, args, input);
process.setReadChannel(QProcess::StandardOutput); return commandProcess.run();
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;
} }

View File

@ -31,5 +31,6 @@ public:
QString getSubscriptionFilename(); QString getSubscriptionFilename();
QString activateSerial(const QString& serial); QString activateSerial(const QString& serial);
QString checkSubscription(); QString checkSubscription();
QString notifyActivation(const QString& identity);
QString run(const QStringList& args, const QString& input = ""); QString run(const QStringList& args, const QString& input = "");
}; };

View File

@ -21,6 +21,7 @@
enum qEditionType { enum qEditionType {
Basic, Basic,
Pro, Pro,
Trial,
Unknown Unknown
}; };

View File

@ -32,7 +32,6 @@
#include "DataDownloader.h" #include "DataDownloader.h"
#include "CommandProcess.h" #include "CommandProcess.h"
#include "SubscriptionManager.h" #include "SubscriptionManager.h"
#include "SubscriptionState.h"
#include "EditionType.h" #include "EditionType.h"
#include "QUtility.h" #include "QUtility.h"
#include "ProcessorArch.h" #include "ProcessorArch.h"
@ -135,7 +134,7 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) :
m_pComboServerList->hide(); m_pComboServerList->hide();
updateEdition(); setEdition(m_AppConfig.edition());
m_pLabelPadlock->hide(); m_pLabelPadlock->hide();
@ -698,22 +697,19 @@ QString MainWindow::appPath(const QString& name)
bool MainWindow::serverArgs(QStringList& args, QString& app) bool MainWindow::serverArgs(QStringList& args, QString& app)
{ {
SubscriptionManager subscriptionManager; int edition;
if (subscriptionManager.checkSubscriptionExist()) SubscriptionManager subscriptionManager(this, appConfig(), edition);
if (subscriptionManager.fileExists())
{ {
int edition; if (!subscriptionManager.checkSubscription()) {
int state = subscriptionManager.checkSubscription(edition);
if (state == kInvalid) {
return false; return false;
} }
else if (state == kExpired) { else {
QMessageBox::warning(this, tr("Subscription is expired"), setEdition(edition);
tr("Your subscription is expired. Please purchase."));
return false;
} }
} }
app = appPath(appConfig().synergysName()); app = appPath(appConfig().synergysName());
if (!QFile::exists(app)) if (!QFile::exists(app))
@ -962,7 +958,7 @@ void MainWindow::changeEvent(QEvent* event)
retranslateUi(this); retranslateUi(this);
retranslateMenuBar(); retranslateMenuBar();
updateEdition(); setEdition(m_AppConfig.edition());
break; break;
} }
@ -1011,6 +1007,9 @@ void MainWindow::setEdition(int type)
else if (type == Pro) { else if (type == Pro) {
title = "Synergy Pro"; title = "Synergy Pro";
} }
else if (type == Trial) {
title = "Synergy Trial";
}
else { else {
title = "Synergy (UNREGISTERED)"; title = "Synergy (UNREGISTERED)";
} }
@ -1305,20 +1304,6 @@ void MainWindow::promptAutoConfig()
m_AppConfig.setAutoConfigPrompted(true); 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 ) void MainWindow::on_m_pComboServerList_currentIndexChanged(QString )
{ {
if (m_pComboServerList->count() != 0) { if (m_pComboServerList->count() != 0) {

View File

@ -172,7 +172,6 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase
bool isBonjourRunning(); bool isBonjourRunning();
void downloadBonjour(); void downloadBonjour();
void promptAutoConfig(); void promptAutoConfig();
void updateEdition();
QString getProfileRootForArg(); QString getProfileRootForArg();
void checkConnected(const QString& line); void checkConnected(const QString& line);
void checkFingerprint(const QString& line); void checkFingerprint(const QString& line);

View File

@ -18,7 +18,6 @@
#include "PluginManager.h" #include "PluginManager.h"
#include "CoreInterface.h" #include "CoreInterface.h"
#include "CommandProcess.h"
#include "DataDownloader.h" #include "DataDownloader.h"
#include "QUtility.h" #include "QUtility.h"
#include "ProcessorArch.h" #include "ProcessorArch.h"

View File

@ -64,8 +64,7 @@ void PluginWizardPage::initializePage()
{ {
QWizardPage::initializePage(); QWizardPage::initializePage();
if (m_Edition == Unknown || if (m_Edition != Pro) {
m_Edition == Basic) {
updateStatus(tr("Setup complete.")); updateStatus(tr("Setup complete."));
showFinished(); showFinished();
return; return;

View File

@ -18,6 +18,7 @@
#include "QUtility.h" #include "QUtility.h"
#include "ProcessorArch.h" #include "ProcessorArch.h"
#include "CommandProcess.h"
#if defined(Q_OS_LINUX) #if defined(Q_OS_LINUX)
#include <QProcess> #include <QProcess>
@ -90,3 +91,23 @@ qProcessorArch getProcessorArch()
return kProcessorArchUnknown; 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;
}

View File

@ -28,3 +28,4 @@ void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData);
QString hash(const QString& string); QString hash(const QString& string);
QString getFirstMacAddress(); QString getFirstMacAddress();
qProcessorArch getProcessorArch(); qProcessorArch getProcessorArch();
QString getOSInformation();

View File

@ -18,9 +18,9 @@
#include "SetupWizard.h" #include "SetupWizard.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "WebClient.h" #include "WebClient.h"
#include "ActivationNotifier.h"
#include "SubscriptionManager.h" #include "SubscriptionManager.h"
#include "EditionType.h" #include "EditionType.h"
#include "SubscriptionState.h"
#include "QSynergyApplication.h" #include "QSynergyApplication.h"
#include "QUtility.h" #include "QUtility.h"
@ -103,7 +103,7 @@ bool SetupWizard::validateCurrentPage()
QMessageBox::StandardButton reply = QMessageBox::StandardButton reply =
QMessageBox::information( QMessageBox::information(
this, tr("Setup Synergy"), 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); QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) { if (reply == QMessageBox::Yes) {
@ -127,15 +127,9 @@ bool SetupWizard::validateCurrentPage()
} }
else { else {
// create subscription file in profile directory // create subscription file in profile directory
SubscriptionManager subscriptionManager; SubscriptionManager subscriptionManager(this, m_MainWindow.appConfig(), m_Edition);
bool r = subscriptionManager.activateSerial(m_pLineEditSerialKey->text(), m_Edition); if (!subscriptionManager.activateSerial(m_pLineEditSerialKey->text())) {
if (!r) { return false;
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;
} }
m_pPluginPage->setEdition(m_Edition); m_pPluginPage->setEdition(m_Edition);
@ -206,21 +200,27 @@ void SetupWizard::accept()
if (m_pRadioButtonActivate->isChecked()) { if (m_pRadioButtonActivate->isChecked()) {
appConfig.setActivateEmail(m_pLineEditEmail->text()); appConfig.setActivateEmail(m_pLineEditEmail->text());
QString mac = getFirstMacAddress();
QString hashSrc = m_pLineEditEmail->text() + mac; notifyActivation("login:" + m_pLineEditEmail->text());
QString hashResult = hash(hashSrc);
appConfig.setUserToken(hashResult);
appConfig.setEdition(m_Edition);
} }
if (m_pRadioButtonSubscription->isChecked()) if (m_pRadioButtonSubscription->isChecked())
{ {
appConfig.setSerialKey(m_pLineEditSerialKey->text()); 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.setEdition(m_Edition);
m_MainWindow.updateLocalFingerprint(); m_MainWindow.updateLocalFingerprint();
appConfig.saveSettings();
settings.sync(); settings.sync();
QWizard::accept(); QWizard::accept();
@ -242,9 +242,28 @@ void SetupWizard::reject()
m_MainWindow.open(); m_MainWindow.open();
} }
// treat cancel as skip
CoreInterface coreInterface;
coreInterface.notifyActivation("skip:unknown");
QWizard::reject(); 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) void SetupWizard::on_m_pComboLanguage_currentIndexChanged(int index)
{ {
QString ietfCode = m_pComboLanguage->itemData(index).toString(); QString ietfCode = m_pComboLanguage->itemData(index).toString();

View File

@ -43,6 +43,7 @@ protected:
void changeEvent(QEvent* event); void changeEvent(QEvent* event);
void accept(); void accept();
void reject(); void reject();
void notifyActivation(QString identity);
private: private:
MainWindow& m_MainWindow; MainWindow& m_MainWindow;

View File

@ -17,20 +17,30 @@
#include "SubscriptionManager.h" #include "SubscriptionManager.h"
#include "CoreInterface.h" #include "CoreInterface.h"
#include "EditionType.h" #include "EditionType.h"
#include "SubscriptionState.h" #include "AppConfig.h"
#include <QMessageBox> #include <QMessageBox>
#include <QDir>
#include <QFile> #include <QFile>
#include <QDateTime>
#include <QDate>
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; CoreInterface coreInterface;
QString output; QString output;
@ -41,17 +51,19 @@ bool SubscriptionManager::activateSerial(const QString& serial, int& edition)
catch (std::exception& e) catch (std::exception& e)
{ {
m_ErrorMessage = e.what(); m_ErrorMessage = e.what();
checkError(m_ErrorMessage);
return false; return false;
} }
edition = getEditionType(output); checkOutput(output);
return true; return true;
} }
int SubscriptionManager::checkSubscription(int& edition) bool SubscriptionManager::checkSubscription()
{ {
edition = Unknown; m_Edition = Unknown;
persistDirectory();
CoreInterface coreInterface; CoreInterface coreInterface;
QString output; QString output;
try try
@ -61,24 +73,16 @@ int SubscriptionManager::checkSubscription(int& edition)
catch (std::exception& e) catch (std::exception& e)
{ {
m_ErrorMessage = e.what(); m_ErrorMessage = e.what();
checkError(m_ErrorMessage);
if (m_ErrorMessage.contains("subscription has expired")) { return false;
return kExpired;
}
return kInvalid;
} }
if (output.contains("subscription will expire soon")) { checkOutput(output);
return kExpiredSoon;
}
edition = getEditionType(output); return true;
return kValid;
} }
bool SubscriptionManager::checkSubscriptionExist() bool SubscriptionManager::fileExists()
{ {
CoreInterface coreInterface; CoreInterface coreInterface;
QString subscriptionFilename = coreInterface.getSubscriptionFilename(); QString subscriptionFilename = coreInterface.getSubscriptionFilename();
@ -86,11 +90,78 @@ bool SubscriptionManager::checkSubscriptionExist()
return QFile::exists(subscriptionFilename); return QFile::exists(subscriptionFilename);
} }
int SubscriptionManager::getEditionType(QString& string) void SubscriptionManager::checkError(QString& error)
{ {
if (string.contains("full subscription valid")) { if (error.contains("trial has expired")) {
return Pro; QMessageBox::warning(m_pParent, tr("Subscription warning"),
tr("Your trial has expired. Click <a href='%1'>here</a> 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 <a href='%3'>here</a> 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(".");
}
} }

View File

@ -19,19 +19,29 @@
#include <QWidget> #include <QWidget>
class AppConfig;
class SubscriptionManager : public QWidget class SubscriptionManager : public QWidget
{ {
public: public:
SubscriptionManager(); SubscriptionManager(QWidget* parent, AppConfig& appConfig, int& edition);
bool activateSerial(const QString& serial, int& edition); bool activateSerial(const QString& serial);
int checkSubscription(int& edition); bool checkSubscription();
bool checkSubscriptionExist(); bool fileExists();
QString getLastError(){ return m_ErrorMessage; } QString getLastError(){ return m_ErrorMessage; }
private: 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: private:
QString m_ErrorMessage; QString m_ErrorMessage;
QWidget* m_pParent;
AppConfig& m_AppConfig;
int& m_Edition;
}; };

View File

@ -95,5 +95,6 @@ QString WebClient::request(
QStringList args("--login-auth"); QStringList args("--login-auth");
// hash password in case it contains interesting chars. // hash password in case it contains interesting chars.
QString credentials(email + ":" + hash(password) + "\n"); QString credentials(email + ":" + hash(password) + "\n");
return m_CoreInterface.run(args, credentials); return m_CoreInterface.run(args, credentials);
} }

View File

@ -44,8 +44,6 @@ ArchSystemUnix::getOSName() const
msg += info.sysname; msg += info.sysname;
msg += " "; msg += " ";
msg += info.release; msg += info.release;
msg += " ";
msg += info.version;
return msg; return msg;
} }
#endif #endif

View File

@ -224,6 +224,28 @@ stringToSizeType(String string)
return value; return value;
} }
std::vector<String>
splitString(String string, const char c)
{
std::vector<String> 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 // CaselessCmp
// //

View File

@ -22,6 +22,7 @@
#include "common/stdstring.h" #include "common/stdstring.h"
#include <stdarg.h> #include <stdarg.h>
#include <vector>
// use standard C++ string class for our string class // use standard C++ string class for our string class
typedef std::string String; typedef std::string String;
@ -100,6 +101,12 @@ Convert an a \c string to an size type
*/ */
size_t stringToSizeType(String string); size_t stringToSizeType(String string);
//! Split a string into substrings
/*!
Split a \c string that separated by a \c c into substrings
*/
std::vector<String> splitString(String string, const char c);
//! Case-insensitive comparisons //! Case-insensitive comparisons
/*! /*!
This class provides case-insensitve comparison functions. This class provides case-insensitve comparison functions.

View File

@ -225,6 +225,10 @@ ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv)
args.m_checkSubscription = true; args.m_checkSubscription = true;
return true; return true;
} }
else if (isArg(i, argc, argv, NULL, "--notify-activation", 0)) {
args.m_notifyActivation = true;
return true;
}
else { else {
return false; return false;
} }

View File

@ -22,6 +22,8 @@
struct SubscriptionKey { struct SubscriptionKey {
String m_name; String m_name;
String m_type; String m_type;
String m_email;
String m_company;
int m_userLimit; int m_userLimit;
int m_warnTime; int m_warnTime;
int m_expireTime; int m_expireTime;

View File

@ -140,24 +140,30 @@ SubscriptionManager::parsePlainSerial(const String& plainText, SubscriptionKey&
pos += 1; pos += 1;
} }
// e.g.: {v1;trial;Bob;1;1398297600;1398384000} // e.g.: {v1;trial;Bob;1;email;company name;1398297600;1398384000}
if ((parts.size() == 6) if ((parts.size() == 8)
&& (parts.at(0).find("v1") != String::npos)) { && (parts.at(0).find("v1") != String::npos)) {
key.m_type = parts.at(1); key.m_type = parts.at(1);
key.m_name = parts.at(2); key.m_name = parts.at(2);
sscanf(parts.at(3).c_str(), "%d", &key.m_userLimit); sscanf(parts.at(3).c_str(), "%d", &key.m_userLimit);
sscanf(parts.at(4).c_str(), "%d", &key.m_warnTime); key.m_email = parts.at(4);
sscanf(parts.at(5).c_str(), "%d", &key.m_expireTime); 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 // only limit to trial version
if (time(0) > key.m_expireTime) { if (key.m_type == "trial") {
throw XSubscription(synergy::string::sprintf( if (time(0) > key.m_expireTime) {
"%s subscription has expired", throw XSubscription("trial has expired");
key.m_type.c_str())); }
} else if (time(0) > key.m_warnTime) {
else if (time(0) > key.m_warnTime) { int secLeft = key.m_expireTime - static_cast<int>(time(0));
LOG((CLOG_WARN "%s subscription will expire soon", const int spd = 60 * 60 * 24;
key.m_type.c_str())); 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"; const char* userText = (key.m_userLimit == 1) ? "user" : "users";

View File

@ -37,12 +37,14 @@ public:
private: private:
FRIEND_TEST(SubscriptionTests, decode_invalidLength_throwException); FRIEND_TEST(SubscriptionTests, decode_invalidLength_throwException);
FRIEND_TEST(SubscriptionTests, decode_unrecognizedDigit_throwException);
FRIEND_TEST(SubscriptionTests, decode_invalidSerial_outputPlainText); FRIEND_TEST(SubscriptionTests, decode_invalidSerial_outputPlainText);
FRIEND_TEST(SubscriptionTests, decode_unrecognizedDigit_throwException);
FRIEND_TEST(SubscriptionTests, parsePlainSerial_noParity_throwException); FRIEND_TEST(SubscriptionTests, parsePlainSerial_noParity_throwException);
FRIEND_TEST(SubscriptionTests, parsePlainSerial_invalidSerial_throwException); FRIEND_TEST(SubscriptionTests, parsePlainSerial_invalidSerial_throwException);
FRIEND_TEST(SubscriptionTests, parsePlainSerial_validSerial_throwException); FRIEND_TEST(SubscriptionTests, parsePlainSerial_validSerial_validSubscriptionKey);
FRIEND_TEST(SubscriptionTests, parsePlainSerial_expiredSerial_throwException); FRIEND_TEST(SubscriptionTests, parsePlainSerial_expiredTrialSerial_throwException);
FRIEND_TEST(SubscriptionTests, parsePlainSerial_expiredBasicSerial_validSubscriptionKey);
FRIEND_TEST(SubscriptionTests, parsePlainSerial_validSerialWithoutCompany_validSubscriptionKey);
private: private:
String decode(const String& input); String decode(const String& input);

View File

@ -30,7 +30,7 @@
#include "platform/MSWindowsSession.h" #include "platform/MSWindowsSession.h"
#endif #endif
#define JSON_URL "https://synergy-project.org/premium/json/" #define JSON_URL "http://test.synergy-project.org/premium/json/"
enum { enum {
kErrorOk, kErrorOk,
@ -117,6 +117,9 @@ ToolApp::run(int argc, char** argv)
return kExitSubscription; return kExitSubscription;
} }
} }
else if (m_args.m_notifyActivation) {
notifyActivation();
}
else { else {
throw XSynergy("Nothing to do"); throw XSynergy("Nothing to do");
} }
@ -149,16 +152,23 @@ ToolApp::loginAuth()
String credentials; String credentials;
std::cin >> credentials; std::cin >> credentials;
size_t separator = credentials.find(':'); std::vector<String> parts = synergy::string::splitString(credentials, ':');
String email = credentials.substr(0, separator); size_t count = parts.size();
String password = credentials.substr(separator + 1, credentials.length());
std::stringstream ss; if (count == 2 ) {
ss << JSON_URL << "auth/"; String email = parts[0];
ss << "?email=" << ARCH->internet().urlEncode(email); String password = parts[1];
ss << "&password=" << password;
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 void
@ -177,4 +187,49 @@ ToolApp::getPluginList()
ss << "&password=" << password; ss << "&password=" << password;
std::cout << ARCH->internet().get(ss.str()) << std::endl; std::cout << ARCH->internet().get(ss.str()) << std::endl;
} }
void
ToolApp::notifyActivation()
{
String info;
std::cin >> info;
std::vector<String> 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"));
}
}

View File

@ -30,6 +30,7 @@ public:
private: private:
void loginAuth(); void loginAuth();
void getPluginList(); void getPluginList();
void notifyActivation();
private: private:
ToolArgs m_args; ToolArgs m_args;

View File

@ -27,6 +27,7 @@ ToolArgs::ToolArgs() :
m_getArch(false), m_getArch(false),
m_getSubscriptionFilename(false), m_getSubscriptionFilename(false),
m_checkSubscription(false), m_checkSubscription(false),
m_notifyActivation(false),
m_subscriptionSerial() m_subscriptionSerial()
{ {
} }

View File

@ -33,5 +33,6 @@ public:
bool m_getArch; bool m_getArch;
bool m_getSubscriptionFilename; bool m_getSubscriptionFilename;
bool m_checkSubscription; bool m_checkSubscription;
bool m_notifyActivation;
String m_subscriptionSerial; String m_subscriptionSerial;
}; };

View File

@ -21,7 +21,7 @@
using namespace synergy; using namespace synergy;
TEST(StringTests, format) TEST(StringTests, format_formatWithArguments_formatedString)
{ {
const char* format = "%%%{1}=%{2}"; const char* format = "%%%{1}=%{2}";
const char* arg1 = "answer"; const char* arg1 = "answer";
@ -32,7 +32,7 @@ TEST(StringTests, format)
EXPECT_EQ("%answer=42", result); EXPECT_EQ("%answer=42", result);
} }
TEST(StringTests, findReplaceAll) TEST(StringTests, findReplaceAll_inputString_replacedString)
{ {
String subject = "foobar"; String subject = "foobar";
String find = "bar"; String find = "bar";
@ -43,7 +43,7 @@ TEST(StringTests, findReplaceAll)
EXPECT_EQ("foobaz", subject); EXPECT_EQ("foobaz", subject);
} }
TEST(StringTests, sprintf) TEST(StringTests, sprintf_formatWithArgument_formatedString)
{ {
const char* format = "%s=%d"; const char* format = "%s=%d";
const char* arg1 = "answer"; const char* arg1 = "answer";
@ -54,7 +54,7 @@ TEST(StringTests, sprintf)
EXPECT_EQ("answer=42", result); EXPECT_EQ("answer=42", result);
} }
TEST(StringTests, toHex) TEST(StringTests, toHex_plaintext_hexString)
{ {
String subject = "foobar"; String subject = "foobar";
int width = 2; int width = 2;
@ -64,7 +64,7 @@ TEST(StringTests, toHex)
EXPECT_EQ("666f6f626172", subject); EXPECT_EQ("666f6f626172", subject);
} }
TEST(StringTests, uppercase) TEST(StringTests, uppercase_lowercaseInput_uppercaseOutput)
{ {
String subject = "12foo3BaR"; String subject = "12foo3BaR";
@ -73,7 +73,7 @@ TEST(StringTests, uppercase)
EXPECT_EQ("12FOO3BAR", subject); EXPECT_EQ("12FOO3BAR", subject);
} }
TEST(StringTests, removeChar) TEST(StringTests, removeChar_inputString_removeAllSpecifiedCharactors)
{ {
String subject = "foobar"; String subject = "foobar";
const char c = 'o'; const char c = 'o';
@ -83,7 +83,7 @@ TEST(StringTests, removeChar)
EXPECT_EQ("fbar", subject); EXPECT_EQ("fbar", subject);
} }
TEST(StringTests, intToString) TEST(StringTests, intToString_inputInt_outputString)
{ {
size_t value = 123; size_t value = 123;
@ -92,7 +92,7 @@ TEST(StringTests, intToString)
EXPECT_EQ("123", number); EXPECT_EQ("123", number);
} }
TEST(StringTests, stringToUint) TEST(StringTests, stringToUint_inputString_outputInt)
{ {
String number = "123"; String number = "123";
@ -100,3 +100,78 @@ TEST(StringTests, stringToUint)
EXPECT_EQ(123, value); EXPECT_EQ(123, value);
} }
TEST(StringTests, splitString_twoSeparator_returnThreeParts)
{
String string = "stub1:stub2:stub3";
std::vector<String> 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<String> 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<String> results = string::splitString(string, ':');
EXPECT_EQ(1, results.size());
EXPECT_EQ("stub1", results[0]);
}
TEST(StringTests, splitString_emptyString_returnEmptyVector)
{
String string;
std::vector<String> results = string::splitString(string, ':');
EXPECT_EQ(0, results.size());
}
TEST(StringTests, splitString_tailSeparator_returnTwoParts)
{
String string = "stub1:stub2:";
std::vector<String> 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<String> 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<String> results = string::splitString(string, ':');
EXPECT_EQ(2, results.size());
EXPECT_EQ("stub1", results[0]);
EXPECT_EQ("stub2", results[1]);
}

View File

@ -54,21 +54,41 @@ TEST(SubscriptionTests, parsePlainSerial_invalidSerial_throwException)
EXPECT_THROW(subscriptionManager.parsePlainSerial(painText, key), XSubscription); EXPECT_THROW(subscriptionManager.parsePlainSerial(painText, key), XSubscription);
} }
TEST(SubscriptionTests, parsePlainSerial_validSerial_throwException) TEST(SubscriptionTests, parsePlainSerial_validSerial_validSubscriptionKey)
{ {
// valid until 2 March 2049
SubscriptionManager subscriptionManager; 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; SubscriptionKey key;
subscriptionManager.parsePlainSerial(painText, key); subscriptionManager.parsePlainSerial(painText, key);
EXPECT_EQ("trial", key.m_type); EXPECT_EQ("trial", key.m_type);
EXPECT_EQ("Bob", key.m_name); EXPECT_EQ("Bob", key.m_name);
EXPECT_EQ(1, key.m_userLimit); EXPECT_EQ(1, key.m_userLimit);
EXPECT_EQ(1498297600, key.m_warnTime); EXPECT_EQ("a@a.a", key.m_email);
EXPECT_EQ(1498384000, key.m_expireTime); 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; SubscriptionManager subscriptionManager;
String painText("{v1;trial;Bob;1;1398297600;1398384000}"); String painText("{v1;trial;Bob;1;1398297600;1398384000}");
@ -76,3 +96,19 @@ TEST(SubscriptionTests, parsePlainSerial_expiredSerial_throwException)
EXPECT_THROW(subscriptionManager.parsePlainSerial(painText, key), XSubscription); 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);
}