From f00f60a8fe7930227fbcd5a84c71c0fd56acf202 Mon Sep 17 00:00:00 2001 From: Robby Stahl Date: Thu, 10 Sep 2015 14:04:01 -0700 Subject: [PATCH 001/241] Update mac directions to not require a root user I updated the directions such that a clear installation path is provided that does not require the direct use of root. --- doc/MacReadme.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/MacReadme.txt b/doc/MacReadme.txt index da2930505..f62762e5a 100755 --- a/doc/MacReadme.txt +++ b/doc/MacReadme.txt @@ -5,10 +5,10 @@ To install on Mac OS X with the .zip distribution (first seen in 1.3.6) you must 1. Extract the zip file to any location (usually double click will do this) 2. Open Terminal, and cd to the extracted directory (e.g. /Users/my-name/Downloads/extracted-dir/) - 3. Change to super user (use the su command) - 4. Copy the binaries to /usr/bin using: cp synergy* /usr/bin + 3. Copy the binaries to /usr/bin using: sudo cp synergy* /usr/bin + 4. Correct the permissions and ownership: sudo chown root:wheel /usr/bin/synergy*; sudo chmod 555 /usr/bin/synergy* -How to enable the root user in Mac OS X: +Alternatively, you can copy the binaries as root. How to enable the root user in Mac OS X: http://support.apple.com/en-us/ht1528 Once the binaries have been copied to /usr/bin, you should follow the configuration guide: From 5a03ece50b4eb7cb0b8f7146a4052df878ca530a Mon Sep 17 00:00:00 2001 From: j2gl Date: Fri, 20 Nov 2015 01:35:23 -0600 Subject: [PATCH 002/241] Update MacOS warning --- src/gui/src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index 70816276b..b24bc5a19 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -156,7 +156,8 @@ bool checkMacAssistiveDevices() QMessageBox::information( NULL, "Synergy", "Please enable access to assistive devices " - "(System Preferences), then re-open Synergy."); + "System Preferences -> Security & Privacy -> " + "Privacy -> Accessibility, then re-open Synergy."); } return result; From f58e95c96f4c88e968f4b6ce8306034d34787963 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 26 Sep 2016 15:46:57 +0100 Subject: [PATCH 003/241] v1.8.4 beta --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 963d84e36..fe96198ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,8 +17,8 @@ # Version number for Synergy set(VERSION_MAJOR 1) set(VERSION_MINOR 8) -set(VERSION_REV 3) -set(VERSION_STAGE stable) +set(VERSION_REV 4) +set(VERSION_STAGE beta) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From a70a2bf864f8726aae6fb7f90117bb0371fc24bb Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 11:37:16 +0100 Subject: [PATCH 004/241] #5329 Fix build date in about dialog --- src/gui/src/AboutDialog.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/src/AboutDialog.cpp b/src/gui/src/AboutDialog.cpp index 6fc1d57ed..1d2f5888e 100644 --- a/src/gui/src/AboutDialog.cpp +++ b/src/gui/src/AboutDialog.cpp @@ -32,7 +32,9 @@ AboutDialog::AboutDialog(QWidget* parent, const QString& synergyApp) : version = version + '-' + VERSION_STAGE + '-' + VERSION_REVISION; m_pLabelSynergyVersion->setText(version); - m_pLabelBuildDate->setText(QDate::currentDate().toString()); + QString buildDateString = QString::fromLocal8Bit(__DATE__).simplified(); + QDate buildDate = QLocale("en_US").toDate(buildDateString, "MMM d yyyy"); + m_pLabelBuildDate->setText(buildDate.toString(Qt::SystemLocaleLongDate)); // change default size based on os #if defined(Q_OS_MAC) From 665bd91dbda59edf2dafa42deb62d5dc0905204c Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 12:02:18 +0100 Subject: [PATCH 005/241] #5628 Move SSL socket code from plugin to lib/net --- src/lib/{plugin/ns => net}/SecureListenSocket.cpp | 0 src/lib/{plugin/ns => net}/SecureListenSocket.h | 0 src/lib/{plugin/ns => net}/SecureSocket.cpp | 0 src/lib/{plugin/ns => net}/SecureSocket.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename src/lib/{plugin/ns => net}/SecureListenSocket.cpp (100%) rename src/lib/{plugin/ns => net}/SecureListenSocket.h (100%) rename src/lib/{plugin/ns => net}/SecureSocket.cpp (100%) rename src/lib/{plugin/ns => net}/SecureSocket.h (100%) diff --git a/src/lib/plugin/ns/SecureListenSocket.cpp b/src/lib/net/SecureListenSocket.cpp similarity index 100% rename from src/lib/plugin/ns/SecureListenSocket.cpp rename to src/lib/net/SecureListenSocket.cpp diff --git a/src/lib/plugin/ns/SecureListenSocket.h b/src/lib/net/SecureListenSocket.h similarity index 100% rename from src/lib/plugin/ns/SecureListenSocket.h rename to src/lib/net/SecureListenSocket.h diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp similarity index 100% rename from src/lib/plugin/ns/SecureSocket.cpp rename to src/lib/net/SecureSocket.cpp diff --git a/src/lib/plugin/ns/SecureSocket.h b/src/lib/net/SecureSocket.h similarity index 100% rename from src/lib/plugin/ns/SecureSocket.h rename to src/lib/net/SecureSocket.h From 76b2558f1a218764c4b29d244dce5e4e13d73bec Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 12:16:20 +0100 Subject: [PATCH 006/241] #5617 Delete the plugin infrastructure --- src/gui/res/PluginWizardPageBase.ui | 137 ----------------- src/gui/src/Plugin.cpp | 73 --------- src/gui/src/Plugin.h | 53 ------- src/gui/src/PluginManager.cpp | 186 ----------------------- src/gui/src/PluginManager.h | 75 ---------- src/gui/src/PluginWizardPage.cpp | 206 ------------------------- src/gui/src/PluginWizardPage.h | 66 -------- src/lib/CMakeLists.txt | 1 - src/lib/arch/IArchPlugin.h | 80 ---------- src/lib/arch/unix/ArchPluginUnix.cpp | 239 ----------------------------- src/lib/arch/unix/ArchPluginUnix.h | 53 ------- src/lib/arch/win32/ArchPluginWindows.cpp | 250 ------------------------------- src/lib/arch/win32/ArchPluginWindows.h | 57 ------- src/lib/common/PluginVersion.cpp | 36 ----- src/lib/common/PluginVersion.h | 31 ---- src/lib/plugin/CMakeLists.txt | 28 ---- src/lib/plugin/ns/CMakeLists.txt | 128 ---------------- src/lib/plugin/ns/ns.cpp | 127 ---------------- src/lib/plugin/ns/ns.h | 41 ----- src/lib/plugin/winmmjoy/CMakeLists.txt | 32 ---- src/lib/plugin/winmmjoy/winmmjoy.cpp | 106 ------------- src/lib/plugin/winmmjoy/winmmjoy.h | 72 --------- 22 files changed, 2077 deletions(-) delete mode 100644 src/gui/res/PluginWizardPageBase.ui delete mode 100644 src/gui/src/Plugin.cpp delete mode 100644 src/gui/src/Plugin.h delete mode 100644 src/gui/src/PluginManager.cpp delete mode 100644 src/gui/src/PluginManager.h delete mode 100644 src/gui/src/PluginWizardPage.cpp delete mode 100644 src/gui/src/PluginWizardPage.h delete mode 100644 src/lib/arch/IArchPlugin.h delete mode 100644 src/lib/arch/unix/ArchPluginUnix.cpp delete mode 100644 src/lib/arch/unix/ArchPluginUnix.h delete mode 100644 src/lib/arch/win32/ArchPluginWindows.cpp delete mode 100644 src/lib/arch/win32/ArchPluginWindows.h delete mode 100644 src/lib/common/PluginVersion.cpp delete mode 100644 src/lib/common/PluginVersion.h delete mode 100644 src/lib/plugin/CMakeLists.txt delete mode 100644 src/lib/plugin/ns/CMakeLists.txt delete mode 100644 src/lib/plugin/ns/ns.cpp delete mode 100644 src/lib/plugin/ns/ns.h delete mode 100644 src/lib/plugin/winmmjoy/CMakeLists.txt delete mode 100644 src/lib/plugin/winmmjoy/winmmjoy.cpp delete mode 100644 src/lib/plugin/winmmjoy/winmmjoy.h diff --git a/src/gui/res/PluginWizardPageBase.ui b/src/gui/res/PluginWizardPageBase.ui deleted file mode 100644 index dfb7a9782..000000000 --- a/src/gui/res/PluginWizardPageBase.ui +++ /dev/null @@ -1,137 +0,0 @@ - - - PluginWizardPage - - - - 0 - 0 - 400 - 300 - - - - Setup Synergy - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Please wait... - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - diff --git a/src/gui/src/Plugin.cpp b/src/gui/src/Plugin.cpp deleted file mode 100644 index 50079da24..000000000 --- a/src/gui/src/Plugin.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-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 "Plugin.h" - -#include "CoreInterface.h" - -static const char kBaseUrl[] = "http://symless.com/files"; -static const char kDefaultVersion[] = "1.1"; -static const char kWinPackagePlatform32[] = "Windows-x86"; -static const char kWinPackagePlatform64[] = "Windows-x64"; -static const char kMacPackagePlatform[] = "MacOSX%1-i386"; -static const char kLinuxPackagePlatformDeb32[] = "Linux-i686-deb"; -static const char kLinuxPackagePlatformDeb64[] = "Linux-x86_64-deb"; -static const char kLinuxPackagePlatformRpm32[] = "Linux-i686-rpm"; -static const char kLinuxPackagePlatformRpm64[] = "Linux-x86_64-rpm"; - -#if defined(Q_OS_WIN) -static const char kWinPluginExt[] = ".dll"; -static const char kInstallerPluginLocation[] = "Plugins"; -#elif defined(Q_OS_MAC) -static const char kMacPluginPrefix[] = "lib"; -static const char kMacPluginExt[] = ".dylib"; -static const char kInstallerPluginLocation[] = "plugins"; // TODO: Fix for mac -#else -static const char kLinuxPluginPrefix[] = "lib"; -static const char kLinuxPluginExt[] = ".so"; -// /usr/bin becomes /usr/bin/../lib/syn... -static const char kInstallerPluginLocation[] = "../lib/synergy/plugins"; -#endif - -QString Plugin::getOsSpecificExt() -{ - -#if defined(Q_OS_WIN) - return kWinPluginExt; -#elif defined(Q_OS_MAC) - return kMacPluginExt; -#else - return kLinuxPluginExt; -#endif -} - -QString Plugin::getOsSpecificName(const QString& pluginName) -{ - QString result = pluginName; -#if defined(Q_OS_WIN) - result.append(getOsSpecificExt()); -#elif defined(Q_OS_MAC) - result = kMacPluginPrefix + pluginName + getOsSpecificExt(); -#else - result = kLinuxPluginPrefix + pluginName + getOsSpecificExt(); -#endif - return result; -} - -QString Plugin::getOsSpecificInstallerLocation() { - return kInstallerPluginLocation; -} diff --git a/src/gui/src/Plugin.h b/src/gui/src/Plugin.h deleted file mode 100644 index bec6a1c22..000000000 --- a/src/gui/src/Plugin.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-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 . - */ -#ifndef PLUGIN_H -#define PLUGIN_H - -#include -#include -#include - -#include "SslCertificate.h" -#include "CoreInterface.h" -#include "DataDownloader.h" - -class Plugin : public QObject -{ - Q_OBJECT - -public: - //Plugin(); - //~PluginManager(); - - static QString getOsSpecificName(const QString& pluginName); - static QString getOsSpecificExt(); - static QString getOsSpecificLocation(); - static QString getOsSpecificInstallerLocation(); - static QString getOsSpecificUserLocation(); - -public slots: - -private: -// CoreInterface m_CoreInterface; - -signals: - -private: - -}; - -#endif // PLUGIN_H diff --git a/src/gui/src/PluginManager.cpp b/src/gui/src/PluginManager.cpp deleted file mode 100644 index b498751a7..000000000 --- a/src/gui/src/PluginManager.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-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 "PluginManager.h" - -#include "CoreInterface.h" -#include "DataDownloader.h" -#include "QUtility.h" -#include "ProcessorArch.h" -#include "Fingerprint.h" -#include "Plugin.h" -#include "../lib/common/PluginVersion.h" - -#include - -#include -#include -#include -#include - - -PluginManager::PluginManager() : - m_PluginList() -{ - init(); -} - -PluginManager::~PluginManager() -{ -} - -void PluginManager::init() -{ - m_PluginDir = m_CoreInterface.getPluginDir(); - if (m_PluginDir.isEmpty()) { - emit error(tr("Failed to get plugin directory.")); - } - - m_ProfileDir = m_CoreInterface.getProfileDir(); - if (m_ProfileDir.isEmpty()) { - emit error(tr("Failed to get profile directory.")); - } - - m_InstalledDir = m_CoreInterface.getInstalledDir(); - if (m_InstalledDir.isEmpty()) { - emit error(tr("Failed to get installed directory.")); - } -} - -bool PluginManager::exist(QString name) -{ - CoreInterface coreInterface; - QString PluginDir = coreInterface.getPluginDir(); - QString pluginName = Plugin::getOsSpecificName(name); - QString filename; - filename.append(PluginDir); - filename.append(QDir::separator()).append(pluginName); - QFile file(filename); - bool exist = false; - if (file.exists()) { - exist = true; - } - - return exist; -} - -void PluginManager::copyPlugins() -{ - try { - // Get the Directory where plugins are put on installation - // If it doesn't exist, there is nothing to do - QString srcDirName(m_InstalledDir.append(QDir::separator()) - .append(Plugin::getOsSpecificInstallerLocation())); - - QDir srcDir(srcDirName); - if (!srcDir.exists()) { - emit info( - tr("No plugins found to copy from %1") - .arg(srcDirName)); - emit copyFinished(); - } - - // Get the directory where Plugins are installed into Synergy - // If it doesn't exist make it - QString destDirName = m_PluginDir; - - QDir destDir(destDirName); - if (!destDir.exists()) { - destDir.mkpath("."); - } - // Run through the list of plugins and copy them - for ( int i = 0 ; i < m_PluginList.size() ; i++ ) { - // Get a file entry for the plugin using the full path - QFile file(srcDirName + QDir::separator() + m_PluginList.at(i)); - - // construct the destination file name - QString newName(destDirName + QDir::separator() + m_PluginList.at(i)); - - // Check to see if the plugin already exists - QFile newFile(newName); - if(newFile.exists()) { - // If it does, delete it. TODO: Check to see if same and leave - bool result = newFile.remove(); - if( !result ) { - emit error( - tr( "Unable to delete plugin:\n%1\n" - "Please stop synergy and run the wizard again.") - .arg(newName)); - return; - } - } - // make a copy of the plugin in the new location - #if defined(Q_OS_WIN) - bool result = file.copy(newName); - #else - bool result = file.link(newName); - #endif - if ( !result ) { - emit error( - tr("Failed to copy plugin '%1' to: %2\n%3\n" - "Please stop synergy and run the wizard again.") - .arg(m_PluginList.at(i)) - .arg(newName) - .arg(file.errorString())); - return; - } - else { - emit info( - tr("Copying '%1' plugin (%2/%3)...") - .arg(m_PluginList.at(i)) - .arg(i+1) - .arg(m_PluginList.size())); - } - } - } - catch (std::exception& e) - { - emit error(tr( "An error occurred while trying to copy the " - "plugin list. Please contact the help desk, and " - "provide the following details.\n\n%1").arg(e.what())); - } - - emit copyFinished(); - return; -} - -void PluginManager::queryPluginList() -{ - try { - setDone(false); - QString extension = "*" + Plugin::getOsSpecificExt(); - QStringList nameFilter(extension); - - QString installDir(m_CoreInterface.getInstalledDir() - .append(QDir::separator()) - .append(Plugin::getOsSpecificInstallerLocation())); - - QString searchDirectory(installDir); - QDir directory(searchDirectory); - m_PluginList = directory.entryList(nameFilter); - setDone(true); - } - catch (std::exception& e) - { - setDone(true); - emit error(tr( "An error occurred while trying to load the " - "plugin list. Please contact the help desk, and " - "provide the following details.\n\n%1").arg(e.what())); - } - emit queryPluginDone(); - return; -} diff --git a/src/gui/src/PluginManager.h b/src/gui/src/PluginManager.h deleted file mode 100644 index b1450fda2..000000000 --- a/src/gui/src/PluginManager.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-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 . - */ - -#ifndef PLUGINMANAGER_H -#define PLUGINMANAGER_H - -#include -#include -#include - -#include "SslCertificate.h" -#include "CoreInterface.h" -#include "DataDownloader.h" -#include "Plugin.h" - -class PluginManager : public QObject -{ - Q_OBJECT - -public: - PluginManager(); - ~PluginManager(); - - void init(); - - int pluginCount() { return m_PluginList.count(); } - QStringList& getPluginList() { return m_PluginList; } - - bool isDone() { return done; } - void setDone(bool b) { done = b; } - static bool exist(QString name); - -public slots: - void copyPlugins(); - void queryPluginList(); - -private: - QString getPluginUrl(const QString& pluginName); - bool runProgram( - const QString& program, - const QStringList& args, - const QStringList& env); - -signals: - void error(QString e); - void info(QString i); - void updateCopyStatus(int); - void copyFinished(); - void queryPluginDone(); - -private: - QStringList m_PluginList; - QString m_PluginDir; - QString m_ProfileDir; - QString m_InstalledDir; - CoreInterface m_CoreInterface; - SslCertificate m_SslCertificate; - bool done; -}; - -#endif // PLUGINMANAGER_H diff --git a/src/gui/src/PluginWizardPage.cpp b/src/gui/src/PluginWizardPage.cpp deleted file mode 100644 index 8be207520..000000000 --- a/src/gui/src/PluginWizardPage.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-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 "PluginWizardPage.h" -#include "ui_PluginWizardPageBase.h" - -#include "SslCertificate.h" -#include "PluginManager.h" -#include "MainWindow.h" -#include "EditionType.h" - -#include -#include -#include - -PluginWizardPage::PluginWizardPage(MainWindow& mainWindow, QWidget *parent) : - QWizardPage(parent), - m_Finished(false), - m_Edition(Unknown), - m_pSslCertificate(NULL), - m_mainWindow(mainWindow) -{ - setupUi(this); - - QMovie *movie = new QMovie(":/res/image/spinning-wheel.gif"); - m_pLabelSpinning->setMovie(movie); - movie->start(); - - m_pSslCertificate = new SslCertificate(this); -} - -PluginWizardPage::~PluginWizardPage() -{ - delete m_pSslCertificate; -} - -void PluginWizardPage::changeEvent(QEvent *e) -{ - QWizardPage::changeEvent(e); - switch (e->type()) { - case QEvent::LanguageChange: - retranslateUi(this); - break; - default: - break; - } -} - -void PluginWizardPage::initializePage() -{ - QWizardPage::initializePage(); - - if (m_Edition != Pro) { - updateStatus(tr("Setup complete.")); - showFinished(); - return; - } - - m_pLabelSpinning->show(); - - QThread* thread = new QThread; - - connect(&m_PluginManager, - SIGNAL(error(QString)), - this, - SLOT(showError(QString))); - - connect(&m_PluginManager, - SIGNAL(info(QString)), - this, - SLOT(updateStatus(QString))); - - connect(&m_PluginManager, - SIGNAL(queryPluginDone()), - this, - SLOT(queryPluginDone())); - - connect(&m_PluginManager, - SIGNAL(queryPluginDone()), - thread, - SLOT(quit())); - - connect(&m_PluginManager, - SIGNAL(error(QString)), - thread, - SLOT(quit())); - - connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - - m_PluginManager.moveToThread(thread); - thread->start(); - - QMetaObject::invokeMethod(&m_PluginManager, "queryPluginList", Qt::QueuedConnection); -} - -void PluginWizardPage::queryPluginDone() -{ - QStringList pluginList = m_PluginManager.getPluginList(); - if (pluginList.isEmpty()) { - updateStatus(tr("Setup complete.")); - showFinished(); - } - else { - m_mainWindow.stopSynergy(); - copyPlugins(); - m_mainWindow.startSynergy(); - } -} - -void PluginWizardPage::copyPlugins() -{ - m_pThread = new QThread; - - connect(&m_PluginManager, - SIGNAL(copyFinished()), - this, - SLOT(generateCertificate())); - - connect(&m_PluginManager, - SIGNAL(error(QString)), - m_pThread, - SLOT(quit())); - - connect(m_pThread, - SIGNAL(finished()), - m_pThread, - SLOT(deleteLater())); - - updateStatus( - tr("Copying plugins...")); - - m_PluginManager.moveToThread(m_pThread); - m_pThread->start(); - - QMetaObject::invokeMethod( - &m_PluginManager, - "copyPlugins", - Qt::QueuedConnection); -} - -void PluginWizardPage::generateCertificate() -{ - connect(m_pSslCertificate, - SIGNAL(generateFinished()), - this, - SLOT(finished())); - - connect(m_pSslCertificate, - SIGNAL(generateFinished()), - m_pThread, - SLOT(quit())); - - updateStatus(tr("Generating SSL certificate...")); - - QMetaObject::invokeMethod( - m_pSslCertificate, - "generateCertificate", - Qt::QueuedConnection); -} - -void PluginWizardPage::showError(QString error) -{ - updateStatus(tr("Error: %1").arg(error)); - showFinished(); -} - - -void PluginWizardPage::updateStatus(QString info) -{ - m_pLabelStatus->setText(info); -} - -void PluginWizardPage::finished() -{ - // TODO: we should check if ns plugin exists - m_mainWindow.appConfig().setCryptoEnabled(true); - - updateStatus(tr("Plugins installed successfully.")); - showFinished(); -} - -void PluginWizardPage::showFinished() -{ - m_pLabelSpinning->hide(); - m_Finished = true; - emit completeChanged(); -} - -bool PluginWizardPage::isComplete() const -{ - return m_Finished; -} diff --git a/src/gui/src/PluginWizardPage.h b/src/gui/src/PluginWizardPage.h deleted file mode 100644 index d43197865..000000000 --- a/src/gui/src/PluginWizardPage.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-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 . - */ - -#ifndef PLUGINWIZARDPAGE_H -#define PLUGINWIZARDPAGE_H - -#include "AppConfig.h" - -#include "ui_PluginWizardPageBase.h" -#include "PluginManager.h" -#include - -class SslCertificate; -class MainWindow; - -class PluginWizardPage : public QWizardPage, public Ui::PluginWizardPage { - - Q_OBJECT - -public: - PluginWizardPage(MainWindow& mainWindow, QWidget *parent = 0); - ~PluginWizardPage(); - - void setFinished(bool b) { m_Finished = b; } - void setEdition(int edition) { m_Edition = edition; } - - bool isComplete() const; - void initializePage(); - -protected: - void changeEvent(QEvent *e); - -protected slots: - void showError(QString error); - void updateStatus(QString info); - void queryPluginDone(); - void generateCertificate(); - void finished(); - -private: - void copyPlugins(); - void showFinished(); - -private: - bool m_Finished; - int m_Edition; - PluginManager m_PluginManager; - SslCertificate* m_pSslCertificate; - QThread* m_pThread; - MainWindow& m_mainWindow; -}; -#endif // PLUGINWIZARDPAGE_H diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 8eba5dffa..48beb80a8 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -23,7 +23,6 @@ add_subdirectory(ipc) add_subdirectory(mt) add_subdirectory(net) add_subdirectory(platform) -add_subdirectory(plugin) add_subdirectory(server) add_subdirectory(synergy) diff --git a/src/lib/arch/IArchPlugin.h b/src/lib/arch/IArchPlugin.h deleted file mode 100644 index e91ed65ff..000000000 --- a/src/lib/arch/IArchPlugin.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 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 . - */ - -#pragma once - -#include "common/IInterface.h" -#include "common/stdmap.h" -#include "base/String.h" - -class IEventQueue; - -//! Interface for plugin manager. -/*! -A plugin manager should load all 3rd party plugins from the plugins dir, -and then look for common function names in the plugins. -*/ -class IArchPlugin : public IInterface { -public: - //! @name manipulators - //@{ - - //!Load plugins - /*! - Scan the plugins dir and load plugins. - */ - virtual void load() = 0; - - //!Unload plugins - /*! - Look through the loaded plugins and unload them. - */ - virtual void unload() = 0; - - //! Init the common parts - /*! - Initializes common parts like log and arch. - */ - virtual void init(void* log, void* arch) = 0; - - //! Init the event part - /*! - Initializes event parts. - */ - virtual void initEvent(void* eventTarget, IEventQueue* events) = 0; - - //! Check if exists - /*! - Returns true if the plugin exists and is loaded. - */ - virtual bool exists(const char* name) = 0; - - //! Invoke function - /*! - Invokes a function from the plugin. - */ - virtual void* invoke(const char* plugin, - const char* command, - void** args, - void* library = NULL) = 0; - - //@} - -protected: - typedef std::map PluginTable; -}; diff --git a/src/lib/arch/unix/ArchPluginUnix.cpp b/src/lib/arch/unix/ArchPluginUnix.cpp deleted file mode 100644 index edf53d17b..000000000 --- a/src/lib/arch/unix/ArchPluginUnix.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 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 . - */ - -#include "arch/unix/ArchPluginUnix.h" - -#include "arch/unix/XArchUnix.h" -#include "common/PluginVersion.h" -#include "base/IEventQueue.h" -#include "base/Event.h" -#include "base/Log.h" - -#include -#include -#include -#include - -typedef void (*initFunc)(void*, void*); -typedef int (*initEventFunc)(void (*sendEvent)(const char*, void*)); -typedef void* (*invokeFunc)(const char*, void*); -typedef void (*cleanupFunc)(); - -void* g_eventTarget = NULL; -IEventQueue* g_events = NULL; - -ArchPluginUnix::ArchPluginUnix() -{ -} - -ArchPluginUnix::~ArchPluginUnix() -{ -} - -void -ArchPluginUnix::load() -{ - String pluginsDir = getPluginsDir(); - LOG((CLOG_DEBUG "plugins dir: %s", pluginsDir.c_str())); - - struct dirent* de = NULL; - DIR* dir = NULL; - - dir = opendir(pluginsDir.c_str()); - if (dir == NULL) { - LOG((CLOG_DEBUG "can't open plugins dir: %s", - pluginsDir.c_str())); - return; - } - - std::vector plugins; - while ((de = readdir(dir)) != NULL) { - // ignore hidden files and diretories like .. and . - if (de->d_name[0] != '.') { - plugins.push_back(de->d_name); - } - } - closedir(dir); - - std::vector::iterator it; - for (it = plugins.begin(); it != plugins.end(); ++it) { - String filename = *it; - String path = synergy::string::sprintf( - "%s/%s", pluginsDir.c_str(), filename.c_str()); - String name = synergy::string::removeFileExt(filename.substr(3)); - - LOG((CLOG_DEBUG "loading plugin: %s", filename.c_str())); - void* handle = dlopen(path.c_str(), RTLD_LAZY); - - if (handle == NULL) { - LOG((CLOG_ERR "failed to load plugin '%s', error: %s", - filename.c_str(), dlerror())); - continue; - } - - - String expectedVersion = getExpectedPluginVersion(name.c_str()); - String currentVersion = getCurrentVersion(name, handle); - - if (currentVersion.empty() || (expectedVersion != currentVersion)) { - LOG((CLOG_ERR - "failed to load plugin '%s', " - "expected version %s but was %s", - filename.c_str(), - expectedVersion.c_str(), - currentVersion.empty() ? "unknown" : currentVersion.c_str())); - - dlclose(handle); - continue; - } - - LOG((CLOG_DEBUG "plugin loaded: %s (version %s)", - filename.c_str(), - currentVersion.c_str())); - - m_pluginTable.insert(std::make_pair(name, handle)); - } -} - -void -ArchPluginUnix::unload() -{ - PluginTable::iterator it; - for (it = m_pluginTable.begin(); it != m_pluginTable.end(); it++) { - cleanupFunc cleanup = (cleanupFunc)dlsym(it->second, "cleanup"); - if (cleanup != NULL) { - cleanup(); - } - else { - LOG((CLOG_DEBUG "no cleanup function in %s", it->first.c_str())); - } - - LOG((CLOG_DEBUG "unloading plugin: %s", it->first.c_str())); - dlclose(it->second); - } -} - -void -ArchPluginUnix::init(void* log, void* arch) -{ - PluginTable::iterator it; - for (it = m_pluginTable.begin(); it != m_pluginTable.end(); it++) { - initFunc initPlugin = (initFunc)dlsym(it->second, "init"); - if (initPlugin != NULL) { - initPlugin(log, arch); - } - else { - LOG((CLOG_DEBUG "no init function in %s", it->first.c_str())); - } - } -} - -void -ArchPluginUnix::initEvent(void* eventTarget, IEventQueue* events) -{ - g_eventTarget = eventTarget; - g_events = events; - - PluginTable::iterator it; - for (it = m_pluginTable.begin(); it != m_pluginTable.end(); it++) { - initEventFunc initEventPlugin = (initEventFunc)dlsym(it->second, "initEvent"); - if (initEventPlugin != NULL) { - initEventPlugin(&sendEvent); - } - else { - LOG((CLOG_DEBUG "no init event function in %s", it->first.c_str())); - } - } -} - -bool -ArchPluginUnix::exists(const char* name) -{ - PluginTable::iterator it; - it = m_pluginTable.find(name); - return it != m_pluginTable.end() ? true : false; -} - -void* -ArchPluginUnix::invoke( - const char* plugin, - const char* command, - void** args, - void* library) -{ - void* lib = NULL; - - if (library == NULL) { - PluginTable::iterator it; - it = m_pluginTable.find(plugin); - if (it != m_pluginTable.end()) { - lib = it->second; - } - else { - LOG((CLOG_DEBUG "invoke command failed, plugin: %s command: %s", - plugin, command)); - return NULL; - } - } - else { - lib = library; - } - - invokeFunc invokePlugin = (invokeFunc)dlsym(lib, "invoke"); - void* result = NULL; - if (invokePlugin != NULL) { - result = invokePlugin(command, args); - } - else { - LOG((CLOG_DEBUG "no invoke function in %s", plugin)); - } - - return result; -} - -String -ArchPluginUnix::getPluginsDir() -{ - return ARCH->getPluginDirectory(); -} - -String -ArchPluginUnix::getCurrentVersion(const String& name, void* handle) -{ - char* version = (char*)invoke(name.c_str(), "version", NULL, handle); - if (version == NULL) { - return ""; - } - - return version; -} - -void -sendEvent(const char* eventName, void* data) -{ - LOG((CLOG_DEBUG5 "plugin sending event")); - Event::Type type = g_events->getRegisteredType(eventName); - g_events->addEvent(Event(type, g_eventTarget, data)); -} - -void -log(const char* text) -{ - LOG((CLOG_DEBUG "plugin: %s", text)); -} - diff --git a/src/lib/arch/unix/ArchPluginUnix.h b/src/lib/arch/unix/ArchPluginUnix.h deleted file mode 100644 index 84ce185ed..000000000 --- a/src/lib/arch/unix/ArchPluginUnix.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 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 . - */ - -#pragma once - -#include "arch/IArchPlugin.h" - -#define ARCH_PLUGIN ArchPluginUnix - -class IEventQueue; - -//! Unix implementation of IArchPlugin -class ArchPluginUnix : public IArchPlugin { -public: - ArchPluginUnix(); - virtual ~ArchPluginUnix(); - - // IArchPlugin overrides - void load(); - void unload(); - void init(void* log, void* arch); - void initEvent(void* eventTarget, IEventQueue* events); - bool exists(const char* name); - virtual void* invoke(const char* pluginName, - const char* functionName, - void** args, - void* library = NULL); - -private: - String getPluginsDir(); - String getCurrentVersion(const String& name, void* handle); - -private: - PluginTable m_pluginTable; -}; - -void sendEvent(const char* text, void* data); -void log(const char* text); diff --git a/src/lib/arch/win32/ArchPluginWindows.cpp b/src/lib/arch/win32/ArchPluginWindows.cpp deleted file mode 100644 index 7e498cb2a..000000000 --- a/src/lib/arch/win32/ArchPluginWindows.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 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 . - */ - -#include "arch/win32/ArchPluginWindows.h" -#include "arch/win32/XArchWindows.h" -#include "common/PluginVersion.h" -#include "base/Log.h" -#include "base/IEventQueue.h" -#include "base/Event.h" -#include "synergy/Screen.h" - -#define WIN32_LEAN_AND_MEAN -#include -#include - -typedef void (*initFunc)(void*, void*); -typedef int (*initEventFunc)(void (*sendEvent)(const char*, void*)); -typedef void* (*invokeFunc)(const char*, void**); -typedef void (*cleanupFunc)(); - -void* g_eventTarget = NULL; -IEventQueue* g_events = NULL; -static const char * kPre174Plugin = "Pre-1.7.v"; - -ArchPluginWindows::ArchPluginWindows() -{ -} - -ArchPluginWindows::~ArchPluginWindows() -{ -} - -void -ArchPluginWindows::load() -{ - String dir = getPluginsDir(); - LOG((CLOG_DEBUG "plugins dir: %s", dir.c_str())); - - String pattern = String(dir).append("\\*.dll"); - std::vector plugins; - getFilenames(pattern, plugins); - - std::vector::iterator it; - for (it = plugins.begin(); it != plugins.end(); ++it) { - String filename = *it; - String name = synergy::string::removeFileExt(filename); - String path = synergy::string::sprintf( - "%s\\%s", dir.c_str(), filename.c_str()); - - LOG((CLOG_DEBUG "loading plugin: %s", filename.c_str())); - HINSTANCE handle = LoadLibrary(path.c_str()); - void* voidHandle = reinterpret_cast(handle); - - if (handle == NULL) { - String error = XArchEvalWindows().eval(); - LOG((CLOG_ERR "failed to load plugin '%s', error: %s", - filename.c_str(), error.c_str())); - continue; - } - - String expectedVersion = getExpectedPluginVersion(name.c_str()); - String currentVersion = getCurrentVersion(name.c_str(), voidHandle); - - if (currentVersion.empty() || (expectedVersion != currentVersion)) { - LOG((CLOG_ERR - "failed to load plugin '%s', " - "expected version %s but was %s", - filename.c_str(), - expectedVersion.c_str(), - currentVersion.empty() ? "unknown" : currentVersion.c_str())); - - FreeLibrary(handle); - continue; - } - - LOG((CLOG_DEBUG "plugin loaded: %s (version %s)", - filename.c_str(), - currentVersion.c_str())); - - m_pluginTable.insert(std::make_pair(name, voidHandle)); - } -} - -void -ArchPluginWindows::unload() -{ - PluginTable::iterator it; - HINSTANCE lib; - for (it = m_pluginTable.begin(); it != m_pluginTable.end(); it++) { - lib = reinterpret_cast(it->second); - cleanupFunc cleanup = (cleanupFunc)GetProcAddress(lib, "cleanup"); - if (cleanup != NULL) { - cleanup(); - } - else { - LOG((CLOG_DEBUG "no cleanup function in %s", it->first.c_str())); - } - - LOG((CLOG_DEBUG "unloading plugin: %s", it->first.c_str())); - FreeLibrary(lib); - } -} - -void -ArchPluginWindows::init(void* log, void* arch) -{ - PluginTable::iterator it; - HINSTANCE lib; - for (it = m_pluginTable.begin(); it != m_pluginTable.end(); it++) { - lib = reinterpret_cast(it->second); - initFunc initPlugin = (initFunc)GetProcAddress(lib, "init"); - if (initPlugin != NULL) { - initPlugin(log, arch); - } - else { - LOG((CLOG_DEBUG "no init function in %s", it->first.c_str())); - } - } -} - -void -ArchPluginWindows::initEvent(void* eventTarget, IEventQueue* events) -{ - g_eventTarget = eventTarget; - g_events = events; - - PluginTable::iterator it; - HINSTANCE lib; - for (it = m_pluginTable.begin(); it != m_pluginTable.end(); it++) { - lib = reinterpret_cast(it->second); - initEventFunc initEventPlugin = (initEventFunc)GetProcAddress(lib, "initEvent"); - if (initEventPlugin != NULL) { - initEventPlugin(&sendEvent); - } - else { - LOG((CLOG_DEBUG "no init event function in %s", it->first.c_str())); - } - } -} - - -bool -ArchPluginWindows::exists(const char* name) -{ - PluginTable::iterator it; - it = m_pluginTable.find(name); - return it != m_pluginTable.end() ? true : false; -} - -void* -ArchPluginWindows::invoke( - const char* plugin, - const char* command, - void** args, - void* library) -{ - HINSTANCE lib = NULL; - - if (library == NULL) { - PluginTable::iterator it; - it = m_pluginTable.find(plugin); - if (it != m_pluginTable.end()) { - lib = reinterpret_cast(it->second); - } - else { - LOG((CLOG_DEBUG "invoke command failed, plugin: %s command: %s", - plugin, command)); - return NULL; - } - } - else { - lib = reinterpret_cast(library); - } - - invokeFunc invokePlugin = (invokeFunc)GetProcAddress(lib, "invoke"); - void* result = NULL; - if (invokePlugin != NULL) { - result = invokePlugin(command, args); - } - else { - LOG((CLOG_DEBUG "no invoke function in %s", plugin)); - } - - return result; -} - -void -ArchPluginWindows::getFilenames(const String& pattern, std::vector& filenames) -{ - WIN32_FIND_DATA data; - HANDLE find = FindFirstFile(pattern.c_str(), &data); - if (find == INVALID_HANDLE_VALUE) { - FindClose(find); - LOG((CLOG_DEBUG "plugins dir is empty: %s", pattern.c_str())); - return; - } - - do { - filenames.push_back(data.cFileName); - } while (FindNextFile(find, &data)); - - FindClose(find); -} - -String -ArchPluginWindows::getPluginsDir() -{ - return ARCH->getPluginDirectory(); -} - -String -ArchPluginWindows::getCurrentVersion(const String& name, void* handle) -{ - char* version = (char*)invoke(name.c_str(), "version", NULL, handle); - if (version == NULL) { - return ""; - } - - return version; -} - - -void -sendEvent(const char* eventName, void* data) -{ - LOG((CLOG_DEBUG5 "plugin sending event")); - Event::Type type = g_events->getRegisteredType(eventName); - g_events->addEvent(Event(type, g_eventTarget, data)); -} - -void -log(const char* text) -{ - LOG((CLOG_DEBUG "plugin: %s", text)); -} diff --git a/src/lib/arch/win32/ArchPluginWindows.h b/src/lib/arch/win32/ArchPluginWindows.h deleted file mode 100644 index dc61ead98..000000000 --- a/src/lib/arch/win32/ArchPluginWindows.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 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 . - */ - -#pragma once - -#include "arch/IArchPlugin.h" - -#include - -#define ARCH_PLUGIN ArchPluginWindows - -class Screen; -class IEventQueue; - -//! Windows implementation of IArchPlugin -class ArchPluginWindows : public IArchPlugin { -public: - ArchPluginWindows(); - virtual ~ArchPluginWindows(); - - // IArchPlugin overrides - void load(); - void unload(); - void init(void* log, void* arch); - void initEvent(void* eventTarget, IEventQueue* events); - bool exists(const char* name); - void* invoke(const char* pluginName, - const char* functionName, - void** args, - void* library = NULL); - -private: - void getFilenames(const String& pattern, std::vector& filenames); - String getPluginsDir(); - String getCurrentVersion(const String& name, void* handle); - -private: - PluginTable m_pluginTable; -}; - -void sendEvent(const char* text, void* data); -void log(const char* text); diff --git a/src/lib/common/PluginVersion.cpp b/src/lib/common/PluginVersion.cpp deleted file mode 100644 index 4ccacead1..000000000 --- a/src/lib/common/PluginVersion.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-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 "PluginVersion.h" - -#include - -static const char kUnknownVersion[] = "unknown"; -const char* s_pluginNames[] = { "ns" }; -static const char* s_pluginVersions[] = { "1.3" }; - -const char* getExpectedPluginVersion(const char* name) -{ - for (int i = 0; i < kPluginCount; i++) { - if (strcmp(name, s_pluginNames[i]) == 0) { - return s_pluginVersions[i]; - break; - } - } - - return kUnknownVersion; -} diff --git a/src/lib/common/PluginVersion.h b/src/lib/common/PluginVersion.h deleted file mode 100644 index b2f6bae2c..000000000 --- a/src/lib/common/PluginVersion.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-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 - -enum EPluginType { - kSecureSocket, - kPluginCount -}; - -extern const char* s_pluginNames[]; - -//! Get expected plugin version -/*! -Returns the plugin version expected by the plugin loader. -*/ -const char* getExpectedPluginVersion(const char* name); diff --git a/src/lib/plugin/CMakeLists.txt b/src/lib/plugin/CMakeLists.txt deleted file mode 100644 index 237da9896..000000000 --- a/src/lib/plugin/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -# synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012-2016 Symless Ltd. -# Copyright (C) 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 . - -if (WIN32) - add_subdirectory(winmmjoy) -endif() - -if (APPLE) - # 10.7 should be supported, but gives is a _NXArgv linker error - if (OSX_TARGET_MINOR GREATER 7) - add_subdirectory(ns) - endif() -else() - add_subdirectory(ns) -endif() diff --git a/src/lib/plugin/ns/CMakeLists.txt b/src/lib/plugin/ns/CMakeLists.txt deleted file mode 100644 index 80b567412..000000000 --- a/src/lib/plugin/ns/CMakeLists.txt +++ /dev/null @@ -1,128 +0,0 @@ -# synergy -- mouse and keyboard sharing utility -# Copyright (C) 2015-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 . - -file(GLOB headers "*.h") -file(GLOB sources "*.cpp") - -if (SYNERGY_ADD_HEADERS) - list(APPEND sources ${headers}) -endif() - -if (WIN32) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(OPENSSL_PLAT_DIR openssl-win64) - else() - set(OPENSSL_PLAT_DIR openssl-win32) - endif() - set(OPENSSL_INCLUDE ../../../../ext/${OPENSSL_PLAT_DIR}/inc32) -endif() - -if (APPLE) - set(OPENSSL_PLAT_DIR openssl-osx) - set(OPENSSL_INCLUDE ../../../../ext/${OPENSSL_PLAT_DIR}/include) -endif() - -include_directories( - ../../../lib/ - ../../../.. - ${OPENSSL_INCLUDE} -) - -add_library(ns SHARED ${sources}) - -if (WIN32) - set(OPENSSL_LIBS - ${CMAKE_SOURCE_DIR}/ext/${OPENSSL_PLAT_DIR}/out32dll/libeay32.lib - ${CMAKE_SOURCE_DIR}/ext/${OPENSSL_PLAT_DIR}/out32dll/ssleay32.lib - ) -endif() - -if (UNIX) - if (APPLE) - set(OPENSSL_LIBS - ${CMAKE_SOURCE_DIR}/ext/${OPENSSL_PLAT_DIR}/libssl.a - ${CMAKE_SOURCE_DIR}/ext/${OPENSSL_PLAT_DIR}/libcrypto.a - ) - else() - set(OPENSSL_LIBS ssl crypto) - endif() -endif() - -target_link_libraries(ns - arch base client common io mt net ipc platform server synergy ${libs} ${OPENSSL_LIBS}) - -if (WIN32) - add_custom_command( - TARGET ns - POST_BUILD - COMMAND xcopy /Y /Q - ..\\..\\..\\..\\..\\lib\\${CMAKE_CFG_INTDIR}\\ns.* - ..\\..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR}\\plugins\\ - ) - add_custom_command( - TARGET ns - POST_BUILD - COMMAND xcopy /Y /Q - ..\\..\\..\\..\\..\\ext\\${OPENSSL_PLAT_DIR}\\out32dll\\libeay32.* - ..\\..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR} - ) - add_custom_command( - TARGET ns - POST_BUILD - COMMAND xcopy /Y /Q - ..\\..\\..\\..\\..\\ext\\${OPENSSL_PLAT_DIR}\\out32dll\\ssleay32.* - ..\\..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR} - ) -endif() - -if (UNIX) - if (APPLE) - add_custom_command( - TARGET ns - POST_BUILD - COMMAND - mkdir -p - ${CMAKE_SOURCE_DIR}/bin/${CMAKE_CFG_INTDIR}/plugins - && - cp - ${CMAKE_SOURCE_DIR}/lib/${CMAKE_CFG_INTDIR}/libns.* - ${CMAKE_SOURCE_DIR}/bin/${CMAKE_CFG_INTDIR}/plugins/ - ) - else() - if (CMAKE_BUILD_TYPE STREQUAL Debug) - add_custom_command( - TARGET ns - POST_BUILD - COMMAND mkdir -p - ${CMAKE_SOURCE_DIR}/bin/debug/plugins - && - cp - ${CMAKE_SOURCE_DIR}/lib/debug/libns.* - ${CMAKE_SOURCE_DIR}/bin/debug/plugins/ - ) - else() - add_custom_command( - TARGET ns - POST_BUILD - COMMAND mkdir -p - ${CMAKE_SOURCE_DIR}/bin/plugins - && - cp - ${CMAKE_SOURCE_DIR}/lib/libns.* - ${CMAKE_SOURCE_DIR}/bin/plugins/ - ) - endif() - endif() -endif() diff --git a/src/lib/plugin/ns/ns.cpp b/src/lib/plugin/ns/ns.cpp deleted file mode 100644 index 17ba322ff..000000000 --- a/src/lib/plugin/ns/ns.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-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 "ns.h" - -#include "SecureSocket.h" -#include "SecureListenSocket.h" -#include "arch/Arch.h" -#include "common/PluginVersion.h" -#include "base/Log.h" - -#include -#include -#include -#include - -SecureSocket* g_secureSocket = NULL; -SecureListenSocket* g_secureListenSocket = NULL; -Arch* g_arch = NULL; -Log* g_log = NULL; - -std::string -helperGetLibsUsed(void) -{ - std::stringstream libs(ARCH->getLibsUsed()); - std::string msg; - std::string pid; - std::getline(libs,pid); - - while( std::getline(libs,msg) ) { - LOG(( CLOG_DEBUG "libs:%s",msg.c_str())); - } - return pid; -} - -extern "C" { -void -init(void* log, void* arch) -{ - if (g_log == NULL) { - g_log = new Log(reinterpret_cast(log)); - } - - if (g_arch == NULL) { - Arch::setInstance(reinterpret_cast(arch)); - } - - LOG(( CLOG_DEBUG "library use: %s", helperGetLibsUsed().c_str())); -} - -int -initEvent(void (*sendEvent)(const char*, void*)) -{ - return 0; -} - -void* -invoke(const char* command, void** args) -{ - IEventQueue* arg1 = NULL; - SocketMultiplexer* arg2 = NULL; - if (args != NULL) { - arg1 = reinterpret_cast(args[0]); - arg2 = reinterpret_cast(args[1]); - } - - if (strcmp(command, "getSocket") == 0) { - if (g_secureSocket != NULL) { - delete g_secureSocket; - } - g_secureSocket = new SecureSocket(arg1, arg2); - g_secureSocket->initSsl(false); - return g_secureSocket; - } - else if (strcmp(command, "getListenSocket") == 0) { - if (g_secureListenSocket != NULL) { - delete g_secureListenSocket; - } - g_secureListenSocket = new SecureListenSocket(arg1, arg2); - return g_secureListenSocket; - } - else if (strcmp(command, "deleteSocket") == 0) { - if (g_secureSocket != NULL) { - delete g_secureSocket; - g_secureSocket = NULL; - } - } - else if (strcmp(command, "deleteListenSocket") == 0) { - if (g_secureListenSocket != NULL) { - delete g_secureListenSocket; - g_secureListenSocket = NULL; - } - } - else if (strcmp(command, "version") == 0) { - return (void*)getExpectedPluginVersion(s_pluginNames[kSecureSocket]); - } - - return NULL; -} - -void -cleanup() -{ - if (g_secureSocket != NULL) { - delete g_secureSocket; - } - - if (g_secureListenSocket != NULL) { - delete g_secureListenSocket; - } -} - -} diff --git a/src/lib/plugin/ns/ns.h b/src/lib/plugin/ns/ns.h deleted file mode 100644 index 39999110b..000000000 --- a/src/lib/plugin/ns/ns.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015-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 - -#if defined _WIN32 -#define WIN32_LEAN_AND_MEAN -#include - -#if defined(ns_EXPORTS) -#define NS_API __declspec(dllexport) -#else -#define NS_API __declspec(dllimport) -#endif - -#else -#define NS_API -#endif - -extern "C" { - -NS_API void init(void* log, void* arch); -NS_API int initEvent(void (*sendEvent)(const char*, void*)); -NS_API void* invoke(const char* command, void** args); -NS_API void cleanup(); - -} diff --git a/src/lib/plugin/winmmjoy/CMakeLists.txt b/src/lib/plugin/winmmjoy/CMakeLists.txt deleted file mode 100644 index b07915bfc..000000000 --- a/src/lib/plugin/winmmjoy/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -# synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012-2016 Symless Ltd. -# Copyright (C) 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 (SYNERGY_ADD_HEADERS) - list(APPEND sources ${headers}) -endif() - -add_library(winmmjoy SHARED ${sources}) - -add_custom_command( - TARGET winmmjoy - POST_BUILD - COMMAND xcopy /Y /Q - ..\\..\\..\\..\\..\\lib\\${CMAKE_CFG_INTDIR}\\winmmjoy.* - ..\\..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR}\\plugins\\ -) diff --git a/src/lib/plugin/winmmjoy/winmmjoy.cpp b/src/lib/plugin/winmmjoy/winmmjoy.cpp deleted file mode 100644 index d6306e82d..000000000 --- a/src/lib/plugin/winmmjoy/winmmjoy.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 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 . - */ - -#include "winmmjoy.h" - -#include -#include -#include - -#pragma comment(lib, "winmm.lib") - -std::stringstream _logStream; -#define LOG(s) \ - _logStream.str(""); \ - _logStream << "winmmjoy: " << s << std::endl; \ - s_log(_logStream.str().c_str()) - -static bool s_running = true; -static void (*s_sendEvent)(const char*, void*) = NULL; -static void (*s_log)(const char*) = NULL; - -extern "C" { - -void -init(void* log, void* arch) -{ -} - -int -initEvent(void (*sendEvent)(const char*, void*)) -{ - s_sendEvent = sendEvent; - CreateThread(NULL, 0, mainLoop, NULL, 0, NULL); - return 0; -} - -void -cleanup() -{ - s_running = false; -} - -} - -DWORD WINAPI -mainLoop(void* data) -{ - // TODO: use a different message - e.g. DPLG%s (data - plugin) - const char* buttonsEvent = "IPrimaryScreen::getGameDeviceButtonsEvent"; - const char* sticksEvent = "IPrimaryScreen::getGameDeviceSticksEvent"; - const char* triggersEvent = "IPrimaryScreen::getGameDeviceTriggersEvent"; - - JOYINFOEX joyInfo; - ZeroMemory(&joyInfo, sizeof(joyInfo)); - joyInfo.dwSize = sizeof(joyInfo); - joyInfo.dwFlags = JOY_RETURNALL; - - // note: synergy data is often 16-bit, where winmm is 32-bit. - UINT index = JOYSTICKID1; - DWORD buttons, buttonsLast = 0; - DWORD xPos, xPosLast = 0; - DWORD yPos, yPosLast = 0; - - while (s_running) { - - if (joyGetPosEx(index, &joyInfo) != JOYERR_NOERROR) { - Sleep(1000); - continue; - } - - buttons = joyInfo.dwButtons; - xPos = joyInfo.dwXpos; - yPos = joyInfo.dwYpos; - - if (buttons != buttonsLast) { - s_sendEvent(buttonsEvent, - new CGameDeviceButtonInfo(index, (GameDeviceButton)joyInfo.dwButtons)); - } - - if (xPos != xPosLast || yPos != yPosLast) { - s_sendEvent(sticksEvent, - new CGameDeviceStickInfo(index, (short)xPos, (short)yPos, 0, 0)); - } - - buttonsLast = buttons; - xPosLast = xPos; - yPosLast = yPos; - Sleep(1); - } - return 0; -} diff --git a/src/lib/plugin/winmmjoy/winmmjoy.h b/src/lib/plugin/winmmjoy/winmmjoy.h deleted file mode 100644 index 712bd6d99..000000000 --- a/src/lib/plugin/winmmjoy/winmmjoy.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012-2016 Symless Ltd. - * Copyright (C) 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 . - */ - -#pragma once - -#define WIN32_LEAN_AND_MEAN -#include - -#if defined(winmmjoy_EXPORTS) -#define WINMMJOY_API __declspec(dllexport) -#else -#define WINMMJOY_API __declspec(dllimport) -#endif - -extern "C" { - -WINMMJOY_API void init(void* log, void* arch); -WINMMJOY_API int initEvent(void (*sendEvent)(const char*, void*)); -WINMMJOY_API void cleanup(); - -} - -DWORD WINAPI mainLoop(void* data); - -typedef unsigned char GameDeviceID; -typedef unsigned short GameDeviceButton; - -class CGameDeviceButtonInfo { -public: - CGameDeviceButtonInfo(GameDeviceID id, GameDeviceButton buttons) : - m_id(id), m_buttons(buttons) { } -public: - GameDeviceID m_id; - GameDeviceButton m_buttons; -}; - -class CGameDeviceStickInfo { -public: - CGameDeviceStickInfo(GameDeviceID id, short x1, short y1, short x2, short y2) : - m_id(id), m_x1(x1), m_x2(x2), m_y1(y1), m_y2(y2) { } -public: - GameDeviceID m_id; - short m_x1; - short m_x2; - short m_y1; - short m_y2; -}; - -class CGameDeviceTriggerInfo { -public: - CGameDeviceTriggerInfo(GameDeviceID id, unsigned char t1, unsigned char t2) : - m_id(id), m_t1(t1), m_t2(t2) { } -public: - GameDeviceID m_id; - unsigned char m_t1; - unsigned char m_t2; -}; From 27ccddbea434a4c436600c5b46779db47b369dc3 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 12:23:09 +0100 Subject: [PATCH 007/241] #5617 Remove plugin infra from ClientListener --- src/lib/server/ClientListener.cpp | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index cf64bc307..8e750ea0c 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -46,14 +46,6 @@ ClientListener::ClientListener(const NetworkAddress& address, assert(m_socketFactory != NULL); try { - // create listen socket - if (enableCrypto) { - m_useSecureNetwork = ARCH->plugin().exists(s_pluginNames[kSecureSocket]); - if (m_useSecureNetwork == false) { - LOG((CLOG_NOTE "crypto disabled because of ns plugin not available")); - } - } - m_listen = m_socketFactory->createListen(m_useSecureNetwork); // setup event handler @@ -250,13 +242,5 @@ ClientListener::handleClientDisconnected(const Event&, void* vclient) void ClientListener::cleanupListenSocket() { - if (!m_useSecureNetwork) { - delete m_listen; - } - else { - ARCH->plugin().invoke( - s_pluginNames[kSecureSocket], - "deleteListenSocket", - NULL); - } + delete m_listen; } From 5774f5a29197887d33bceb63bee4dc75c25d72ea Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 12:25:40 +0100 Subject: [PATCH 008/241] #5617 Remove plugin infra from Client --- src/lib/client/Client.cpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 748c8cb7c..b1a5268ff 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -18,7 +18,6 @@ #include "client/Client.h" -#include "../plugin/ns/SecureSocket.h" #include "client/ServerProxy.h" #include "synergy/Screen.h" #include "synergy/FileChunk.h" @@ -33,6 +32,7 @@ #include "net/TCPSocket.h" #include "net/IDataSocket.h" #include "net/ISocketFactory.h" +#include "net/SecureSocket.h" #include "arch/Arch.h" #include "base/Log.h" #include "base/IEventQueue.h" @@ -99,13 +99,6 @@ Client::Client( new TMethodEventJob(this, &Client::handleFileRecieveCompleted)); } - - if (m_args.m_enableCrypto) { - m_useSecureNetwork = ARCH->plugin().exists(s_pluginNames[kSecureSocket]); - if (m_useSecureNetwork == false) { - LOG((CLOG_NOTE "crypto disabled because of ns plugin not available")); - } - } } Client::~Client() @@ -593,13 +586,6 @@ Client::cleanupStream() { delete m_stream; m_stream = NULL; - - // PacketStreamFilter doen't adopt secure socket, because - // we need to tell the dynamic lib that allocated this object - // to do the deletion. - if (m_useSecureNetwork) { - ARCH->plugin().invoke(s_pluginNames[kSecureSocket], "deleteSocket", NULL); - } } void From dc93b063b7f12d7cf006297136b91b5b46d6da5e Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 12:29:03 +0100 Subject: [PATCH 009/241] #5617 Remove plugin infra from TCPSocketFactory --- src/lib/net/TCPSocketFactory.cpp | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/lib/net/TCPSocketFactory.cpp b/src/lib/net/TCPSocketFactory.cpp index 9f8dc0290..a639710ab 100644 --- a/src/lib/net/TCPSocketFactory.cpp +++ b/src/lib/net/TCPSocketFactory.cpp @@ -17,9 +17,9 @@ */ #include "net/TCPSocketFactory.h" - #include "net/TCPSocket.h" #include "net/TCPListenSocket.h" +#include "net/SecureListenSocket.h" #include "arch/Arch.h" #include "common/PluginVersion.h" #include "base/Log.h" @@ -45,12 +45,7 @@ TCPSocketFactory::create(bool secure) const { IDataSocket* socket = NULL; if (secure) { - void* args[2] = { - m_events, - m_socketMultiplexer - }; - socket = static_cast( - ARCH->plugin().invoke(s_pluginNames[kSecureSocket], "getSocket", args)); + socket = new SecureSocket(m_events, m_socketMultiplexer); } else { socket = new TCPSocket(m_events, m_socketMultiplexer); @@ -64,12 +59,7 @@ TCPSocketFactory::createListen(bool secure) const { IListenSocket* socket = NULL; if (secure) { - void* args[2] = { - m_events, - m_socketMultiplexer - }; - socket = static_cast( - ARCH->plugin().invoke(s_pluginNames[kSecureSocket], "getListenSocket", args)); + socket = new SecureListenSocket(m_events, m_socketMultiplexer); } else { socket = new TCPListenSocket(m_events, m_socketMultiplexer); From f7e588dfff1ce2072db8c858ba3923cb76e91bfa Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 12:31:24 +0100 Subject: [PATCH 010/241] #5617 Remove plugin directory support --- src/lib/arch/unix/ArchFileUnix.cpp | 20 -------------------- src/lib/arch/win32/ArchFileWindows.cpp | 18 ------------------ 2 files changed, 38 deletions(-) diff --git a/src/lib/arch/unix/ArchFileUnix.cpp b/src/lib/arch/unix/ArchFileUnix.cpp index 9dcc0b078..4aaddae43 100644 --- a/src/lib/arch/unix/ArchFileUnix.cpp +++ b/src/lib/arch/unix/ArchFileUnix.cpp @@ -104,20 +104,6 @@ ArchFileUnix::getLogDirectory() return "/var/log"; } -std::string -ArchFileUnix::getPluginDirectory() -{ - if (!m_pluginDirectory.empty()) { - return m_pluginDirectory; - } - -#if WINAPI_XWINDOWS - return getProfileDirectory().append("/plugins"); -#else - return getProfileDirectory().append("/Plugins"); -#endif -} - std::string ArchFileUnix::getProfileDirectory() { @@ -155,9 +141,3 @@ ArchFileUnix::setProfileDirectory(const String& s) { m_profileDirectory = s; } - -void -ArchFileUnix::setPluginDirectory(const String& s) -{ - m_pluginDirectory = s; -} diff --git a/src/lib/arch/win32/ArchFileWindows.cpp b/src/lib/arch/win32/ArchFileWindows.cpp index 373df72c8..322323a48 100644 --- a/src/lib/arch/win32/ArchFileWindows.cpp +++ b/src/lib/arch/win32/ArchFileWindows.cpp @@ -139,18 +139,6 @@ ArchFileWindows::getLogDirectory() return getInstalledDirectory(); } -std::string -ArchFileWindows::getPluginDirectory() -{ - if (!m_pluginDirectory.empty()) { - return m_pluginDirectory; - } - - std::string dir = getProfileDirectory(); - dir.append("\\Plugins"); - return dir; -} - std::string ArchFileWindows::getProfileDirectory() { @@ -195,9 +183,3 @@ ArchFileWindows::setProfileDirectory(const String& s) { m_profileDirectory = s; } - -void -ArchFileWindows::setPluginDirectory(const String& s) -{ - m_pluginDirectory = s; -} From 85227f41a1827a286a5e966ab6f310823f052759 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 12:33:34 +0100 Subject: [PATCH 011/241] #5617 Remove plugin infra from ServerApp --- src/lib/synergy/ServerApp.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/lib/synergy/ServerApp.cpp b/src/lib/synergy/ServerApp.cpp index 0153f9a20..52e4331fa 100644 --- a/src/lib/synergy/ServerApp.cpp +++ b/src/lib/synergy/ServerApp.cpp @@ -707,11 +707,6 @@ ServerApp::mainLoop() return kExitFailed; } - // load all available plugins. - ARCH->plugin().load(); - // pass log and arch into plugins. - ARCH->plugin().init(Log::getInstance(), Arch::getInstance()); - // start server, etc appUtil().startNode(); @@ -721,9 +716,6 @@ ServerApp::mainLoop() initIpcClient(); } - // init event for all available plugins. - ARCH->plugin().initEvent(m_serverScreen->getEventTarget(), m_events); - // handle hangup signal by reloading the server's configuration ARCH->setSignalHandler(Arch::kHANGUP, &reloadSignalHandler, NULL); m_events->adoptHandler(m_events->forServerApp().reloadConfig(), @@ -780,9 +772,6 @@ ServerApp::mainLoop() cleanupIpcClient(); } - // unload all plugins. - ARCH->plugin().unload(); - return kExitSuccess; } From 011da60cca63f4068211f7f3ea635d30605b7b00 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 12:35:15 +0100 Subject: [PATCH 012/241] #5617 Remove plugin infra from ClientApp --- src/lib/synergy/ClientApp.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/lib/synergy/ClientApp.cpp b/src/lib/synergy/ClientApp.cpp index 6adac9b98..54f9f687e 100644 --- a/src/lib/synergy/ClientApp.cpp +++ b/src/lib/synergy/ClientApp.cpp @@ -455,11 +455,6 @@ ClientApp::mainLoop() SocketMultiplexer multiplexer; setSocketMultiplexer(&multiplexer); - // load all available plugins. - ARCH->plugin().load(); - // pass log and arch into plugins. - ARCH->plugin().init(Log::getInstance(), Arch::getInstance()); - // start client, etc appUtil().startNode(); @@ -469,9 +464,6 @@ ClientApp::mainLoop() initIpcClient(); } - // init event for all available plugins. - ARCH->plugin().initEvent(m_clientScreen->getEventTarget(), m_events); - // run event loop. if startClient() failed we're supposed to retry // later. the timer installed by startClient() will take care of // that. @@ -506,9 +498,6 @@ ClientApp::mainLoop() cleanupIpcClient(); } - // unload all plugins. - ARCH->plugin().unload(); - return kExitSuccess; } From a2ad4cb0dca7d2e4abdb24961eef2b01777b3c4c Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 12:43:05 +0100 Subject: [PATCH 013/241] #5617 Remove plugin args from ArgParser --- src/lib/synergy/ArgParser.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 431a91b73..a77a8d852 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -189,18 +189,10 @@ ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv) args.m_loginAuthenticate = true; return true; } - else if (isArg(i, argc, argv, NULL, "--get-plugin-list", 0)) { - args.m_getPluginList = true; - return true; - } else if (isArg(i, argc, argv, NULL, "--get-installed-dir", 0)) { args.m_getInstalledDir = true; return true; } - else if (isArg(i, argc, argv, NULL, "--get-plugin-dir", 0)) { - args.m_getPluginDir = true; - return true; - } else if (isArg(i, argc, argv, NULL, "--get-profile-dir", 0)) { args.m_getProfileDir = true; return true; @@ -330,9 +322,6 @@ ArgParser::parseGenericArgs(int argc, const char* const* argv, int& i) else if (isArg(i, argc, argv, NULL, "--profile-dir", 1)) { argsBase().m_profileDirectory = argv[++i]; } - else if (isArg(i, argc, argv, NULL, "--plugin-dir", 1)) { - argsBase().m_pluginDirectory = argv[++i]; - } else { // option not supported here return false; @@ -348,6 +337,10 @@ ArgParser::parseDeprecatedArgs(int argc, const char* const* argv, int& i) LOG((CLOG_NOTE "--crypto-pass is deprecated")); i++; return true; + } else if (isArg(i, argc, argv, NULL, "--plugin-dir", 1)) { + LOG((CLOG_NOTE "--plugin-dir is deprecated")); + ++i; + return true; } return false; From aee8e2874e030fb64d9741d219910d600fd3573a Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 12:51:16 +0100 Subject: [PATCH 014/241] #5617 Remove plugins from Windows installer --- src/setup/win32/Product.wxs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/setup/win32/Product.wxs b/src/setup/win32/Product.wxs index 552f76d1d..03a309e81 100644 --- a/src/setup/win32/Product.wxs +++ b/src/setup/win32/Product.wxs @@ -27,7 +27,6 @@ - @@ -70,7 +69,6 @@ - @@ -140,11 +138,5 @@ - - - - - - From 77d6b83b0c254aeb85a2edc7b8a01ba31d7dd554 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 12:54:09 +0100 Subject: [PATCH 015/241] #5617 Remove stray PluginVersion includes --- src/lib/client/Client.cpp | 1 - src/lib/net/TCPSocketFactory.cpp | 1 - src/lib/server/ClientListener.cpp | 1 - 3 files changed, 3 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index b1a5268ff..4090c3515 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -38,7 +38,6 @@ #include "base/IEventQueue.h" #include "base/TMethodEventJob.h" #include "base/TMethodJob.h" -#include "common/PluginVersion.h" #include "common/stdexcept.h" #include diff --git a/src/lib/net/TCPSocketFactory.cpp b/src/lib/net/TCPSocketFactory.cpp index a639710ab..3fcb249f3 100644 --- a/src/lib/net/TCPSocketFactory.cpp +++ b/src/lib/net/TCPSocketFactory.cpp @@ -21,7 +21,6 @@ #include "net/TCPListenSocket.h" #include "net/SecureListenSocket.h" #include "arch/Arch.h" -#include "common/PluginVersion.h" #include "base/Log.h" // diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index 8e750ea0c..3bd7656b0 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -25,7 +25,6 @@ #include "net/IListenSocket.h" #include "net/ISocketFactory.h" #include "net/XSocket.h" -#include "common/PluginVersion.h" #include "base/Log.h" #include "base/IEventQueue.h" #include "base/TMethodEventJob.h" From 45ef3e1080a45460e64c88ce52a4e967c5611047 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 12:57:30 +0100 Subject: [PATCH 016/241] #5617 Remove plugin interface from Arch --- src/lib/arch/Arch.h | 4 ---- src/lib/arch/IArchFile.h | 14 -------------- src/lib/arch/unix/ArchFileUnix.h | 3 --- src/lib/arch/win32/ArchFileWindows.h | 3 --- 4 files changed, 24 deletions(-) diff --git a/src/lib/arch/Arch.h b/src/lib/arch/Arch.h index 3c5a23ff2..42be6eb8e 100644 --- a/src/lib/arch/Arch.h +++ b/src/lib/arch/Arch.h @@ -50,7 +50,6 @@ # include "arch/win32/ArchSystemWindows.h" # include "arch/win32/ArchTaskBarWindows.h" # include "arch/win32/ArchTimeWindows.h" -# include "arch/win32/ArchPluginWindows.h" # include "arch/win32/ArchInternetWindows.h" #elif SYSAPI_UNIX # include "arch/unix/ArchConsoleUnix.h" @@ -66,7 +65,6 @@ # include "arch/unix/ArchSystemUnix.h" # include "arch/unix/ArchTaskBarXWindows.h" # include "arch/unix/ArchTimeUnix.h" -# include "arch/unix/ArchPluginUnix.h" # include "arch/unix/ArchInternetUnix.h" #endif @@ -122,12 +120,10 @@ class Arch : public ARCH_CONSOLE, static void setInstance(Arch* s) { s_instance = s; } - ARCH_PLUGIN& plugin() const { return (ARCH_PLUGIN&)m_plugin; } ARCH_INTERNET& internet() const { return (ARCH_INTERNET&)m_internet; } private: static Arch* s_instance; - ARCH_PLUGIN m_plugin; ARCH_INTERNET m_internet; }; diff --git a/src/lib/arch/IArchFile.h b/src/lib/arch/IArchFile.h index bcbba6e0d..da6623f27 100644 --- a/src/lib/arch/IArchFile.h +++ b/src/lib/arch/IArchFile.h @@ -63,13 +63,6 @@ class IArchFile : public IInterface { */ virtual std::string getLogDirectory() = 0; - //! Get plugins directory - /*! - Returns the plugin files directory. If no plugin directory is set, - this will return the plugin folder within the user's profile. - */ - virtual std::string getPluginDirectory() = 0; - //! Get user's profile directory /*! Returns the user's profile directory. If no profile directory is set, @@ -95,11 +88,4 @@ class IArchFile : public IInterface { Returns the user's profile directory. */ virtual void setProfileDirectory(const String& s) = 0; - - //@} - //! Set the user's plugin directory - /* - Returns the user's plugin directory. - */ - virtual void setPluginDirectory(const String& s) = 0; }; diff --git a/src/lib/arch/unix/ArchFileUnix.h b/src/lib/arch/unix/ArchFileUnix.h index cbf78668d..6d3f1e3c4 100644 --- a/src/lib/arch/unix/ArchFileUnix.h +++ b/src/lib/arch/unix/ArchFileUnix.h @@ -34,14 +34,11 @@ class ArchFileUnix : public IArchFile { virtual std::string getSystemDirectory(); virtual std::string getInstalledDirectory(); virtual std::string getLogDirectory(); - virtual std::string getPluginDirectory(); virtual std::string getProfileDirectory(); virtual std::string concatPath(const std::string& prefix, const std::string& suffix); virtual void setProfileDirectory(const String& s); - virtual void setPluginDirectory(const String& s); private: String m_profileDirectory; - String m_pluginDirectory; }; diff --git a/src/lib/arch/win32/ArchFileWindows.h b/src/lib/arch/win32/ArchFileWindows.h index cdb8e4a23..032b796e1 100644 --- a/src/lib/arch/win32/ArchFileWindows.h +++ b/src/lib/arch/win32/ArchFileWindows.h @@ -34,14 +34,11 @@ class ArchFileWindows : public IArchFile { virtual std::string getSystemDirectory(); virtual std::string getInstalledDirectory(); virtual std::string getLogDirectory(); - virtual std::string getPluginDirectory(); virtual std::string getProfileDirectory(); virtual std::string concatPath(const std::string& prefix, const std::string& suffix); virtual void setProfileDirectory(const String& s); - virtual void setPluginDirectory(const String& s); private: String m_profileDirectory; - String m_pluginDirectory; }; From b1a991e8cd224e9d0d265666c4617211713ce528 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 13:05:55 +0100 Subject: [PATCH 017/241] #5617 Revert "Remove plugin directory support" This reverts commit fc697d2ab79bbd2d607c97658c986b629a1280ed. --- src/lib/arch/unix/ArchFileUnix.cpp | 20 ++++++++++++++++++++ src/lib/arch/win32/ArchFileWindows.cpp | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/lib/arch/unix/ArchFileUnix.cpp b/src/lib/arch/unix/ArchFileUnix.cpp index 4aaddae43..9dcc0b078 100644 --- a/src/lib/arch/unix/ArchFileUnix.cpp +++ b/src/lib/arch/unix/ArchFileUnix.cpp @@ -104,6 +104,20 @@ ArchFileUnix::getLogDirectory() return "/var/log"; } +std::string +ArchFileUnix::getPluginDirectory() +{ + if (!m_pluginDirectory.empty()) { + return m_pluginDirectory; + } + +#if WINAPI_XWINDOWS + return getProfileDirectory().append("/plugins"); +#else + return getProfileDirectory().append("/Plugins"); +#endif +} + std::string ArchFileUnix::getProfileDirectory() { @@ -141,3 +155,9 @@ ArchFileUnix::setProfileDirectory(const String& s) { m_profileDirectory = s; } + +void +ArchFileUnix::setPluginDirectory(const String& s) +{ + m_pluginDirectory = s; +} diff --git a/src/lib/arch/win32/ArchFileWindows.cpp b/src/lib/arch/win32/ArchFileWindows.cpp index 322323a48..373df72c8 100644 --- a/src/lib/arch/win32/ArchFileWindows.cpp +++ b/src/lib/arch/win32/ArchFileWindows.cpp @@ -139,6 +139,18 @@ ArchFileWindows::getLogDirectory() return getInstalledDirectory(); } +std::string +ArchFileWindows::getPluginDirectory() +{ + if (!m_pluginDirectory.empty()) { + return m_pluginDirectory; + } + + std::string dir = getProfileDirectory(); + dir.append("\\Plugins"); + return dir; +} + std::string ArchFileWindows::getProfileDirectory() { @@ -183,3 +195,9 @@ ArchFileWindows::setProfileDirectory(const String& s) { m_profileDirectory = s; } + +void +ArchFileWindows::setPluginDirectory(const String& s) +{ + m_pluginDirectory = s; +} From b55b8f00383be23b314e84f51632388408653129 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 13:08:12 +0100 Subject: [PATCH 018/241] #5617 Revert "Remove plugin args from ArgParser" This reverts commit cd58a8f0d0abe344d8e31a817386f613c7acde25. --- src/lib/synergy/ArgParser.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index a77a8d852..431a91b73 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -189,10 +189,18 @@ ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv) args.m_loginAuthenticate = true; return true; } + else if (isArg(i, argc, argv, NULL, "--get-plugin-list", 0)) { + args.m_getPluginList = true; + return true; + } else if (isArg(i, argc, argv, NULL, "--get-installed-dir", 0)) { args.m_getInstalledDir = true; return true; } + else if (isArg(i, argc, argv, NULL, "--get-plugin-dir", 0)) { + args.m_getPluginDir = true; + return true; + } else if (isArg(i, argc, argv, NULL, "--get-profile-dir", 0)) { args.m_getProfileDir = true; return true; @@ -322,6 +330,9 @@ ArgParser::parseGenericArgs(int argc, const char* const* argv, int& i) else if (isArg(i, argc, argv, NULL, "--profile-dir", 1)) { argsBase().m_profileDirectory = argv[++i]; } + else if (isArg(i, argc, argv, NULL, "--plugin-dir", 1)) { + argsBase().m_pluginDirectory = argv[++i]; + } else { // option not supported here return false; @@ -337,10 +348,6 @@ ArgParser::parseDeprecatedArgs(int argc, const char* const* argv, int& i) LOG((CLOG_NOTE "--crypto-pass is deprecated")); i++; return true; - } else if (isArg(i, argc, argv, NULL, "--plugin-dir", 1)) { - LOG((CLOG_NOTE "--plugin-dir is deprecated")); - ++i; - return true; } return false; From 5f5153f450737666b1c782f23f9f69c4edd9d400 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 13:15:44 +0100 Subject: [PATCH 019/241] #5617 Remove the plugin wizard from GUI --- src/gui/gui.pro | 11 +---------- src/gui/src/MainWindow.h | 3 +-- src/gui/src/SetupWizard.cpp | 5 ----- src/gui/src/SetupWizard.h | 2 -- 4 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 408d44dda..28eca39e2 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -16,8 +16,7 @@ FORMS += res/MainWindowBase.ui \ res/HotkeyDialogBase.ui \ res/SettingsDialogBase.ui \ res/SetupWizardBase.ui \ - res/AddClientDialogBase.ui \ - res/PluginWizardPageBase.ui + res/AddClientDialogBase.ui SOURCES += src/main.cpp \ src/MainWindow.cpp \ src/AboutDialog.cpp \ @@ -54,14 +53,10 @@ SOURCES += src/main.cpp \ src/DataDownloader.cpp \ src/AddClientDialog.cpp \ src/CommandProcess.cpp \ - src/PluginWizardPage.cpp \ - src/PluginManager.cpp \ src/CoreInterface.cpp \ src/Fingerprint.cpp \ src/SslCertificate.cpp \ - src/Plugin.cpp \ src/WebClient.cpp \ - ../lib/common/PluginVersion.cpp \ src/SubscriptionManager.cpp \ src/ActivationNotifier.cpp HEADERS += src/MainWindow.h \ @@ -101,15 +96,11 @@ HEADERS += src/MainWindow.h \ src/AddClientDialog.h \ src/CommandProcess.h \ src/EditionType.h \ - src/PluginWizardPage.h \ src/ProcessorArch.h \ - src/PluginManager.h \ src/CoreInterface.h \ src/Fingerprint.h \ src/SslCertificate.h \ - src/Plugin.h \ src/WebClient.h \ - ../lib/common/PluginVersion.h \ src/SubscriptionManager.h \ src/ActivationNotifier.h \ src/ElevateMode.h diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 87380f963..d6d140553 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -63,8 +63,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase friend class QSynergyApplication; friend class SetupWizard; - friend class PluginWizardPage; - + public: enum qSynergyState { diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index 3d23b017f..ed574e04e 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -33,8 +33,6 @@ SetupWizard::SetupWizard(MainWindow& mainWindow, bool startMain) : m_LoginAttemps(0) { setupUi(this); - m_pPluginPage = new PluginWizardPage(mainWindow); - addPage(m_pPluginPage); #if defined(Q_OS_MAC) @@ -114,7 +112,6 @@ bool SetupWizard::validateCurrentPage() return false; } else { - m_pPluginPage->setEdition(m_Edition); return true; } } @@ -132,8 +129,6 @@ bool SetupWizard::validateCurrentPage() return false; } - m_pPluginPage->setEdition(m_Edition); - return true; } } diff --git a/src/gui/src/SetupWizard.h b/src/gui/src/SetupWizard.h index 34b301a25..fd6dcd14d 100644 --- a/src/gui/src/SetupWizard.h +++ b/src/gui/src/SetupWizard.h @@ -19,7 +19,6 @@ #include "ui_SetupWizardBase.h" #include "SynergyLocale.h" -#include "PluginWizardPage.h" #include #include @@ -50,7 +49,6 @@ class SetupWizard : public QWizard, public Ui::SetupWizardBase bool m_StartMain; SynergyLocale m_Locale; int m_Edition; - PluginWizardPage* m_pPluginPage; int m_LoginAttemps; private slots: From c3889667ba3a0b3a52db4e6a239c55642430fee3 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 13:22:23 +0100 Subject: [PATCH 020/241] #5617 Remove online check for plugins --- src/lib/synergy/ToolApp.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index b024bfe94..e6695f518 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -174,19 +174,6 @@ ToolApp::loginAuth() void ToolApp::getPluginList() { - 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::stringstream ss; - ss << JSON_URL << "plugins/"; - ss << "?email=" << ARCH->internet().urlEncode(email); - ss << "&password=" << password; - - std::cout << ARCH->internet().get(ss.str()) << std::endl; } void From 79fc6239fd82b9c17562dbb6f2ce948afa978efa Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 14:43:14 +0100 Subject: [PATCH 021/241] #5617 Revert "Remove plugin interface from Arch" This reverts commit 4613edc17ced61d6aca80cff661323ede0cb9d30. --- src/lib/arch/Arch.h | 4 ++++ src/lib/arch/IArchFile.h | 14 ++++++++++++++ src/lib/arch/unix/ArchFileUnix.h | 3 +++ src/lib/arch/win32/ArchFileWindows.h | 3 +++ 4 files changed, 24 insertions(+) diff --git a/src/lib/arch/Arch.h b/src/lib/arch/Arch.h index 42be6eb8e..3c5a23ff2 100644 --- a/src/lib/arch/Arch.h +++ b/src/lib/arch/Arch.h @@ -50,6 +50,7 @@ # include "arch/win32/ArchSystemWindows.h" # include "arch/win32/ArchTaskBarWindows.h" # include "arch/win32/ArchTimeWindows.h" +# include "arch/win32/ArchPluginWindows.h" # include "arch/win32/ArchInternetWindows.h" #elif SYSAPI_UNIX # include "arch/unix/ArchConsoleUnix.h" @@ -65,6 +66,7 @@ # include "arch/unix/ArchSystemUnix.h" # include "arch/unix/ArchTaskBarXWindows.h" # include "arch/unix/ArchTimeUnix.h" +# include "arch/unix/ArchPluginUnix.h" # include "arch/unix/ArchInternetUnix.h" #endif @@ -120,10 +122,12 @@ class Arch : public ARCH_CONSOLE, static void setInstance(Arch* s) { s_instance = s; } + ARCH_PLUGIN& plugin() const { return (ARCH_PLUGIN&)m_plugin; } ARCH_INTERNET& internet() const { return (ARCH_INTERNET&)m_internet; } private: static Arch* s_instance; + ARCH_PLUGIN m_plugin; ARCH_INTERNET m_internet; }; diff --git a/src/lib/arch/IArchFile.h b/src/lib/arch/IArchFile.h index da6623f27..bcbba6e0d 100644 --- a/src/lib/arch/IArchFile.h +++ b/src/lib/arch/IArchFile.h @@ -63,6 +63,13 @@ class IArchFile : public IInterface { */ virtual std::string getLogDirectory() = 0; + //! Get plugins directory + /*! + Returns the plugin files directory. If no plugin directory is set, + this will return the plugin folder within the user's profile. + */ + virtual std::string getPluginDirectory() = 0; + //! Get user's profile directory /*! Returns the user's profile directory. If no profile directory is set, @@ -88,4 +95,11 @@ class IArchFile : public IInterface { Returns the user's profile directory. */ virtual void setProfileDirectory(const String& s) = 0; + + //@} + //! Set the user's plugin directory + /* + Returns the user's plugin directory. + */ + virtual void setPluginDirectory(const String& s) = 0; }; diff --git a/src/lib/arch/unix/ArchFileUnix.h b/src/lib/arch/unix/ArchFileUnix.h index 6d3f1e3c4..cbf78668d 100644 --- a/src/lib/arch/unix/ArchFileUnix.h +++ b/src/lib/arch/unix/ArchFileUnix.h @@ -34,11 +34,14 @@ class ArchFileUnix : public IArchFile { virtual std::string getSystemDirectory(); virtual std::string getInstalledDirectory(); virtual std::string getLogDirectory(); + virtual std::string getPluginDirectory(); virtual std::string getProfileDirectory(); virtual std::string concatPath(const std::string& prefix, const std::string& suffix); virtual void setProfileDirectory(const String& s); + virtual void setPluginDirectory(const String& s); private: String m_profileDirectory; + String m_pluginDirectory; }; diff --git a/src/lib/arch/win32/ArchFileWindows.h b/src/lib/arch/win32/ArchFileWindows.h index 032b796e1..cdb8e4a23 100644 --- a/src/lib/arch/win32/ArchFileWindows.h +++ b/src/lib/arch/win32/ArchFileWindows.h @@ -34,11 +34,14 @@ class ArchFileWindows : public IArchFile { virtual std::string getSystemDirectory(); virtual std::string getInstalledDirectory(); virtual std::string getLogDirectory(); + virtual std::string getPluginDirectory(); virtual std::string getProfileDirectory(); virtual std::string concatPath(const std::string& prefix, const std::string& suffix); virtual void setProfileDirectory(const String& s); + virtual void setPluginDirectory(const String& s); private: String m_profileDirectory; + String m_pluginDirectory; }; From 645627114184f502f2ea5c1157e4a257c153d621 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 14:44:13 +0100 Subject: [PATCH 022/241] #5617 Remove plugin interface from Arch --- src/lib/arch/Arch.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/lib/arch/Arch.h b/src/lib/arch/Arch.h index 3c5a23ff2..42be6eb8e 100644 --- a/src/lib/arch/Arch.h +++ b/src/lib/arch/Arch.h @@ -50,7 +50,6 @@ # include "arch/win32/ArchSystemWindows.h" # include "arch/win32/ArchTaskBarWindows.h" # include "arch/win32/ArchTimeWindows.h" -# include "arch/win32/ArchPluginWindows.h" # include "arch/win32/ArchInternetWindows.h" #elif SYSAPI_UNIX # include "arch/unix/ArchConsoleUnix.h" @@ -66,7 +65,6 @@ # include "arch/unix/ArchSystemUnix.h" # include "arch/unix/ArchTaskBarXWindows.h" # include "arch/unix/ArchTimeUnix.h" -# include "arch/unix/ArchPluginUnix.h" # include "arch/unix/ArchInternetUnix.h" #endif @@ -122,12 +120,10 @@ class Arch : public ARCH_CONSOLE, static void setInstance(Arch* s) { s_instance = s; } - ARCH_PLUGIN& plugin() const { return (ARCH_PLUGIN&)m_plugin; } ARCH_INTERNET& internet() const { return (ARCH_INTERNET&)m_internet; } private: static Arch* s_instance; - ARCH_PLUGIN m_plugin; ARCH_INTERNET m_internet; }; From 52c8763d97c0ff0b1122e0468ed752a714de27d3 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 14:46:48 +0100 Subject: [PATCH 023/241] #5617 Add SecureSocket to TCPSocketFactory --- src/lib/net/TCPSocketFactory.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/net/TCPSocketFactory.cpp b/src/lib/net/TCPSocketFactory.cpp index 3fcb249f3..87d4caa3f 100644 --- a/src/lib/net/TCPSocketFactory.cpp +++ b/src/lib/net/TCPSocketFactory.cpp @@ -19,6 +19,7 @@ #include "net/TCPSocketFactory.h" #include "net/TCPSocket.h" #include "net/TCPListenSocket.h" +#include "net/SecureSocket.h" #include "net/SecureListenSocket.h" #include "arch/Arch.h" #include "base/Log.h" From 5db78acab4da367a9bd42997e769ae631e086391 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 15:01:58 +0100 Subject: [PATCH 024/241] #5628 Link Synergy core against OpenSSL --- src/CMakeLists.txt | 32 ++++++++++++++++++++++++++++++++ src/cmd/synergyc/CMakeLists.txt | 2 +- src/cmd/synergyd/CMakeLists.txt | 2 +- src/cmd/synergyp/CMakeLists.txt | 2 +- src/cmd/synergys/CMakeLists.txt | 2 +- src/cmd/syntool/CMakeLists.txt | 2 +- src/lib/net/CMakeLists.txt | 1 + src/test/integtests/CMakeLists.txt | 2 +- src/test/unittests/CMakeLists.txt | 2 +- 9 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d75c0adcc..237ba4843 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,38 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +if (WIN32) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(OPENSSL_PLAT_DIR openssl-win64) + else() + set(OPENSSL_PLAT_DIR openssl-win32) + endif() + set(OPENSSL_INCLUDE ${CMAKE_SOURCE_DIR}/ext/${OPENSSL_PLAT_DIR}/inc32) +endif() + +if (APPLE) + set(OPENSSL_PLAT_DIR openssl-osx) + set(OPENSSL_INCLUDE ${CMAKE_SOURCE_DIR}/ext/${OPENSSL_PLAT_DIR}/include) +endif() + +if (WIN32) + set(OPENSSL_LIBS + ${CMAKE_SOURCE_DIR}/ext/${OPENSSL_PLAT_DIR}/out32dll/libeay32.lib + ${CMAKE_SOURCE_DIR}/ext/${OPENSSL_PLAT_DIR}/out32dll/ssleay32.lib + ) +endif() + +if (UNIX) + if (APPLE) + set(OPENSSL_LIBS + ${CMAKE_SOURCE_DIR}/ext/${OPENSSL_PLAT_DIR}/libssl.a + ${CMAKE_SOURCE_DIR}/ext/${OPENSSL_PLAT_DIR}/libcrypto.a + ) + else() + set(OPENSSL_LIBS ssl crypto) + endif() +endif() + add_subdirectory(lib) add_subdirectory(cmd) add_subdirectory(micro) diff --git a/src/cmd/synergyc/CMakeLists.txt b/src/cmd/synergyc/CMakeLists.txt index 11d428d44..22bbe2573 100644 --- a/src/cmd/synergyc/CMakeLists.txt +++ b/src/cmd/synergyc/CMakeLists.txt @@ -58,7 +58,7 @@ endif() add_executable(synergyc ${sources}) target_link_libraries(synergyc - arch base client common io mt net ipc platform server synergy ${libs}) + arch base client common io mt net ipc platform server synergy ${libs} ${OPENSSL_LIBS}) if (CONF_CPACK) install(TARGETS diff --git a/src/cmd/synergyd/CMakeLists.txt b/src/cmd/synergyd/CMakeLists.txt index 1caa35b52..005648f3d 100644 --- a/src/cmd/synergyd/CMakeLists.txt +++ b/src/cmd/synergyd/CMakeLists.txt @@ -35,7 +35,7 @@ else() endif() target_link_libraries(synergyd - arch base common io ipc mt net platform synergy ${libs}) + arch base common io ipc mt net platform synergy ${libs} ${OPENSSL_LIBS}) if (CONF_CPACK) install(TARGETS diff --git a/src/cmd/synergyp/CMakeLists.txt b/src/cmd/synergyp/CMakeLists.txt index 171ef9990..c99892528 100644 --- a/src/cmd/synergyp/CMakeLists.txt +++ b/src/cmd/synergyp/CMakeLists.txt @@ -63,7 +63,7 @@ else() endif() target_link_libraries(synergyp - arch base client common io mt net ipc platform server synergy client ${libs}) + arch base client common io mt net ipc platform server synergy client ${libs} ${OPENSSL_LIBS}) if (CONF_CPACK) install(TARGETS diff --git a/src/cmd/synergys/CMakeLists.txt b/src/cmd/synergys/CMakeLists.txt index c749e09a8..2474bcc67 100644 --- a/src/cmd/synergys/CMakeLists.txt +++ b/src/cmd/synergys/CMakeLists.txt @@ -58,7 +58,7 @@ endif() add_executable(synergys ${sources}) target_link_libraries(synergys - arch base client common io mt net ipc platform server synergy ${libs}) + arch base client common io mt net ipc platform server synergy ${libs} ${OPENSSL_LIBS}) if (CONF_CPACK) install(TARGETS diff --git a/src/cmd/syntool/CMakeLists.txt b/src/cmd/syntool/CMakeLists.txt index 7f10c010b..ffbb61aa0 100644 --- a/src/cmd/syntool/CMakeLists.txt +++ b/src/cmd/syntool/CMakeLists.txt @@ -29,7 +29,7 @@ endif() add_executable(syntool ${sources}) target_link_libraries(syntool - synergy arch base client common io ipc mt net platform server ${libs}) + synergy arch base client common io ipc mt net platform server ${libs} ${OPENSSL_LIBS}) if (CONF_CPACK) install(TARGETS diff --git a/src/lib/net/CMakeLists.txt b/src/lib/net/CMakeLists.txt index ee50fe364..6e3527e43 100644 --- a/src/lib/net/CMakeLists.txt +++ b/src/lib/net/CMakeLists.txt @@ -23,6 +23,7 @@ endif() include_directories( ../ + ${OPENSSL_INCLUDE} ) if (UNIX) diff --git a/src/test/integtests/CMakeLists.txt b/src/test/integtests/CMakeLists.txt index bde35c2fa..2f1ca7f39 100644 --- a/src/test/integtests/CMakeLists.txt +++ b/src/test/integtests/CMakeLists.txt @@ -68,4 +68,4 @@ endif() add_executable(integtests ${sources}) target_link_libraries(integtests - arch base client common io ipc mt net platform server synergy gtest gmock ${libs}) + arch base client common io ipc mt net platform server synergy gtest gmock ${libs} ${OPENSSL_LIBS}) diff --git a/src/test/unittests/CMakeLists.txt b/src/test/unittests/CMakeLists.txt index 4cacdf936..4cdab9bf1 100644 --- a/src/test/unittests/CMakeLists.txt +++ b/src/test/unittests/CMakeLists.txt @@ -68,4 +68,4 @@ endif() add_executable(unittests ${sources}) target_link_libraries(unittests - arch base client server common io net platform server synergy mt ipc gtest gmock ${libs}) + arch base client server common io net platform server synergy mt ipc gtest gmock ${libs} ${OPENSSL_LIBS}) From 1fceb2b6462857d0761e0a8a41eb5be0841c8373 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 15:53:50 +0100 Subject: [PATCH 025/241] #5617 Remove PluginManager from MainWindow --- src/gui/src/MainWindow.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 332fcbd65..fa1f48ecc 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -23,7 +23,6 @@ #include "MainWindow.h" #include "Fingerprint.h" -#include "PluginManager.h" #include "AboutDialog.h" #include "ServerConfigDialog.h" #include "SettingsDialog.h" From 964e6d2f12a92584100a108dd376480781f89c33 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 16:11:51 +0100 Subject: [PATCH 026/241] #5617 Remove PluginManager from SettingsDialog --- src/gui/src/SettingsDialog.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index 5d5b53d26..8f7544901 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -18,7 +18,6 @@ #include "SettingsDialog.h" -#include "PluginManager.h" #include "CoreInterface.h" #include "SynergyLocale.h" #include "QSynergyApplication.h" @@ -61,13 +60,7 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) : m_pComboElevate->hide(); #endif - if (!PluginManager::exist(networkSecurity)) { - m_pGroupNetworkSecurity->setEnabled(false); - m_pCheckBoxEnableCrypto->setChecked(false); - } - else { - m_pCheckBoxEnableCrypto->setChecked(m_AppConfig.getCryptoEnabled()); - } + m_pCheckBoxEnableCrypto->setChecked(m_AppConfig.getCryptoEnabled()); } void SettingsDialog::accept() From e18f8c62e7a1eaf6ee2b7e4852309d5df20ead06 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 16:20:29 +0100 Subject: [PATCH 027/241] #5617 Remove plugins support from toolchain --- ext/toolchain/commands1.py | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 79cfc6f1b..7f3570297 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -741,16 +741,6 @@ def macPostGuiMake(self, target): shutil.copy(targetDir + "/synergys", bundleBinDir) shutil.copy(targetDir + "/syntool", bundleBinDir) - # Copy all generated plugins to the package - bundlePluginDir = bundleBinDir + "plugins" - pluginDir = targetDir + "/plugins" - print "Copying plugins dirtree: " + pluginDir - if os.path.isdir(pluginDir): - print "Copying to: " + bundlePluginDir - shutil.copytree(pluginDir, bundlePluginDir) - else: - print "pluginDir doesn't exist, skipping" - self.loadConfig() if not self.macIdentity: raise Exception("run config with --mac-identity") @@ -1151,14 +1141,12 @@ def distDeb(self): controlFile.close() targetBin = '%s/%s/usr/bin' % (debDir, package) - targetPlugin = '%s/%s/usr/lib/synergy/plugins' % (debDir, package) targetShare = '%s/%s/usr/share' % (debDir, package) targetApplications = "%s/applications" % targetShare targetIcons = "%s/icons" % targetShare targetDocs = "%s/doc/%s" % (targetShare, self.project) os.makedirs(targetBin) - os.makedirs(targetPlugin) os.makedirs(targetApplications) os.makedirs(targetIcons) os.makedirs(targetDocs) @@ -1176,17 +1164,6 @@ def distDeb(self): if err != 0: raise Exception('strip failed: ' + str(err)) - pluginDir = "%s/plugins" % binDir - - pluginFiles = [ 'libns.so'] - for f in pluginFiles: - shutil.copy("%s/%s" % (pluginDir, f), targetPlugin) - target = "%s/%s" % (targetPlugin, f) - os.chmod(target, 0o0644) - err = os.system("strip " + target) - if err != 0: - raise Exception('strip failed: ' + str(err)) - shutil.copy("%s/synergy.desktop" % resDir, targetApplications) shutil.copy("%s/synergy.ico" % resDir, targetIcons) @@ -1402,13 +1379,6 @@ def distftp(self, type, ftp): packageTarget = filename ftp.upload(packageSource, packageTarget) - if type != 'src': - pluginsDir = binDir + '/plugins' - nsPluginSource = self.findLibraryFile(type, pluginsDir, 'ns') - if nsPluginSource: - nsPluginTarget = self.getLibraryDistFilename(type, pluginsDir, 'ns') - ftp.upload(nsPluginSource, nsPluginTarget, "plugins") - def getLibraryDistFilename(self, type, dir, name): (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) firstPart = '%s-%s-%s' % (name, self.getVersionForFilename(), platform) From 3b5940ac185d1c38133e113535f3dc2413f76e40 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 16:44:19 +0100 Subject: [PATCH 028/241] #5628 Restore copying of SSL libs on Windows --- src/lib/net/CMakeLists.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/lib/net/CMakeLists.txt b/src/lib/net/CMakeLists.txt index 6e3527e43..60a144995 100644 --- a/src/lib/net/CMakeLists.txt +++ b/src/lib/net/CMakeLists.txt @@ -34,6 +34,30 @@ endif() add_library(net STATIC ${sources}) +if (WIN32) + add_custom_command( + TARGET net + POST_BUILD + COMMAND xcopy /Y /Q + ..\\..\\..\\..\\lib\\${CMAKE_CFG_INTDIR}\\ns.* + ..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR}\\plugins\\ + ) + add_custom_command( + TARGET net + POST_BUILD + COMMAND xcopy /Y /Q + ..\\..\\..\\..\\ext\\${OPENSSL_PLAT_DIR}\\out32dll\\libeay32.* + ..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR} + ) + add_custom_command( + TARGET net + POST_BUILD + COMMAND xcopy /Y /Q + ..\\..\\..\\..\\ext\\${OPENSSL_PLAT_DIR}\\out32dll\\ssleay32.* + ..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR} + ) +endif() + if (UNIX) target_link_libraries(net mt io) endif() From f4f57e1f95730b68a30a84e9cc076451e1db962b Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 16:53:29 +0100 Subject: [PATCH 029/241] #5617 Remove plugins from RPM spec --- res/synergy.spec.in | 3 --- 1 file changed, 3 deletions(-) diff --git a/res/synergy.spec.in b/res/synergy.spec.in index 16b695e3e..3f0c88f93 100644 --- a/res/synergy.spec.in +++ b/res/synergy.spec.in @@ -20,7 +20,6 @@ source=%{_topdir}/../.. mkdir -p %{buildroot}/%{_datarootdir}/applications mkdir -p %{buildroot}/%{_datarootdir}/icons mkdir -p %{buildroot}/%{_bindir} -mkdir -p %{buildroot}/%{_bindir}/../lib/synergy/plugins cp $source/bin/synergy %{buildroot}%{_bindir} cp $source/bin/synergyc %{buildroot}%{_bindir} @@ -29,7 +28,6 @@ cp $source/bin/synergyd %{buildroot}%{_bindir} cp $source/bin/syntool %{buildroot}%{_bindir} cp $source/res/synergy.desktop %{buildroot}%{_datarootdir}/applications cp $source/res/synergy.ico %{buildroot}%{_datarootdir}/icons -cp $source/bin/plugins/* %{buildroot}%{_bindir}/../lib/synergy/plugins %files %defattr(755,root,root,-) @@ -40,7 +38,6 @@ cp $source/bin/plugins/* %{buildroot}%{_bindir}/../lib/synergy/plugins %{_bindir}/syntool %attr(644,-,-) %{_datarootdir}/applications/synergy.desktop %attr(644,-,-) %{_datarootdir}/icons/synergy.ico -%attr(644,-,-) %{_bindir}/../lib/synergy/plugins/* %changelog * Thu Mar 20 2014 Nick Bolton From 0d84e4eed6bf3e328cdb82da4f2ff6dbdae4922f Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 27 Sep 2016 17:02:44 +0100 Subject: [PATCH 030/241] #5617 Make PacketStreamFilter adopt all socket types --- src/lib/client/Client.cpp | 3 +-- src/lib/server/ClientListener.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 4090c3515..001bf2fab 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -152,8 +152,7 @@ Client::connect() // filter socket messages, including a packetizing filter m_stream = socket; - bool adopt = !m_useSecureNetwork; - m_stream = new PacketStreamFilter(m_events, m_stream, adopt); + m_stream = new PacketStreamFilter(m_events, m_stream, true); // connect LOG((CLOG_DEBUG1 "connecting to server")); diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index 3bd7656b0..f8d07c198 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -155,8 +155,7 @@ ClientListener::handleClientAccepted(const Event&, void* vsocket) IDataSocket* socket = static_cast(vsocket); // filter socket messages, including a packetizing filter - bool adopt = !m_useSecureNetwork; - synergy::IStream* stream = new PacketStreamFilter(m_events, socket, adopt); + synergy::IStream* stream = new PacketStreamFilter(m_events, socket, true); assert(m_server != NULL); // create proxy for unknown client From 55a2d1231fe86e7e856ac6da5462e3c5293d173a Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 28 Sep 2016 11:24:33 +0100 Subject: [PATCH 031/241] #5628 Initialize SSL on secure client sockets --- src/lib/net/TCPSocketFactory.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib/net/TCPSocketFactory.cpp b/src/lib/net/TCPSocketFactory.cpp index 87d4caa3f..e205a9fa2 100644 --- a/src/lib/net/TCPSocketFactory.cpp +++ b/src/lib/net/TCPSocketFactory.cpp @@ -43,15 +43,14 @@ TCPSocketFactory::~TCPSocketFactory() IDataSocket* TCPSocketFactory::create(bool secure) const { - IDataSocket* socket = NULL; if (secure) { - socket = new SecureSocket(m_events, m_socketMultiplexer); + SecureSocket* secureSocket = new SecureSocket(m_events, m_socketMultiplexer); + secureSocket->initSsl (false); + return secureSocket; } else { - socket = new TCPSocket(m_events, m_socketMultiplexer); + return new TCPSocket(m_events, m_socketMultiplexer); } - - return socket; } IListenSocket* From 45da1dfc7c54584708284f65adbac76cd10c3d0c Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 28 Sep 2016 11:37:33 +0100 Subject: [PATCH 032/241] #5628 Make enableCrypto directly enable SSL --- src/lib/client/Client.cpp | 2 +- src/lib/server/ClientListener.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 001bf2fab..d984be4cd 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -71,7 +71,7 @@ Client::Client( m_sendFileThread(NULL), m_writeToDropDirThread(NULL), m_socket(NULL), - m_useSecureNetwork(false), + m_useSecureNetwork(args.m_enableCrypto), m_args(args), m_enableClipboard(true) { diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index f8d07c198..a9777aae1 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -40,7 +40,7 @@ ClientListener::ClientListener(const NetworkAddress& address, m_socketFactory(socketFactory), m_server(NULL), m_events(events), - m_useSecureNetwork(false) + m_useSecureNetwork(enableCrypto) { assert(m_socketFactory != NULL); From 663e3f5854a8a632cb6afc34596c27e2faa58065 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 29 Sep 2016 13:45:06 +0100 Subject: [PATCH 033/241] #5629 Add rudimentary activation dialog --- src/gui/gui.pro | 9 ++++++--- src/gui/res/MainWindowBase.ui | 10 +++++++++- src/gui/src/MainWindow.cpp | 8 ++++++++ src/gui/src/MainWindow.h | 2 +- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 28eca39e2..df0887e61 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -16,7 +16,8 @@ FORMS += res/MainWindowBase.ui \ res/HotkeyDialogBase.ui \ res/SettingsDialogBase.ui \ res/SetupWizardBase.ui \ - res/AddClientDialogBase.ui + res/AddClientDialogBase.ui \ + res/ActivationDialog.ui SOURCES += src/main.cpp \ src/MainWindow.cpp \ src/AboutDialog.cpp \ @@ -58,7 +59,8 @@ SOURCES += src/main.cpp \ src/SslCertificate.cpp \ src/WebClient.cpp \ src/SubscriptionManager.cpp \ - src/ActivationNotifier.cpp + src/ActivationNotifier.cpp \ + src/ActivationDialog.cpp HEADERS += src/MainWindow.h \ src/AboutDialog.h \ src/ServerConfig.h \ @@ -103,7 +105,8 @@ HEADERS += src/MainWindow.h \ src/WebClient.h \ src/SubscriptionManager.h \ src/ActivationNotifier.h \ - src/ElevateMode.h + src/ElevateMode.h \ + src/ActivationDialog.h RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc macx { diff --git a/src/gui/res/MainWindowBase.ui b/src/gui/res/MainWindowBase.ui index 0a47e0d35..fa7955ccf 100644 --- a/src/gui/res/MainWindowBase.ui +++ b/src/gui/res/MainWindowBase.ui @@ -1,4 +1,4 @@ - + MainWindowBase @@ -489,6 +489,14 @@ + + + Activate + + + Activate + + diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index fa1f48ecc..966736e6c 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -27,6 +27,7 @@ #include "ServerConfigDialog.h" #include "SettingsDialog.h" #include "SetupWizard.h" +#include "ActivationDialog.h" #include "ZeroconfService.h" #include "DataDownloader.h" #include "CommandProcess.h" @@ -263,6 +264,7 @@ void MainWindow::createMenuBar() m_pMenuFile->addAction(m_pActionStopSynergy); m_pMenuFile->addSeparator(); m_pMenuFile->addAction(m_pActionWizard); + m_pMenuFile->addAction(m_pActivate); m_pMenuFile->addAction(m_pActionSave); m_pMenuFile->addSeparator(); m_pMenuFile->addAction(m_pActionQuit); @@ -1150,6 +1152,12 @@ void MainWindow::on_m_pActionWizard_triggered() wizard.exec(); } +void MainWindow::on_m_pActivate_triggered() +{ + ActivationDialog activationDialog; + activationDialog.exec(); +} + void MainWindow::on_m_pButtonApply_clicked() { restartSynergy(); diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index d6d140553..b08709a7c 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -113,7 +113,6 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase void serverDetected(const QString name); void setEdition(int type); void updateLocalFingerprint(); - public slots: void appendLogRaw(const QString& text); void appendLogInfo(const QString& text); @@ -130,6 +129,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase void on_m_pActionAbout_triggered(); void on_m_pActionSettings_triggered(); void on_m_pActionWizard_triggered(); + void on_m_pActivate_triggered(); void synergyFinished(int exitCode, QProcess::ExitStatus); void trayActivated(QSystemTrayIcon::ActivationReason reason); void stopSynergy(); From 81ae0c211a25510734017cc4d56e2a6852a33cef Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 29 Sep 2016 13:53:44 +0100 Subject: [PATCH 034/241] #5629 Move activation widgets to new activation dialog --- src/gui/res/ActivationDialog.ui | 218 +++++++++++++++++++++++++++++++++++++++ src/gui/src/ActivationDialog.cpp | 14 +++ src/gui/src/ActivationDialog.h | 22 ++++ 3 files changed, 254 insertions(+) create mode 100644 src/gui/res/ActivationDialog.ui create mode 100644 src/gui/src/ActivationDialog.cpp create mode 100644 src/gui/src/ActivationDialog.h diff --git a/src/gui/res/ActivationDialog.ui b/src/gui/res/ActivationDialog.ui new file mode 100644 index 000000000..20b483d1e --- /dev/null +++ b/src/gui/res/ActivationDialog.ui @@ -0,0 +1,218 @@ + + + ActivationDialog + + + + 0 + 0 + 564 + 385 + + + + Dialog + + + + + 30 + 340 + 521 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 30 + 244 + 521 + 70 + + + + + + + 30 + 60 + 520 + 24 + + + + + 75 + true + + + + &Account login + + + true + + + + + + 30 + 214 + 520 + 24 + + + + + 75 + true + + + + &Serial key + + + + + + 30 + 20 + 520 + 18 + + + + Enable your <a href="http://symless.com/pricing?source=gui">Synergy Pro</a> and Synergy Basic features. + + + true + + + + + + 30 + 100 + 285 + 92 + + + + + QFormLayout::AllNonFixedFieldsGrow + + + 20 + + + 10 + + + + + Email: + + + + + + + + 0 + 0 + + + + + 200 + 20 + + + + QLineEdit::Normal + + + + + + + + 0 + 0 + + + + + 200 + 20 + + + + QLineEdit::Password + + + + + + + <a href="https://symless.com/account/reset/?source=gui">Forgot password</a> + + + true + + + + + + + Password: + + + + + + + + + + buttonBox + accepted() + ActivationDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ActivationDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp new file mode 100644 index 000000000..a08ac1149 --- /dev/null +++ b/src/gui/src/ActivationDialog.cpp @@ -0,0 +1,14 @@ +#include "ActivationDialog.h" +#include "ui_ActivationDialog.h" + +ActivationDialog::ActivationDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ActivationDialog) +{ + ui->setupUi(this); +} + +ActivationDialog::~ActivationDialog() +{ + delete ui; +} diff --git a/src/gui/src/ActivationDialog.h b/src/gui/src/ActivationDialog.h new file mode 100644 index 000000000..52dbd4e06 --- /dev/null +++ b/src/gui/src/ActivationDialog.h @@ -0,0 +1,22 @@ +#ifndef ACTIVATIONDIALOG_H +#define ACTIVATIONDIALOG_H + +#include + +namespace Ui { +class ActivationDialog; +} + +class ActivationDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ActivationDialog(QWidget *parent = 0); + ~ActivationDialog(); + +private: + Ui::ActivationDialog *ui; +}; + +#endif // ACTIVATIONDIALOG_H From d4646b1cc6f857f6baf1a34d913da9c925bad1ef Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 29 Sep 2016 16:14:09 +0100 Subject: [PATCH 035/241] #5629 Add activation cancellation dialog --- src/gui/gui.pro | 9 ++-- src/gui/res/CancelActivationDialog.ui | 89 ++++++++++++++++++++++++++++++++++ src/gui/src/CancelActivationDialog.cpp | 14 ++++++ src/gui/src/CancelActivationDialog.h | 22 +++++++++ 4 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 src/gui/res/CancelActivationDialog.ui create mode 100644 src/gui/src/CancelActivationDialog.cpp create mode 100644 src/gui/src/CancelActivationDialog.h diff --git a/src/gui/gui.pro b/src/gui/gui.pro index df0887e61..ee4ba52bd 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -17,7 +17,8 @@ FORMS += res/MainWindowBase.ui \ res/SettingsDialogBase.ui \ res/SetupWizardBase.ui \ res/AddClientDialogBase.ui \ - res/ActivationDialog.ui + res/ActivationDialog.ui \ + res/CancelActivationDialog.ui SOURCES += src/main.cpp \ src/MainWindow.cpp \ src/AboutDialog.cpp \ @@ -60,7 +61,8 @@ SOURCES += src/main.cpp \ src/WebClient.cpp \ src/SubscriptionManager.cpp \ src/ActivationNotifier.cpp \ - src/ActivationDialog.cpp + src/ActivationDialog.cpp \ + src/CancelActivationDialog.cpp HEADERS += src/MainWindow.h \ src/AboutDialog.h \ src/ServerConfig.h \ @@ -106,7 +108,8 @@ HEADERS += src/MainWindow.h \ src/SubscriptionManager.h \ src/ActivationNotifier.h \ src/ElevateMode.h \ - src/ActivationDialog.h + src/ActivationDialog.h \ + src/CancelActivationDialog.h RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc macx { diff --git a/src/gui/res/CancelActivationDialog.ui b/src/gui/res/CancelActivationDialog.ui new file mode 100644 index 000000000..2c6ef41b8 --- /dev/null +++ b/src/gui/res/CancelActivationDialog.ui @@ -0,0 +1,89 @@ + + + CancelActivationDialog + + + + 0 + 0 + 400 + 156 + + + + Cancel Activation + + + + + + Are you sure? + +If you don't activate Synergy you'll be missing out on some great features + + + true + + + true + + + + + + + <html><head/><body><p><a href="https://symless.com/pricing?source=gui"><span style=" text-decoration: underline; color:#0000ff;">Buy now</span></a></p></body></html> + + + true + + + + + + + Qt::Horizontal + + + QDialogButtonBox::No|QDialogButtonBox::Yes + + + + + + + + + buttonBox + accepted() + CancelActivationDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + CancelActivationDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/gui/src/CancelActivationDialog.cpp b/src/gui/src/CancelActivationDialog.cpp new file mode 100644 index 000000000..074b76bbd --- /dev/null +++ b/src/gui/src/CancelActivationDialog.cpp @@ -0,0 +1,14 @@ +#include "CancelActivationDialog.h" +#include "ui_CancelActivationDialog.h" + +CancelActivationDialog::CancelActivationDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::CancelActivationDialog) +{ + ui->setupUi(this); +} + +CancelActivationDialog::~CancelActivationDialog() +{ + delete ui; +} diff --git a/src/gui/src/CancelActivationDialog.h b/src/gui/src/CancelActivationDialog.h new file mode 100644 index 000000000..b90af0839 --- /dev/null +++ b/src/gui/src/CancelActivationDialog.h @@ -0,0 +1,22 @@ +#ifndef CANCELACTIVATIONDIALOG_H +#define CANCELACTIVATIONDIALOG_H + +#include + +namespace Ui { +class CancelActivationDialog; +} + +class CancelActivationDialog : public QDialog +{ + Q_OBJECT + +public: + explicit CancelActivationDialog(QWidget *parent = 0); + ~CancelActivationDialog(); + +private: + Ui::CancelActivationDialog *ui; +}; + +#endif // CANCELACTIVATIONDIALOG_H From 0d5d7e11c06a5b599e904a03ec4777bf50f268ef Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 29 Sep 2016 16:16:49 +0100 Subject: [PATCH 036/241] #5629 Simplify Activation dialog --- src/gui/res/ActivationDialog.ui | 297 +++++++++++++++++----------------------- 1 file changed, 129 insertions(+), 168 deletions(-) diff --git a/src/gui/res/ActivationDialog.ui b/src/gui/res/ActivationDialog.ui index 20b483d1e..63e9e2612 100644 --- a/src/gui/res/ActivationDialog.ui +++ b/src/gui/res/ActivationDialog.ui @@ -6,179 +6,140 @@ 0 0 - 564 - 385 + 440 + 314 Dialog - - - - 30 - 340 - 521 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 30 - 244 - 521 - 70 - - - - - - - 30 - 60 - 520 - 24 - - - - - 75 - true - - - - &Account login - - - true - - - - - - 30 - 214 - 520 - 24 - - - - - 75 - true - - - - &Serial key - - - - - - 30 - 20 - 520 - 18 - - - - Enable your <a href="http://symless.com/pricing?source=gui">Synergy Pro</a> and Synergy Basic features. - - - true - - - - - - 30 - 100 - 285 - 92 - - - - - QFormLayout::AllNonFixedFieldsGrow - - - 20 - - - 10 - - - - - Email: - - - - - - - - 0 - 0 - - - - - 200 - 20 - - - - QLineEdit::Normal - - - - - - - - 0 - 0 - - - - - 200 - 20 - - - - QLineEdit::Password - - - - - - - <a href="https://symless.com/account/reset/?source=gui">Forgot password</a> - - - true - - - - - - - Password: - - - - - + + + + + + 75 + true + + + + &Account login + + + true + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + 20 + + + 10 + + + + + Email: + + + + + + + + 0 + 0 + + + + + 200 + 20 + + + + QLineEdit::Normal + + + + + + + Password: + + + + + + + + 0 + 0 + + + + + 200 + 20 + + + + QLineEdit::Password + + + + + + + + + + 75 + true + + + + &Serial key + + + + + + + Found on your <a href="https://symless.com/account/?source=gui">account</a> page. + + + true + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans'; font-size:10pt; 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;"><br /></p></body></html> + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + From 60a4e627795bda1ffb85baa915e695ef1886b5f1 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 29 Sep 2016 16:17:38 +0100 Subject: [PATCH 037/241] #5629 Enable activation cancellation dialog --- src/gui/src/ActivationDialog.cpp | 9 +++++++++ src/gui/src/ActivationDialog.h | 3 +++ src/gui/src/MainWindow.cpp | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index a08ac1149..da7e934ac 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -1,5 +1,6 @@ #include "ActivationDialog.h" #include "ui_ActivationDialog.h" +#include "CancelActivationDialog.h" ActivationDialog::ActivationDialog(QWidget *parent) : QDialog(parent), @@ -12,3 +13,11 @@ ActivationDialog::~ActivationDialog() { delete ui; } + +void ActivationDialog::reject() +{ + CancelActivationDialog cancelActivationDialog(this); + if (QDialog::Accepted == cancelActivationDialog.exec()) { + QDialog::reject(); + } +} diff --git a/src/gui/src/ActivationDialog.h b/src/gui/src/ActivationDialog.h index 52dbd4e06..396c41046 100644 --- a/src/gui/src/ActivationDialog.h +++ b/src/gui/src/ActivationDialog.h @@ -14,6 +14,9 @@ class ActivationDialog : public QDialog public: explicit ActivationDialog(QWidget *parent = 0); ~ActivationDialog(); + +public slots: + void reject(); private: Ui::ActivationDialog *ui; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 966736e6c..64f9f4254 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -1154,7 +1154,7 @@ void MainWindow::on_m_pActionWizard_triggered() void MainWindow::on_m_pActivate_triggered() { - ActivationDialog activationDialog; + ActivationDialog activationDialog (this); activationDialog.exec(); } From d48f6801f0a8ef79845a5b7025f2257f7e7e1900 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 3 Oct 2016 13:44:21 +0100 Subject: [PATCH 038/241] Disable DPI scaling on core binaries --- res/dpiaware.manifest | 8 ++++++++ src/cmd/synergyc/CMakeLists.txt | 9 +++++++++ src/cmd/synergyd/CMakeLists.txt | 9 +++++++++ src/cmd/synergys/CMakeLists.txt | 9 +++++++++ 4 files changed, 35 insertions(+) create mode 100644 res/dpiaware.manifest diff --git a/res/dpiaware.manifest b/res/dpiaware.manifest new file mode 100644 index 000000000..5de107ba5 --- /dev/null +++ b/res/dpiaware.manifest @@ -0,0 +1,8 @@ + + + + + true + + + \ No newline at end of file diff --git a/src/cmd/synergyc/CMakeLists.txt b/src/cmd/synergyc/CMakeLists.txt index 11d428d44..922ee324b 100644 --- a/src/cmd/synergyc/CMakeLists.txt +++ b/src/cmd/synergyc/CMakeLists.txt @@ -60,6 +60,15 @@ add_executable(synergyc ${sources}) target_link_libraries(synergyc arch base client common io mt net ipc platform server synergy ${libs}) +if (WIN32) + ADD_CUSTOM_COMMAND( + TARGET synergyc + POST_BUILD + COMMAND "mt.exe" -manifest \"${CMAKE_SOURCE_DIR}\\res\\dpiaware.manifest\" -inputresource:\"$\"\;\#1 -outputresource:\"$\"\;\#1 + COMMENT "Adding display aware manifest..." + ) +endif() + if (CONF_CPACK) install(TARGETS synergyc diff --git a/src/cmd/synergyd/CMakeLists.txt b/src/cmd/synergyd/CMakeLists.txt index 1caa35b52..49a6a7f87 100644 --- a/src/cmd/synergyd/CMakeLists.txt +++ b/src/cmd/synergyd/CMakeLists.txt @@ -37,6 +37,15 @@ endif() target_link_libraries(synergyd arch base common io ipc mt net platform synergy ${libs}) +if (WIN32) + ADD_CUSTOM_COMMAND( + TARGET synergyd + POST_BUILD + COMMAND "mt.exe" -manifest \"${CMAKE_SOURCE_DIR}\\res\\dpiaware.manifest\" -inputresource:\"$\"\;\#1 -outputresource:\"$\"\;\#1 + COMMENT "Adding display aware manifest..." + ) +endif() + if (CONF_CPACK) install(TARGETS synergyd diff --git a/src/cmd/synergys/CMakeLists.txt b/src/cmd/synergys/CMakeLists.txt index c749e09a8..3e2faad1a 100644 --- a/src/cmd/synergys/CMakeLists.txt +++ b/src/cmd/synergys/CMakeLists.txt @@ -60,6 +60,15 @@ add_executable(synergys ${sources}) target_link_libraries(synergys arch base client common io mt net ipc platform server synergy ${libs}) +if (WIN32) + ADD_CUSTOM_COMMAND( + TARGET synergys + POST_BUILD + COMMAND "mt.exe" -manifest \"${CMAKE_SOURCE_DIR}\\res\\dpiaware.manifest\" -inputresource:\"$\"\;\#1 -outputresource:\"$\"\;\#1 + COMMENT "Adding display aware manifest..." + ) +endif() + if (CONF_CPACK) install(TARGETS synergys From 8788faffdd031a6967bcebe4a6783e609d10534c Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 3 Oct 2016 14:41:53 +0100 Subject: [PATCH 039/241] Enable Windows 7 compatibility mode for core binaries --- res/dpiaware.manifest | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/res/dpiaware.manifest b/res/dpiaware.manifest index 5de107ba5..743e3369b 100644 --- a/res/dpiaware.manifest +++ b/res/dpiaware.manifest @@ -1,8 +1,15 @@ - - - - - true - - - \ No newline at end of file + + + + + + + + + + + true + + + + From 0f95c6e941c9a8b545f3903e4e1842c229d60fe7 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 30 Sep 2016 15:03:49 +0100 Subject: [PATCH 040/241] #5629 Complete activation support for activation dialog --- src/gui/res/ActivationDialog.ui | 24 +++--- src/gui/res/CancelActivationDialog.ui | 2 +- src/gui/src/ActivationDialog.cpp | 147 +++++++++++++++++++++++++++++++++- src/gui/src/ActivationDialog.h | 13 ++- src/gui/src/AppConfig.cpp | 91 ++++++++++++++++++++- src/gui/src/AppConfig.h | 77 +++++++++--------- src/gui/src/EditionType.h | 6 +- src/gui/src/MainWindow.cpp | 20 +---- src/gui/src/MainWindow.h | 4 +- src/gui/src/QUtility.cpp | 17 ++++ src/gui/src/QUtility.h | 1 + src/gui/src/SetupWizard.cpp | 9 ++- src/gui/src/SubscriptionManager.cpp | 4 +- src/gui/src/WebClient.cpp | 92 +++++++++------------ src/gui/src/WebClient.h | 16 ++-- 15 files changed, 374 insertions(+), 149 deletions(-) diff --git a/src/gui/res/ActivationDialog.ui b/src/gui/res/ActivationDialog.ui index 63e9e2612..fb700f919 100644 --- a/src/gui/res/ActivationDialog.ui +++ b/src/gui/res/ActivationDialog.ui @@ -11,7 +11,7 @@ - Dialog + Activate Synergy @@ -51,17 +51,11 @@ - + 0 0 - - - 200 - 20 - - QLineEdit::Normal @@ -77,17 +71,11 @@ - + 0 0 - - - 200 - 20 - - QLineEdit::Password @@ -120,6 +108,9 @@ + + false + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> @@ -127,6 +118,9 @@ p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:10pt; 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;"><br /></p></body></html> + + false + diff --git a/src/gui/res/CancelActivationDialog.ui b/src/gui/res/CancelActivationDialog.ui index 2c6ef41b8..b98733f6e 100644 --- a/src/gui/res/CancelActivationDialog.ui +++ b/src/gui/res/CancelActivationDialog.ui @@ -7,7 +7,7 @@ 0 0 400 - 156 + 165 diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index da7e934ac..df0d1618e 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -1,12 +1,44 @@ #include "ActivationDialog.h" #include "ui_ActivationDialog.h" #include "CancelActivationDialog.h" +#include "AppConfig.h" +#include "WebClient.h" +#include "EditionType.h" +#include "ActivationNotifier.h" +#include "MainWindow.h" +#include "QUtility.h" +#include "SubscriptionManager.h" -ActivationDialog::ActivationDialog(QWidget *parent) : +#include +#include +#include + +ActivationDialog::ActivationDialog(QWidget* parent, AppConfig& appConfig) : QDialog(parent), - ui(new Ui::ActivationDialog) + ui(new Ui::ActivationDialog), + m_appConfig (&appConfig) { ui->setupUi(this); + + ui->m_pLineEditEmail->setText(appConfig.activateEmail()); + ui->m_pTextEditSerialKey->setText(appConfig.serialKey()); + + if (!appConfig.serialKey().isEmpty()) { + ui->m_pRadioButtonActivate->setAutoExclusive(false); + ui->m_pRadioButtonSubscription->setAutoExclusive(false); + ui->m_pRadioButtonActivate->setChecked(false); + ui->m_pRadioButtonSubscription->setChecked(true); + ui->m_pRadioButtonActivate->setAutoExclusive(true); + ui->m_pRadioButtonSubscription->setAutoExclusive(true); + ui->m_pTextEditSerialKey->setFocus(); + ui->m_pTextEditSerialKey->moveCursor(QTextCursor::End); + } else { + if (ui->m_pLineEditEmail->text().isEmpty()) { + ui->m_pLineEditEmail->setFocus(); + } else { + ui->m_pLineEditPassword->setFocus(); + } + } } ActivationDialog::~ActivationDialog() @@ -14,10 +46,121 @@ ActivationDialog::~ActivationDialog() delete ui; } +void ActivationDialog::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 ActivationDialog::reject() { CancelActivationDialog cancelActivationDialog(this); if (QDialog::Accepted == cancelActivationDialog.exec()) { + notifyActivation("skip:unknown"); QDialog::reject(); } } + +void ActivationDialog::on_m_pRadioButtonSubscription_toggled(bool checked) +{ + if (checked) { + ui->m_pLineEditEmail->setEnabled(false); + ui->m_pLineEditPassword->setEnabled(false); + ui->m_pTextEditSerialKey->setEnabled(true); + ui->m_pTextEditSerialKey->setFocus(); + } +} + +void ActivationDialog::on_m_pRadioButtonActivate_toggled(bool checked) +{ + if (checked) { + ui->m_pLineEditEmail->setEnabled(true); + ui->m_pLineEditPassword->setEnabled(true); + ui->m_pTextEditSerialKey->setEnabled(false); + if (ui->m_pLineEditEmail->text().isEmpty()) { + ui->m_pLineEditEmail->setFocus(); + } else { + ui->m_pLineEditPassword->setFocus(); + } + } +} + +void ActivationDialog::accept() +{ + QMessageBox message; + QString error; + int edition = Unregistered; + + try { + if (ui->m_pRadioButtonActivate->isChecked()) { + WebClient webClient; + QString email = ui->m_pLineEditEmail->text(); + QString password = ui->m_pLineEditPassword->text(); + + if (!webClient.setEmail (email, error)) { + message.critical (this, "Invalid Email Address", tr("%1").arg(error)); + return; + } + else if (!webClient.setPassword (password, error)) { + message.critical (this, "Invalid Password", tr("%1").arg(error)); + return; + } + else if (!webClient.getEdition (edition, error)) { + message.critical (this, "Activation Error", + tr("An error occurred while trying to activate Synergy. " + "The Symless server returned the following error:\n\n%1").arg(error)); + return; + } + + m_appConfig->setActivateEmail (email); + m_appConfig->clearSerialKey(); + ui->m_pTextEditSerialKey->clear(); + notifyActivation ("login:" + m_appConfig->activateEmail()); + } + else { + QString serialKey = ui->m_pTextEditSerialKey->toPlainText(); + + if (!m_appConfig->setSerialKey (serialKey, error)) { + message.critical (this, "Invalid Serial Key", tr("%1").arg(error)); + return; + } + + SubscriptionManager subscriptionManager (this, *m_appConfig, edition); + if (!subscriptionManager.activateSerial (serialKey)) { + return; + } + m_appConfig->setActivateEmail(""); + notifyActivation ("serial:" + m_appConfig->serialKey()); + } + } + catch (std::exception& e) { + message.critical (this, "Unknown Error", + tr("An error occurred while trying to activate Synergy. " + "Please contact the helpdesk, and provide the " + "following details.\n\n%1").arg(e.what())); + return; + } + + m_appConfig->setEdition(edition); + m_appConfig->saveSettings(); + + message.information (this, "Activated!", + tr("Thanks for activating %1!").arg + (getEditionName (edition))); + MainWindow& mainWindow = dynamic_cast(*this->parent()); + mainWindow.setEdition(edition); + mainWindow.updateLocalFingerprint(); + mainWindow.settings().sync(); + + QDialog::accept(); +} diff --git a/src/gui/src/ActivationDialog.h b/src/gui/src/ActivationDialog.h index 396c41046..6fb926cce 100644 --- a/src/gui/src/ActivationDialog.h +++ b/src/gui/src/ActivationDialog.h @@ -7,19 +7,30 @@ namespace Ui { class ActivationDialog; } +class AppConfig; + class ActivationDialog : public QDialog { Q_OBJECT public: - explicit ActivationDialog(QWidget *parent = 0); + explicit ActivationDialog(QWidget *parent, AppConfig& appConfig); ~ActivationDialog(); public slots: void reject(); + void accept(); + +protected: + void notifyActivation (QString identity); private: Ui::ActivationDialog *ui; + AppConfig* m_appConfig; + +private slots: + void on_m_pRadioButtonSubscription_toggled(bool checked); + void on_m_pRadioButtonActivate_toggled(bool checked); }; #endif // ACTIVATIONDIALOG_H diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index a48bbc04c..6347c6cf6 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -73,6 +73,18 @@ AppConfig::~AppConfig() saveSettings(); } +const QString &AppConfig::screenName() const { return m_ScreenName; } + +int AppConfig::port() const { return m_Port; } + +const QString &AppConfig::interface() 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::synergyLogDir() const { #if defined(Q_OS_WIN) @@ -116,6 +128,16 @@ QString AppConfig::logLevelText() const return logLevelNames[logLevel()]; } +ProcessMode AppConfig::processMode() const { return m_ProcessMode; } + +bool AppConfig::wizardShouldRun() const { return m_WizardLastRun < kWizardVersion; } + +const QString &AppConfig::language() const { return m_Language; } + +bool AppConfig::startedBefore() const { return m_StartedBefore; } + +bool AppConfig::autoConfig() const { return m_AutoConfig; } + void AppConfig::loadSettings() { m_ScreenName = settings().value("screenName", QHostInfo::localHostName()).toString(); @@ -135,7 +157,7 @@ void AppConfig::loadSettings() } m_ElevateMode = static_cast(elevateMode.toInt()); m_AutoConfigPrompted = settings().value("autoConfigPrompted", false).toBool(); - m_Edition = settings().value("edition", Unknown).toInt(); + m_Edition = settings().value("edition", Unregistered).toInt(); m_ActivateEmail = settings().value("activateEmail", "").toString(); m_CryptoEnabled = settings().value("cryptoEnabled", false).toBool(); m_AutoHide = settings().value("autoHide", false).toBool(); @@ -168,17 +190,84 @@ void AppConfig::saveSettings() settings().setValue("lastExpiringWarningTime", m_LastExpiringWarningTime); } +QSettings &AppConfig::settings() { return *m_pSettings; } + +void AppConfig::setScreenName(const QString &s) { m_ScreenName = s; } + +void AppConfig::setPort(int i) { m_Port = i; } + +void AppConfig::setInterface(const QString &s) { m_Interface = s; } + +void AppConfig::setLogLevel(int i) { m_LogLevel = i; } + +void AppConfig::setLogToFile(bool b) { m_LogToFile = b; } + +void AppConfig::setLogFilename(const QString &s) { m_LogFilename = s; } + +void AppConfig::setWizardHasRun() { m_WizardLastRun = kWizardVersion; } + +void AppConfig::setLanguage(const QString language) { m_Language = language; } + +void AppConfig::setStartedBefore(bool b) { m_StartedBefore = b; } + +void AppConfig::setElevateMode(ElevateMode em) { m_ElevateMode = em; } + void AppConfig::setAutoConfig(bool autoConfig) { m_AutoConfig = autoConfig; } +bool AppConfig::autoConfigPrompted() { return m_AutoConfigPrompted; } + void AppConfig::setAutoConfigPrompted(bool prompted) { m_AutoConfigPrompted = prompted; } +void AppConfig::setEdition(int e) { m_Edition = e; } + +int AppConfig::edition() { return m_Edition; } + +bool AppConfig::setActivateEmail(QString e) { + m_ActivateEmail = e; + return true; +} + +QString AppConfig::activateEmail() { return m_ActivateEmail; } + +bool AppConfig::setSerialKey(QString serial, QString& errorOut) { + if (serial.isEmpty()) { + errorOut = "Your serial key cannot be blank."; + return false; + } + m_Serialkey = serial; + return true; +} + +void AppConfig::clearSerialKey() +{ + m_Serialkey.clear(); +} + +QString AppConfig::serialKey() { return m_Serialkey; } + +int AppConfig::lastExpiringWarningTime() const { return m_LastExpiringWarningTime; } + +void AppConfig::setLastExpiringWarningTime(int t) { m_LastExpiringWarningTime = t; } + +QString AppConfig::synergysName() const { return m_SynergysName; } + +QString AppConfig::synergycName() const { return m_SynergycName; } + ElevateMode AppConfig::elevateMode() { return m_ElevateMode; } + +void AppConfig::setCryptoEnabled(bool e) { m_CryptoEnabled = e; } + +bool AppConfig::getCryptoEnabled() { return m_CryptoEnabled; } + +void AppConfig::setAutoHide(bool b) { m_AutoHide = b; } + +bool AppConfig::getAutoHide() { return m_AutoHide; } diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 43e35c7fc..df391bfba 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -58,33 +58,34 @@ class AppConfig ~AppConfig(); public: - const QString& screenName() const { return m_ScreenName; } - int port() const { return m_Port; } - const QString& interface() const { return m_Interface; } - int logLevel() const { return m_LogLevel; } - bool logToFile() const { return m_LogToFile; } - const QString& logFilename() const { return m_LogFilename; } + const QString& screenName() const; + int port() const; + const QString& interface() const; + int logLevel() const; + bool logToFile() const; + const QString& logFilename() const; const QString logFilenameCmd() const; QString logLevelText() const; - ProcessMode processMode() const { return m_ProcessMode; } - bool wizardShouldRun() const { return m_WizardLastRun < kWizardVersion; } - const QString& language() const { return m_Language; } - bool startedBefore() const { return m_StartedBefore; } - bool autoConfig() const { return m_AutoConfig; } + ProcessMode processMode() const; + bool wizardShouldRun() const; + const QString& language() const; + bool startedBefore() const; + bool autoConfig() const; void setAutoConfig(bool autoConfig); - bool autoConfigPrompted() { return m_AutoConfigPrompted; } + bool autoConfigPrompted(); void setAutoConfigPrompted(bool prompted); - void setEdition(int e) { m_Edition = e; } - int edition() { return m_Edition; } - void setActivateEmail(QString e) { m_ActivateEmail = e; } - QString activateEmail() { return m_ActivateEmail; } - 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; } + void setEdition(int e); + int edition(); + bool setActivateEmail(QString e); + QString activateEmail(); + bool setSerialKey(QString serial, QString& error); + void clearSerialKey(); + QString serialKey(); + int lastExpiringWarningTime() const; + void setLastExpiringWarningTime(int t); + + QString synergysName() const; + QString synergycName() const; QString synergyProgramDir() const; QString synergyLogDir() const; @@ -92,25 +93,25 @@ class AppConfig void persistLogDir(); ElevateMode elevateMode(); - void setCryptoEnabled(bool e) { m_CryptoEnabled = e; } - bool getCryptoEnabled() { return m_CryptoEnabled; } - void setAutoHide(bool b) { m_AutoHide = b; } - bool getAutoHide() { return m_AutoHide; } + void setCryptoEnabled(bool e); + bool getCryptoEnabled(); + void setAutoHide(bool b); + bool getAutoHide(); void saveSettings(); protected: - QSettings& settings() { return *m_pSettings; } - void setScreenName(const QString& s) { m_ScreenName = s; } - void setPort(int i) { m_Port = i; } - void setInterface(const QString& s) { m_Interface = s; } - void setLogLevel(int i) { m_LogLevel = i; } - void setLogToFile(bool b) { m_LogToFile = b; } - void setLogFilename(const QString& s) { m_LogFilename = s; } - void setWizardHasRun() { m_WizardLastRun = kWizardVersion; } - void setLanguage(const QString language) { m_Language = language; } - void setStartedBefore(bool b) { m_StartedBefore = b; } - void setElevateMode(ElevateMode em) { m_ElevateMode = em; } + QSettings& settings(); + void setScreenName(const QString& s); + void setPort(int i); + void setInterface(const QString& s); + void setLogLevel(int i); + void setLogToFile(bool b); + void setLogFilename(const QString& s); + void setWizardHasRun(); + void setLanguage(const QString language); + void setStartedBefore(bool b); + void setElevateMode(ElevateMode em); void loadSettings(); diff --git a/src/gui/src/EditionType.h b/src/gui/src/EditionType.h index d294cda53..5869a32b4 100644 --- a/src/gui/src/EditionType.h +++ b/src/gui/src/EditionType.h @@ -18,11 +18,13 @@ #ifndef EDITIONTYPE_H #define EDITIONTYPE_H -enum qEditionType { +/* Do not reorder these! */ + +enum EditionType { Basic, Pro, Trial, - Unknown + Unregistered }; #endif // EDITIONTYPE_H diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 64f9f4254..15c20d122 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -1015,23 +1015,9 @@ void MainWindow::serverDetected(const QString name) } } -void MainWindow::setEdition(int type) +void MainWindow::setEdition(int edition) { - QString title; - if (type == Basic) { - title = "Synergy Basic"; - } - else if (type == Pro) { - title = "Synergy Pro"; - } - else if (type == Trial) { - title = "Synergy Trial"; - } - else { - title = "Synergy (UNREGISTERED)"; - } - - setWindowTitle(title); + setWindowTitle(getEditionName(edition)); } void MainWindow::updateLocalFingerprint() @@ -1154,7 +1140,7 @@ void MainWindow::on_m_pActionWizard_triggered() void MainWindow::on_m_pActivate_triggered() { - ActivationDialog activationDialog (this); + ActivationDialog activationDialog (this, this->appConfig()); activationDialog.exec(); } diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index b08709a7c..8788e4a26 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -33,6 +33,7 @@ #include "VersionChecker.h" #include "IpcClient.h" #include "Ipc.h" +#include "ActivationDialog.h" #include @@ -63,6 +64,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase friend class QSynergyApplication; friend class SetupWizard; + friend class ActivationDialog; public: enum qSynergyState @@ -111,7 +113,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase void autoAddScreen(const QString name); void updateZeroconfService(); void serverDetected(const QString name); - void setEdition(int type); + void setEdition(int edition); void updateLocalFingerprint(); public slots: void appendLogRaw(const QString& text); diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index a21343ec6..ca2c52c0c 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -19,6 +19,7 @@ #include "ProcessorArch.h" #include "CommandProcess.h" +#include "EditionType.h" #if defined(Q_OS_LINUX) #include @@ -42,6 +43,22 @@ void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData) } } +QString +getEditionName (int edition) { + if (edition == Basic) { + return "Synergy Basic"; + } + else if (edition == Pro) { + return "Synergy Pro"; + } + else if (edition == Trial) { + return "Synergy Trial"; + } + else { + return "Synergy (UNREGISTERED)"; + } +} + QString hash(const QString& string) { QByteArray data = string.toUtf8(); diff --git a/src/gui/src/QUtility.h b/src/gui/src/QUtility.h index 0738d96cc..ca00d06fa 100644 --- a/src/gui/src/QUtility.h +++ b/src/gui/src/QUtility.h @@ -29,3 +29,4 @@ QString hash(const QString& string); QString getFirstMacAddress(); qProcessorArch getProcessorArch(); QString getOSInformation(); +QString getEditionName (int edition); diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index ed574e04e..6a28c2f87 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -29,7 +29,7 @@ SetupWizard::SetupWizard(MainWindow& mainWindow, bool startMain) : m_MainWindow(mainWindow), m_StartMain(startMain), - m_Edition(Unknown), + m_Edition(Unregistered), m_LoginAttemps(0) { setupUi(this); @@ -76,7 +76,7 @@ bool SetupWizard::validateCurrentPage() message.setWindowTitle(tr("Setup Synergy")); message.setIcon(QMessageBox::Information); - if (currentPage() == m_pActivatePage) + /*if (currentPage() == m_pActivatePage) { if (m_pRadioButtonActivate->isChecked()) { if (m_pLineEditEmail->text().isEmpty() || @@ -136,7 +136,8 @@ bool SetupWizard::validateCurrentPage() return true; } } - else if (currentPage() == m_pNodePage) + else */ + if (currentPage() == m_pNodePage) { bool result = m_pClientRadioButton->isChecked() || m_pServerRadioButton->isChecked(); @@ -201,7 +202,7 @@ void SetupWizard::accept() if (m_pRadioButtonSubscription->isChecked()) { - appConfig.setSerialKey(m_pTextEditSerialKey->toPlainText()); + //appConfig.setSerialKey(m_pTextEditSerialKey->toPlainText()); notifyActivation("serial:" + m_pTextEditSerialKey->toPlainText()); } diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index 52096a11a..107bdff13 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -39,7 +39,7 @@ SubscriptionManager::SubscriptionManager(QWidget* parent, AppConfig& appConfig, bool SubscriptionManager::activateSerial(const QString& serial) { - m_Edition = Unknown; + m_Edition = Unregistered; persistDirectory(); CoreInterface coreInterface; QString output; @@ -62,7 +62,7 @@ bool SubscriptionManager::activateSerial(const QString& serial) bool SubscriptionManager::checkSubscription() { - m_Edition = Unknown; + m_Edition = Unregistered; persistDirectory(); CoreInterface coreInterface; QString output; diff --git a/src/gui/src/WebClient.cpp b/src/gui/src/WebClient.cpp index 4b4b999b4..a0a6118a0 100644 --- a/src/gui/src/WebClient.cpp +++ b/src/gui/src/WebClient.cpp @@ -25,76 +25,60 @@ #include #include -int WebClient::getEdition( - const QString& email, - const QString& password, - QMessageBox& message, - QWidget* w) -{ - QString responseJson; - int edition = Unknown; - try { - responseJson = request(email, password); - } - catch (std::exception& e) - { - message.critical( - w, "Error", - tr("An error occurred while trying to sign in. " - "Please contact the helpdesk, and provide the " - "following details.\n\n%1").arg(e.what())); - return edition; - } +bool +WebClient::getEdition (int& edition, QString& errorOut) { + QString responseJson = request(); + + /* TODO: This is horrible and should be ripped out as soon as we move + * to Qt 5. See issue #5630 + */ QRegExp resultRegex(".*\"result\".*:.*(true|false).*"); - if (resultRegex.exactMatch(responseJson)) { + if (resultRegex.exactMatch (responseJson)) { QString boolString = resultRegex.cap(1); if (boolString == "true") { QRegExp editionRegex(".*\"edition\".*:.*\"([^\"]+)\".*"); if (editionRegex.exactMatch(responseJson)) { QString e = editionRegex.cap(1); edition = e.toInt(); + return true; + } else { + throw std::runtime_error ("Unrecognised server response."); } - - return edition; + } else { + errorOut = tr("Login failed. Invalid email or password."); + return false; } - else if (boolString == "false") { - message.critical( - w, "Error", - tr("Login failed, invalid email or password.")); - - return edition; - } - } - else { + } else { QRegExp errorRegex(".*\"error\".*:.*\"([^\"]+)\".*"); - if (errorRegex.exactMatch(responseJson)) { - - // replace "\n" with real new lines. - QString error = errorRegex.cap(1).replace("\\n", "\n"); - message.critical( - w, "Error", - tr("Login failed, an error occurred.\n\n%1").arg(error)); - - return edition; + if (errorRegex.exactMatch (responseJson)) { + errorOut = errorRegex.cap(1).replace("\\n", "\n"); + return false; + } else { + throw std::runtime_error ("Unrecognised server response."); } } +} - message.critical( - w, "Error", - tr("Login failed, an error occurred.\n\nServer response:\n\n%1") - .arg(responseJson)); +bool +WebClient::setEmail (QString email, QString& errorOut) { + if (email.isEmpty()) { + errorOut = tr("Your email address cannot be left blank."); + return false; + } + m_Email = email; + return true; +} - return edition; +bool +WebClient::setPassword (QString password, QString&) { + m_Password = password; + return true; } -QString WebClient::request( - const QString& email, - const QString& password) -{ +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); + QString credentials (m_Email + ":" + hash(m_Password) + "\n"); + return m_CoreInterface.run (args, credentials); } diff --git a/src/gui/src/WebClient.h b/src/gui/src/WebClient.h index 100b63d12..7ff6e2beb 100644 --- a/src/gui/src/WebClient.h +++ b/src/gui/src/WebClient.h @@ -32,21 +32,15 @@ class WebClient : public QObject Q_OBJECT public: - int getEdition(const QString& email, - const QString& password, - QMessageBox& message, - QWidget* w); - void setEmail(QString& e) { m_Email = e; } - void setPassword(QString& p) { m_Password = p; } - + bool getEdition (int& edition, QString& errorOut); + bool setEmail (QString email, QString& errorOut); + bool setPassword (QString password, QString& errorOut); signals: void error(QString e); private: - QString request(const QString& email, - const QString& password); - -private: + QString request(); + QString m_Email; QString m_Password; CoreInterface m_CoreInterface; From 6033f0c946d22f02d277fbc4e0622d810c4809bb Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 30 Sep 2016 15:21:12 +0100 Subject: [PATCH 041/241] #5629 Minor grammar tweaks --- src/gui/src/ActivationDialog.cpp | 3 +-- src/gui/src/SubscriptionManager.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index df0d1618e..8f1fb8c78 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -155,8 +155,7 @@ void ActivationDialog::accept() m_appConfig->saveSettings(); message.information (this, "Activated!", - tr("Thanks for activating %1!").arg - (getEditionName (edition))); + tr("Thanks for activating %1!").arg (getEditionName (edition))); MainWindow& mainWindow = dynamic_cast(*this->parent()); mainWindow.setEdition(edition); mainWindow.updateLocalFingerprint(); diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index 107bdff13..77e0a91c1 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -98,7 +98,7 @@ void SubscriptionManager::checkError(QString& error) } else { QMessageBox::warning(m_pParent, tr("Subscription error"), - tr("An error occurred while trying to activate using a serial key. " + tr("An error occurred while trying to activate Synergy using your serial key. " "Please contact the helpdesk, and provide the " "following details.\n\n%1").arg(error)); } From 088ac82e186b82d3e36e2cf9c8ca0682221ae024 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 30 Sep 2016 15:35:04 +0100 Subject: [PATCH 042/241] #5629 Remove activation from the wizard --- src/gui/res/SetupWizardBase.ui | 208 ----------------------------------------- src/gui/src/SetupWizard.cpp | 138 --------------------------- src/gui/src/SetupWizard.h | 4 - 3 files changed, 350 deletions(-) diff --git a/src/gui/res/SetupWizardBase.ui b/src/gui/res/SetupWizardBase.ui index 6788a3d53..8c0898861 100644 --- a/src/gui/res/SetupWizardBase.ui +++ b/src/gui/res/SetupWizardBase.ui @@ -120,214 +120,6 @@ - - - Activate - - - - - - Enable your <a href="http://symless.com/pricing?source=gui">Synergy Pro</a> and Synergy Basic features. - - - true - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - - 75 - true - - - - &Account login - - - true - - - - - - - QFormLayout::AllNonFixedFieldsGrow - - - 20 - - - 10 - - - - - Email: - - - - - - - - 0 - 0 - - - - - 200 - 20 - - - - QLineEdit::Normal - - - - - - - Password: - - - - - - - - 0 - 0 - - - - - 200 - 20 - - - - QLineEdit::Password - - - - - - - <a href="https://symless.com/account/reset/?source=gui">Forgot password</a> - - - true - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - - 75 - true - - - - &Serial key - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - S&kip activation - - - - - - - color: rgb(100, 100, 100); - - - You will see UNREGISTERED in the window title (not recommended). - - - true - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 500 - - - - - - diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index 6a28c2f87..a9dab840b 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -57,13 +57,6 @@ SetupWizard::SetupWizard(MainWindow& mainWindow, bool startMain) : m_Locale.fillLanguageComboBox(m_pComboLanguage); setIndexFromItemData(m_pComboLanguage, m_MainWindow.appConfig().language()); - AppConfig& appConfig = m_MainWindow.appConfig(); - - m_pLineEditEmail->setText(appConfig.activateEmail()); - m_pTextEditSerialKey->setText(appConfig.serialKey()); - - m_pTextEditSerialKey->setEnabled(false); - } SetupWizard::~SetupWizard() @@ -76,67 +69,6 @@ bool SetupWizard::validateCurrentPage() message.setWindowTitle(tr("Setup Synergy")); message.setIcon(QMessageBox::Information); - /*if (currentPage() == m_pActivatePage) - { - if (m_pRadioButtonActivate->isChecked()) { - if (m_pLineEditEmail->text().isEmpty() || - m_pLineEditPassword->text().isEmpty()) { - message.setText(tr("Please enter your email address and password.")); - message.exec(); - return false; - } - else { - WebClient webClient; - m_Edition = webClient.getEdition( - m_pLineEditEmail->text(), - m_pLineEditPassword->text(), - message, - this); - - if (m_Edition == Unknown) { - m_LoginAttemps++; - if (m_LoginAttemps == kMaximiumLoginAttemps) { - m_LoginAttemps = 0; - - QMessageBox::StandardButton reply = - QMessageBox::information( - this, tr("Setup Synergy"), - tr("Would you like to use your serial key instead?"), - QMessageBox::Yes | QMessageBox::No); - - if (reply == QMessageBox::Yes) { - m_pRadioButtonSubscription->setChecked(true); - } - } - - return false; - } - else { - return true; - } - } - } - else if (m_pRadioButtonSubscription->isChecked()) { - if (m_pTextEditSerialKey->toPlainText().isEmpty()) { - message.setText(tr("Please enter your subscription serial key.")); - message.exec(); - return false; - } - else { - // create subscription file in profile directory - SubscriptionManager subscriptionManager(this, m_MainWindow.appConfig(), m_Edition); - if (!subscriptionManager.activateSerial(m_pTextEditSerialKey->toPlainText())) { - return false; - } - - return true; - } - } - else { - return true; - } - } - else */ if (currentPage() == m_pNodePage) { bool result = m_pClientRadioButton->isChecked() || @@ -194,31 +126,6 @@ void SetupWizard::accept() settings.setValue("groupServerChecked", false); } - if (m_pRadioButtonActivate->isChecked()) { - appConfig.setActivateEmail(m_pLineEditEmail->text()); - - notifyActivation("login:" + m_pLineEditEmail->text()); - } - - if (m_pRadioButtonSubscription->isChecked()) - { - //appConfig.setSerialKey(m_pTextEditSerialKey->toPlainText()); - - notifyActivation("serial:" + m_pTextEditSerialKey->toPlainText()); - } - - 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(); if (m_StartMain) @@ -238,56 +145,11 @@ void SetupWizard::reject() m_MainWindow.open(); } - // treat cancel as skip - 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(); QSynergyApplication::getInstance()->switchTranslator(ietfCode); } - -void SetupWizard::on_m_pRadioButtonSkip_toggled(bool checked) -{ - if (checked) { - m_pLineEditEmail->setEnabled(false); - m_pLineEditPassword->setEnabled(false); - m_pTextEditSerialKey->setEnabled(false); - } -} - -void SetupWizard::on_m_pRadioButtonActivate_toggled(bool checked) -{ - if (checked) { - m_pLineEditEmail->setEnabled(true); - m_pLineEditPassword->setEnabled(true); - m_pTextEditSerialKey->setEnabled(false); - } -} - -void SetupWizard::on_m_pRadioButtonSubscription_toggled(bool checked) -{ - if (checked) { - m_pLineEditEmail->setEnabled(false); - m_pLineEditPassword->setEnabled(false); - m_pTextEditSerialKey->setEnabled(true); - } -} diff --git a/src/gui/src/SetupWizard.h b/src/gui/src/SetupWizard.h index fd6dcd14d..a1fde1494 100644 --- a/src/gui/src/SetupWizard.h +++ b/src/gui/src/SetupWizard.h @@ -42,7 +42,6 @@ class SetupWizard : public QWizard, public Ui::SetupWizardBase void changeEvent(QEvent* event); void accept(); void reject(); - void notifyActivation(QString identity); private: MainWindow& m_MainWindow; @@ -52,8 +51,5 @@ class SetupWizard : public QWizard, public Ui::SetupWizardBase int m_LoginAttemps; private slots: - void on_m_pRadioButtonSubscription_toggled(bool checked); - void on_m_pRadioButtonActivate_toggled(bool checked); - void on_m_pRadioButtonSkip_toggled(bool checked); void on_m_pComboLanguage_currentIndexChanged(int index); }; From 02d75cd370ad4c404c8d801f747c990ef4c0a440 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 30 Sep 2016 15:52:24 +0100 Subject: [PATCH 043/241] #5603 Ignore exceptions in getOSInformation() --- src/gui/src/QUtility.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index ca2c52c0c..f463f1d2f 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -114,15 +114,18 @@ 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; + result = "Linux"; + try { + QStringList arguments; + arguments.append("/etc/os-release"); + CommandProcess cp("/bin/cat", arguments); + QString output = cp.run(); + + QRegExp resultRegex(".*PRETTY_NAME=\"([^\"]+)\".*"); + if (resultRegex.exactMatch(output)) { + result = resultRegex.cap(1); + } + } catch (...) { } #endif From bcf2d5de0d97e4f053176ab63ab7dd84d2d413d9 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 30 Sep 2016 16:04:49 +0100 Subject: [PATCH 044/241] #5629 Trigger activation dialog if previously unseen --- src/gui/src/AppConfig.cpp | 13 +++++++++++++ src/gui/src/AppConfig.h | 4 ++++ src/gui/src/MainWindow.cpp | 6 ++++++ 3 files changed, 23 insertions(+) diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 6347c6cf6..60481930a 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -163,6 +163,7 @@ void AppConfig::loadSettings() m_AutoHide = settings().value("autoHide", false).toBool(); m_Serialkey = settings().value("serialKey", "").toString(); m_LastExpiringWarningTime = settings().value("lastExpiringWarningTime", 0).toInt(); + m_ActivationHasRun = settings().value("activationHasRun", false).toBool(); } void AppConfig::saveSettings() @@ -188,6 +189,18 @@ void AppConfig::saveSettings() settings().setValue("autoHide", m_AutoHide); settings().setValue("serialKey", m_Serialkey); settings().setValue("lastExpiringWarningTime", m_LastExpiringWarningTime); + settings().setValue("activationHasRun", m_ActivationHasRun); +} + +bool AppConfig::activationHasRun() const +{ + return m_ActivationHasRun; +} + +AppConfig& AppConfig::activationHasRun(bool value) +{ + m_ActivationHasRun = value; + return *this; } QSettings &AppConfig::settings() { return *m_pSettings; } diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index df391bfba..a3dd0a11e 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -100,6 +100,9 @@ class AppConfig void saveSettings(); + bool activationHasRun() const; + AppConfig& activationHasRun(bool value); + protected: QSettings& settings(); void setScreenName(const QString& s); @@ -136,6 +139,7 @@ class AppConfig bool m_AutoHide; QString m_Serialkey; int m_LastExpiringWarningTime; + bool m_ActivationHasRun; static const char m_SynergysName[]; static const char m_SynergycName[]; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 15c20d122..f27777a16 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -139,6 +139,12 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : m_pLabelPadlock->hide(); updateLocalFingerprint(); + + if (!appConfig.activationHasRun() && (appConfig.edition() == Unregistered)) { + ActivationDialog activationDialog (this, appConfig); + activationDialog.exec(); + appConfig.activationHasRun(true); + } } MainWindow::~MainWindow() From 2809530793c80d182d094b6aadaf0ab6e175fe2c Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 30 Sep 2016 16:23:04 +0100 Subject: [PATCH 045/241] #5629 Remove 'Run Wizard' from file menu --- src/gui/res/MainWindowBase.ui | 8 -------- src/gui/src/MainWindow.cpp | 9 +-------- src/gui/src/MainWindow.h | 1 - 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/gui/res/MainWindowBase.ui b/src/gui/res/MainWindowBase.ui index fa7955ccf..5a852c8ca 100644 --- a/src/gui/res/MainWindowBase.ui +++ b/src/gui/res/MainWindowBase.ui @@ -481,14 +481,6 @@ - - - Run Wizard - - - - - Activate diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index f27777a16..8dca067d9 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -26,7 +26,6 @@ #include "AboutDialog.h" #include "ServerConfigDialog.h" #include "SettingsDialog.h" -#include "SetupWizard.h" #include "ActivationDialog.h" #include "ZeroconfService.h" #include "DataDownloader.h" @@ -269,8 +268,8 @@ void MainWindow::createMenuBar() m_pMenuFile->addAction(m_pActionStartSynergy); m_pMenuFile->addAction(m_pActionStopSynergy); m_pMenuFile->addSeparator(); - m_pMenuFile->addAction(m_pActionWizard); m_pMenuFile->addAction(m_pActivate); + m_pMenuFile->addSeparator(); m_pMenuFile->addAction(m_pActionSave); m_pMenuFile->addSeparator(); m_pMenuFile->addAction(m_pActionQuit); @@ -1138,12 +1137,6 @@ void MainWindow::on_m_pButtonConfigureServer_clicked() showConfigureServer(); } -void MainWindow::on_m_pActionWizard_triggered() -{ - SetupWizard wizard(*this, false); - wizard.exec(); -} - void MainWindow::on_m_pActivate_triggered() { ActivationDialog activationDialog (this, this->appConfig()); diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 8788e4a26..e815d53f7 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -130,7 +130,6 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase bool on_m_pActionSave_triggered(); void on_m_pActionAbout_triggered(); void on_m_pActionSettings_triggered(); - void on_m_pActionWizard_triggered(); void on_m_pActivate_triggered(); void synergyFinished(int exitCode, QProcess::ExitStatus); void trayActivated(QSystemTrayIcon::ActivationReason reason); From d6b7d8e357bf3e091e89f1a9c830c6dd906a6293 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 30 Sep 2016 17:01:43 +0100 Subject: [PATCH 046/241] #5627 Enable encryption for Pro users --- src/gui/res/SettingsDialogBase.ui | 7 +++++-- src/gui/src/AppConfig.cpp | 8 +++++--- src/gui/src/AppConfig.h | 4 ++-- src/gui/src/SettingsDialog.cpp | 2 ++ 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/gui/res/SettingsDialogBase.ui b/src/gui/res/SettingsDialogBase.ui index 1cd828016..edfabaf91 100644 --- a/src/gui/res/SettingsDialogBase.ui +++ b/src/gui/res/SettingsDialogBase.ui @@ -7,7 +7,7 @@ 0 0 368 - 380 + 470 @@ -176,8 +176,11 @@ + + false + - Use &SSL encryption (unique certificate) + Use &SSL encryption diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 60481930a..1fbd966b8 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -159,7 +159,7 @@ void AppConfig::loadSettings() m_AutoConfigPrompted = settings().value("autoConfigPrompted", false).toBool(); m_Edition = settings().value("edition", Unregistered).toInt(); m_ActivateEmail = settings().value("activateEmail", "").toString(); - m_CryptoEnabled = settings().value("cryptoEnabled", false).toBool(); + m_CryptoEnabled = settings().value("cryptoEnabled", true).toBool(); m_AutoHide = settings().value("autoHide", false).toBool(); m_Serialkey = settings().value("serialKey", "").toString(); m_LastExpiringWarningTime = settings().value("lastExpiringWarningTime", 0).toInt(); @@ -239,7 +239,7 @@ void AppConfig::setAutoConfigPrompted(bool prompted) void AppConfig::setEdition(int e) { m_Edition = e; } -int AppConfig::edition() { return m_Edition; } +int AppConfig::edition() const { return m_Edition; } bool AppConfig::setActivateEmail(QString e) { m_ActivateEmail = e; @@ -279,7 +279,9 @@ ElevateMode AppConfig::elevateMode() void AppConfig::setCryptoEnabled(bool e) { m_CryptoEnabled = e; } -bool AppConfig::getCryptoEnabled() { return m_CryptoEnabled; } +bool AppConfig::getCryptoEnabled() const { + return (edition() == Pro) && m_CryptoEnabled; +} void AppConfig::setAutoHide(bool b) { m_AutoHide = b; } diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index a3dd0a11e..5110bf915 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -75,7 +75,7 @@ class AppConfig bool autoConfigPrompted(); void setAutoConfigPrompted(bool prompted); void setEdition(int e); - int edition(); + int edition() const; bool setActivateEmail(QString e); QString activateEmail(); bool setSerialKey(QString serial, QString& error); @@ -94,7 +94,7 @@ class AppConfig ElevateMode elevateMode(); void setCryptoEnabled(bool e); - bool getCryptoEnabled(); + bool getCryptoEnabled() const; void setAutoHide(bool b); bool getAutoHide(); diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index 8f7544901..e6bec0fae 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -23,6 +23,7 @@ #include "QSynergyApplication.h" #include "QUtility.h" #include "AppConfig.h" +#include "EditionType.h" #include #include @@ -61,6 +62,7 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) : #endif m_pCheckBoxEnableCrypto->setChecked(m_AppConfig.getCryptoEnabled()); + m_pCheckBoxEnableCrypto->setEnabled(m_AppConfig.edition() == Pro); } void SettingsDialog::accept() From 4924f2faff109d371e5e58de7965d620837c6dc5 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 30 Sep 2016 17:07:26 +0100 Subject: [PATCH 047/241] #5627 Save settings after creating MainWindow --- src/gui/src/MainWindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 8dca067d9..021b4f4b5 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -144,6 +144,8 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : activationDialog.exec(); appConfig.activationHasRun(true); } + + appConfig.saveSettings(); } MainWindow::~MainWindow() From c799041ce8a7e2a0fecfc970f394f87a91dc57d1 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 30 Sep 2016 17:31:55 +0100 Subject: [PATCH 048/241] #5627 Only generate an SSL certificate when it doesn't exist --- src/gui/src/MainWindow.cpp | 11 ++++++- src/gui/src/MainWindow.h | 2 ++ src/gui/src/SslCertificate.cpp | 75 ++++++++++++++++++++++-------------------- 3 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 021b4f4b5..6f326be11 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -34,6 +34,7 @@ #include "EditionType.h" #include "QUtility.h" #include "ProcessorArch.h" +#include "SslCertificate.h" #include #include @@ -97,7 +98,8 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : m_SuppressAutoConfigWarning(false), m_BonjourInstall(NULL), m_SuppressEmptyServerWarning(false), - m_ExpectedRunningState(kStopped) + m_ExpectedRunningState(kStopped), + m_pSslCertificate(NULL) { setupUi(this); @@ -145,6 +147,11 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : appConfig.activationHasRun(true); } + if (appConfig.getCryptoEnabled()) { + m_pSslCertificate = new SslCertificate(this); + m_pSslCertificate->generateCertificate(); + } + appConfig.saveSettings(); } @@ -166,6 +173,8 @@ MainWindow::~MainWindow() if (m_BonjourInstall != NULL) { delete m_BonjourInstall; } + + delete m_pSslCertificate; } void MainWindow::open() diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index e815d53f7..650b05d1e 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -57,6 +57,7 @@ class SetupWizard; class ZeroconfService; class DataDownloader; class CommandProcess; +class SslCertificate; class MainWindow : public QMainWindow, public Ui::MainWindowBase { @@ -207,6 +208,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase bool m_SuppressEmptyServerWarning; qRuningState m_ExpectedRunningState; QMutex m_StopDesktopMutex; + SslCertificate* m_pSslCertificate; private slots: void on_m_pCheckBoxAutoConfig_toggled(bool checked); diff --git a/src/gui/src/SslCertificate.cpp b/src/gui/src/SslCertificate.cpp index 7a8403c05..a669c5f6f 100644 --- a/src/gui/src/SslCertificate.cpp +++ b/src/gui/src/SslCertificate.cpp @@ -90,55 +90,58 @@ bool SslCertificate::runTool(const QStringList& args) void SslCertificate::generateCertificate() { - QStringList arguments; - - // self signed certificate - arguments.append("req"); - arguments.append("-x509"); - arguments.append("-nodes"); - - // valide duration - arguments.append("-days"); - arguments.append(kCertificateLifetime); - - // subject information - arguments.append("-subj"); - - QString subInfo(kCertificateSubjectInfo); - arguments.append(subInfo); - - // private key - arguments.append("-newkey"); - arguments.append("rsa:1024"); - QString sslDirPath = QString("%1%2%3") .arg(m_ProfileDir) .arg(QDir::separator()) .arg(kSslDir); - QDir sslDir(sslDirPath); - if (!sslDir.exists()) { - sslDir.mkpath("."); - } - QString filename = QString("%1%2%3") .arg(sslDirPath) .arg(QDir::separator()) .arg(kCertificateFilename); - // key output filename - arguments.append("-keyout"); - arguments.append(filename); + QFile file(filename); + if (!file.exists()) { + QStringList arguments; - // certificate output filename - arguments.append("-out"); - arguments.append(filename); + // self signed certificate + arguments.append("req"); + arguments.append("-x509"); + arguments.append("-nodes"); - if (!runTool(arguments)) { - return; - } + // valide duration + arguments.append("-days"); + arguments.append(kCertificateLifetime); + + // subject information + arguments.append("-subj"); + + QString subInfo(kCertificateSubjectInfo); + arguments.append(subInfo); + + // private key + arguments.append("-newkey"); + arguments.append("rsa:1024"); - emit info(tr("SSL certificate generated.")); + QDir sslDir(sslDirPath); + if (!sslDir.exists()) { + sslDir.mkpath("."); + } + + // key output filename + arguments.append("-keyout"); + arguments.append(filename); + + // certificate output filename + arguments.append("-out"); + arguments.append(filename); + + if (!runTool(arguments)) { + return; + } + + emit info(tr("SSL certificate generated.")); + } generateFingerprint(filename); From d6bcdcbea74da9db6f718e0a6e72e101f364cf57 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 3 Oct 2016 11:54:45 +0100 Subject: [PATCH 049/241] #5629 Change activation failure to a QDialog --- src/gui/gui.pro | 9 ++-- src/gui/res/FailedLoginDialog.ui | 108 ++++++++++++++++++++++++++++++++++++++ src/gui/src/ActivationDialog.cpp | 6 +-- src/gui/src/FailedLoginDialog.cpp | 15 ++++++ src/gui/src/FailedLoginDialog.h | 23 ++++++++ src/gui/src/WebClient.cpp | 2 +- 6 files changed, 156 insertions(+), 7 deletions(-) create mode 100644 src/gui/res/FailedLoginDialog.ui create mode 100644 src/gui/src/FailedLoginDialog.cpp create mode 100644 src/gui/src/FailedLoginDialog.h diff --git a/src/gui/gui.pro b/src/gui/gui.pro index ee4ba52bd..c0bf4fad0 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -18,7 +18,8 @@ FORMS += res/MainWindowBase.ui \ res/SetupWizardBase.ui \ res/AddClientDialogBase.ui \ res/ActivationDialog.ui \ - res/CancelActivationDialog.ui + res/CancelActivationDialog.ui \ + res/FailedLoginDialog.ui SOURCES += src/main.cpp \ src/MainWindow.cpp \ src/AboutDialog.cpp \ @@ -62,7 +63,8 @@ SOURCES += src/main.cpp \ src/SubscriptionManager.cpp \ src/ActivationNotifier.cpp \ src/ActivationDialog.cpp \ - src/CancelActivationDialog.cpp + src/CancelActivationDialog.cpp \ + src/FailedLoginDialog.cpp HEADERS += src/MainWindow.h \ src/AboutDialog.h \ src/ServerConfig.h \ @@ -109,7 +111,8 @@ HEADERS += src/MainWindow.h \ src/ActivationNotifier.h \ src/ElevateMode.h \ src/ActivationDialog.h \ - src/CancelActivationDialog.h + src/CancelActivationDialog.h \ + src/FailedLoginDialog.h RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc macx { diff --git a/src/gui/res/FailedLoginDialog.ui b/src/gui/res/FailedLoginDialog.ui new file mode 100644 index 000000000..d3c6b5077 --- /dev/null +++ b/src/gui/res/FailedLoginDialog.ui @@ -0,0 +1,108 @@ + + + FailedLoginDialog + + + + 0 + 0 + 400 + 165 + + + + Activation Error + + + + + 50 + 120 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + 10 + 90 + 382 + 30 + + + + <html><head/><body><p><a href="https://symless.com/account/reset/?source=gui"><span style=" text-decoration: underline; color:#0000ff;">Forgotten your password?</span></a></p></body></html> + + + true + + + + + + 10 + 10 + 382 + 72 + + + + An error occurred while trying to activate Synergy. The Symless server returned the following error: + +%1 + + + true + + + true + + + label_2 + messageLabel + buttonBox + + + + + buttonBox + accepted() + FailedLoginDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FailedLoginDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 8f1fb8c78..3950d1989 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -8,6 +8,7 @@ #include "MainWindow.h" #include "QUtility.h" #include "SubscriptionManager.h" +#include "FailedLoginDialog.h" #include #include @@ -116,9 +117,8 @@ void ActivationDialog::accept() return; } else if (!webClient.getEdition (edition, error)) { - message.critical (this, "Activation Error", - tr("An error occurred while trying to activate Synergy. " - "The Symless server returned the following error:\n\n%1").arg(error)); + FailedLoginDialog failedLoginDialog (this, error); + failedLoginDialog.exec(); return; } diff --git a/src/gui/src/FailedLoginDialog.cpp b/src/gui/src/FailedLoginDialog.cpp new file mode 100644 index 000000000..07ec6bdcd --- /dev/null +++ b/src/gui/src/FailedLoginDialog.cpp @@ -0,0 +1,15 @@ +#include "FailedLoginDialog.h" +#include "ui_FailedLoginDialog.h" + +FailedLoginDialog::FailedLoginDialog(QWidget *parent, QString message): + QDialog(parent), + ui(new Ui::FailedLoginDialog) +{ + ui->setupUi(this); + ui->messageLabel->setText(ui->messageLabel->text().arg(message)); +} + +FailedLoginDialog::~FailedLoginDialog() +{ + delete ui; +} diff --git a/src/gui/src/FailedLoginDialog.h b/src/gui/src/FailedLoginDialog.h new file mode 100644 index 000000000..2eb676346 --- /dev/null +++ b/src/gui/src/FailedLoginDialog.h @@ -0,0 +1,23 @@ +#ifndef FAILEDLOGINDIALOG_H +#define FAILEDLOGINDIALOG_H + +#include +#include + +namespace Ui { +class FailedLoginDialog; +} + +class FailedLoginDialog : public QDialog +{ + Q_OBJECT + +public: + explicit FailedLoginDialog(QWidget *parent = 0, QString message = ""); + ~FailedLoginDialog(); + +private: + Ui::FailedLoginDialog *ui; +}; + +#endif // FAILEDLOGINDIALOG_H diff --git a/src/gui/src/WebClient.cpp b/src/gui/src/WebClient.cpp index a0a6118a0..1d21dc820 100644 --- a/src/gui/src/WebClient.cpp +++ b/src/gui/src/WebClient.cpp @@ -46,7 +46,7 @@ WebClient::getEdition (int& edition, QString& errorOut) { throw std::runtime_error ("Unrecognised server response."); } } else { - errorOut = tr("Login failed. Invalid email or password."); + errorOut = tr("Login failed. Invalid email address or password."); return false; } } else { From 32f143f52b412203406f2624e0db208b65398278 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 3 Oct 2016 15:16:03 +0100 Subject: [PATCH 050/241] #5627 Update Main Window fingerprint after SSL cert gen --- src/gui/src/MainWindow.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 6f326be11..971bd8804 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -139,8 +139,6 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : m_pLabelPadlock->hide(); - updateLocalFingerprint(); - if (!appConfig.activationHasRun() && (appConfig.edition() == Unregistered)) { ActivationDialog activationDialog (this, appConfig); activationDialog.exec(); @@ -152,6 +150,7 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : m_pSslCertificate->generateCertificate(); } + updateLocalFingerprint(); appConfig.saveSettings(); } From d1a180f652158dc1e91cd0d9f41f7bdd5cad9d1b Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 3 Oct 2016 15:26:27 +0100 Subject: [PATCH 051/241] #5627 Automatically generate SSL cert when enabling SSL --- src/gui/src/SettingsDialog.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index e6bec0fae..813eac367 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -24,6 +24,8 @@ #include "QUtility.h" #include "AppConfig.h" #include "EditionType.h" +#include "SslCertificate.h" +#include "MainWindow.h" #include #include @@ -143,4 +145,11 @@ void SettingsDialog::on_m_pComboLanguage_currentIndexChanged(int index) void SettingsDialog::on_m_pCheckBoxEnableCrypto_toggled(bool checked) { m_AppConfig.setCryptoEnabled(checked); + m_AppConfig.saveSettings(); + if (checked) { + SslCertificate sslCertificate; + sslCertificate.generateCertificate(); + MainWindow& mainWindow = dynamic_cast (*this->parent()); + mainWindow.updateLocalFingerprint(); + } } From 912ed9be9c44ab2957f06d6224ff5c858087d7b7 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 3 Oct 2016 15:27:38 +0100 Subject: [PATCH 052/241] #5629 Ensure settings are saved when AppConfig updates them --- src/gui/src/AppConfig.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 1fbd966b8..e6b9b0edd 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -190,6 +190,7 @@ void AppConfig::saveSettings() settings().setValue("serialKey", m_Serialkey); settings().setValue("lastExpiringWarningTime", m_LastExpiringWarningTime); settings().setValue("activationHasRun", m_ActivationHasRun); + settings().sync(); } bool AppConfig::activationHasRun() const From 176d7e47257d8553f78c0509c8f416631aaef47c Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 3 Oct 2016 15:36:21 +0100 Subject: [PATCH 053/241] #5617 Don't copy plugins directory on Windows --- src/lib/net/CMakeLists.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/lib/net/CMakeLists.txt b/src/lib/net/CMakeLists.txt index 60a144995..3e64fe5d1 100644 --- a/src/lib/net/CMakeLists.txt +++ b/src/lib/net/CMakeLists.txt @@ -35,13 +35,6 @@ endif() add_library(net STATIC ${sources}) if (WIN32) - add_custom_command( - TARGET net - POST_BUILD - COMMAND xcopy /Y /Q - ..\\..\\..\\..\\lib\\${CMAKE_CFG_INTDIR}\\ns.* - ..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR}\\plugins\\ - ) add_custom_command( TARGET net POST_BUILD From 15a6a27dc6a31d98671fe1b352a092dd397146bf Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 3 Oct 2016 17:23:10 +0100 Subject: [PATCH 054/241] #5629 Show activation dialog only after main window --- src/gui/src/ActivationDialog.cpp | 3 ++- src/gui/src/MainWindow.cpp | 15 ++++++++------- src/gui/src/MainWindow.h | 2 ++ src/gui/src/SettingsDialog.cpp | 1 + 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 3950d1989..a0221fece 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -152,6 +152,7 @@ void ActivationDialog::accept() } m_appConfig->setEdition(edition); + m_appConfig->activationHasRun(true); m_appConfig->saveSettings(); message.information (this, "Activated!", @@ -159,7 +160,7 @@ void ActivationDialog::accept() MainWindow& mainWindow = dynamic_cast(*this->parent()); mainWindow.setEdition(edition); mainWindow.updateLocalFingerprint(); - mainWindow.settings().sync(); + mainWindow.saveSettings(); QDialog::accept(); } diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 971bd8804..0c64b6b48 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -139,19 +139,20 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : m_pLabelPadlock->hide(); - if (!appConfig.activationHasRun() && (appConfig.edition() == Unregistered)) { - ActivationDialog activationDialog (this, appConfig); - activationDialog.exec(); - appConfig.activationHasRun(true); - } - if (appConfig.getCryptoEnabled()) { m_pSslCertificate = new SslCertificate(this); m_pSslCertificate->generateCertificate(); } updateLocalFingerprint(); - appConfig.saveSettings(); +} + +void +MainWindow::showEvent(QShowEvent*) { + if (!m_AppConfig.activationHasRun() && (m_AppConfig.edition() == Unregistered)) { + ActivationDialog activationDialog (this, m_AppConfig); + activationDialog.exec(); + } } MainWindow::~MainWindow() diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 650b05d1e..d66201e36 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -66,6 +66,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase friend class QSynergyApplication; friend class SetupWizard; friend class ActivationDialog; + friend class SettingsDialog; public: enum qSynergyState @@ -181,6 +182,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase void restartSynergy(); void proofreadInfo(); + void showEvent(QShowEvent *event); private: QSettings& m_Settings; AppConfig& m_AppConfig; diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index 813eac367..14cd1fc78 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -151,5 +151,6 @@ void SettingsDialog::on_m_pCheckBoxEnableCrypto_toggled(bool checked) sslCertificate.generateCertificate(); MainWindow& mainWindow = dynamic_cast (*this->parent()); mainWindow.updateLocalFingerprint(); + mainWindow.saveSettings(); } } From 603b12dc59970eb56716e82d4b67063c80004e68 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 4 Oct 2016 10:45:27 +0100 Subject: [PATCH 055/241] #5629 Trigger main window show event before opening activation dialog --- src/gui/src/MainWindow.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 0c64b6b48..87a79c51d 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -148,7 +148,8 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : } void -MainWindow::showEvent(QShowEvent*) { +MainWindow::showEvent(QShowEvent* event) { + QMainWindow::showEvent (event); if (!m_AppConfig.activationHasRun() && (m_AppConfig.edition() == Unregistered)) { ActivationDialog activationDialog (this, m_AppConfig); activationDialog.exec(); From 16ef224ba8f185a0eb7d8ac28d6cdb2e6e72cae5 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 4 Oct 2016 11:15:53 +0100 Subject: [PATCH 056/241] #5629 Save activation dialog seen state on reject --- src/gui/src/ActivationDialog.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index a0221fece..e4beeb493 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -68,6 +68,8 @@ void ActivationDialog::reject() CancelActivationDialog cancelActivationDialog(this); if (QDialog::Accepted == cancelActivationDialog.exec()) { notifyActivation("skip:unknown"); + m_appConfig->activationHasRun(true); + m_appConfig->saveSettings(); QDialog::reject(); } } @@ -102,6 +104,9 @@ void ActivationDialog::accept() QString error; int edition = Unregistered; + m_appConfig->activationHasRun(true); + m_appConfig->saveSettings(); + try { if (ui->m_pRadioButtonActivate->isChecked()) { WebClient webClient; @@ -152,7 +157,6 @@ void ActivationDialog::accept() } m_appConfig->setEdition(edition); - m_appConfig->activationHasRun(true); m_appConfig->saveSettings(); message.information (this, "Activated!", From 15f2e27d1883e8cd8da9f9c848574e22c41e743b Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 4 Oct 2016 11:48:47 +0100 Subject: [PATCH 057/241] #5629 Show the main window behind the activation dialog --- src/gui/src/MainWindow.cpp | 23 +++++++++++++++-------- src/gui/src/MainWindow.h | 8 +++++++- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 87a79c51d..f4cab6ef1 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -145,15 +145,8 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : } updateLocalFingerprint(); -} -void -MainWindow::showEvent(QShowEvent* event) { - QMainWindow::showEvent (event); - if (!m_AppConfig.activationHasRun() && (m_AppConfig.edition() == Unregistered)) { - ActivationDialog activationDialog (this, m_AppConfig); - activationDialog.exec(); - } + connect (this, SIGNAL(windowShown()), this, SLOT(on_windowShown()), Qt::QueuedConnection); } MainWindow::~MainWindow() @@ -518,6 +511,12 @@ void MainWindow::proofreadInfo() setSynergyState((qSynergyState)oldState); } +void MainWindow::showEvent(QShowEvent* event) +{ + QMainWindow::showEvent(event); + emit windowShown(); +} + void MainWindow::clearLog() { m_pLogOutput->clear(); @@ -1367,6 +1366,14 @@ void MainWindow::bonjourInstallFinished() m_pCheckBoxAutoConfig->setChecked(true); } +void MainWindow::on_windowShown() +{ + if (!m_AppConfig.activationHasRun() && (m_AppConfig.edition() == Unregistered)) { + ActivationDialog activationDialog (this, m_AppConfig); + activationDialog.exec(); + } +} + QString MainWindow::getProfileRootForArg() { CoreInterface coreInterface; diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index d66201e36..b71d8a003 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -117,6 +117,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase void serverDetected(const QString name); void setEdition(int edition); void updateLocalFingerprint(); + public slots: void appendLogRaw(const QString& text); void appendLogInfo(const QString& text); @@ -182,7 +183,8 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase void restartSynergy(); void proofreadInfo(); - void showEvent(QShowEvent *event); + void showEvent (QShowEvent*); + private: QSettings& m_Settings; AppConfig& m_AppConfig; @@ -217,6 +219,10 @@ private slots: void on_m_pComboServerList_currentIndexChanged(QString ); void on_m_pButtonApply_clicked(); void installBonjour(); + void on_windowShown(); + +signals: + void windowShown(); }; #endif From 783056f7cc0d4ddce71f958de87c0c42d1384af7 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 4 Oct 2016 13:41:27 +0100 Subject: [PATCH 058/241] #5629 Ensure setEdition signals main UI --- src/gui/src/ActivationDialog.cpp | 5 ----- src/gui/src/AppConfig.cpp | 5 ++++- src/gui/src/AppConfig.h | 8 +++++++- src/gui/src/MainWindow.cpp | 17 ++++++++--------- src/gui/src/MainWindow.h | 2 +- src/gui/src/SettingsDialog.cpp | 1 - src/gui/src/SetupWizard.cpp | 5 +---- src/gui/src/SetupWizard.h | 2 -- 8 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index e4beeb493..402510190 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -161,10 +161,5 @@ void ActivationDialog::accept() message.information (this, "Activated!", tr("Thanks for activating %1!").arg (getEditionName (edition))); - MainWindow& mainWindow = dynamic_cast(*this->parent()); - mainWindow.setEdition(edition); - mainWindow.updateLocalFingerprint(); - mainWindow.saveSettings(); - QDialog::accept(); } diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index e6b9b0edd..a6d081f96 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -238,7 +238,10 @@ void AppConfig::setAutoConfigPrompted(bool prompted) m_AutoConfigPrompted = prompted; } -void AppConfig::setEdition(int e) { m_Edition = e; } +void AppConfig::setEdition(int e) { + m_Edition = e; + emit editionSet (e); +} int AppConfig::edition() const { return m_Edition; } diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 5110bf915..3d15ec6e4 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -20,6 +20,7 @@ #define APPCONFIG_H +#include #include #include "ElevateMode.h" @@ -47,8 +48,10 @@ enum ProcessMode { Desktop }; -class AppConfig +class AppConfig: public QObject { + Q_OBJECT + friend class SettingsDialog; friend class MainWindow; friend class SetupWizard; @@ -144,6 +147,9 @@ class AppConfig static const char m_SynergysName[]; static const char m_SynergycName[]; static const char m_SynergyLogDir[]; + + signals: + void editionSet(int); }; #endif diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index f4cab6ef1..64271c142 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -138,15 +138,8 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : setEdition(m_AppConfig.edition()); m_pLabelPadlock->hide(); - - if (appConfig.getCryptoEnabled()) { - m_pSslCertificate = new SslCertificate(this); - m_pSslCertificate->generateCertificate(); - } - - updateLocalFingerprint(); - connect (this, SIGNAL(windowShown()), this, SLOT(on_windowShown()), Qt::QueuedConnection); + connect (&m_AppConfig, SIGNAL(editionSet(int)), this, SLOT(setEdition(int)), Qt::QueuedConnection); } MainWindow::~MainWindow() @@ -1034,11 +1027,17 @@ void MainWindow::serverDetected(const QString name) void MainWindow::setEdition(int edition) { setWindowTitle(getEditionName(edition)); + if (m_AppConfig.getCryptoEnabled()) { + m_pSslCertificate = new SslCertificate(this); + m_pSslCertificate->generateCertificate(); + } + updateLocalFingerprint(); + saveSettings(); } void MainWindow::updateLocalFingerprint() { - if (Fingerprint::local().fileExists()) { + if (m_AppConfig.getCryptoEnabled() && Fingerprint::local().fileExists()) { m_pLabelFingerprint->setVisible(true); m_pLabelLocalFingerprint->setVisible(true); m_pLabelLocalFingerprint->setText(Fingerprint::local().readFirst()); diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index b71d8a003..2f0b2c5bc 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -115,10 +115,10 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase void autoAddScreen(const QString name); void updateZeroconfService(); void serverDetected(const QString name); - void setEdition(int edition); void updateLocalFingerprint(); public slots: + void setEdition(int edition); void appendLogRaw(const QString& text); void appendLogInfo(const QString& text); void appendLogDebug(const QString& text); diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index 14cd1fc78..813eac367 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -151,6 +151,5 @@ void SettingsDialog::on_m_pCheckBoxEnableCrypto_toggled(bool checked) sslCertificate.generateCertificate(); MainWindow& mainWindow = dynamic_cast (*this->parent()); mainWindow.updateLocalFingerprint(); - mainWindow.saveSettings(); } } diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index a9dab840b..b4e6ab3a4 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -28,9 +28,7 @@ SetupWizard::SetupWizard(MainWindow& mainWindow, bool startMain) : m_MainWindow(mainWindow), - m_StartMain(startMain), - m_Edition(Unregistered), - m_LoginAttemps(0) + m_StartMain(startMain) { setupUi(this); @@ -141,7 +139,6 @@ void SetupWizard::reject() if (m_StartMain) { - m_MainWindow.setEdition(m_Edition); m_MainWindow.open(); } diff --git a/src/gui/src/SetupWizard.h b/src/gui/src/SetupWizard.h index a1fde1494..009939b23 100644 --- a/src/gui/src/SetupWizard.h +++ b/src/gui/src/SetupWizard.h @@ -47,8 +47,6 @@ class SetupWizard : public QWizard, public Ui::SetupWizardBase MainWindow& m_MainWindow; bool m_StartMain; SynergyLocale m_Locale; - int m_Edition; - int m_LoginAttemps; private slots: void on_m_pComboLanguage_currentIndexChanged(int index); From 002bcebbd2010b6ec06275f67725091843022d99 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 4 Oct 2016 13:47:28 +0100 Subject: [PATCH 059/241] #5629 Fix main window AppConfig naming conventions --- src/gui/src/SettingsDialog.cpp | 10 +++++----- src/gui/src/SettingsDialog.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index 813eac367..9b1d1b0e4 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -38,7 +38,7 @@ static const char networkSecurity[] = "ns"; SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), Ui::SettingsDialogBase(), - m_AppConfig(config) + m_appConfig(config) { setupUi(this); @@ -63,8 +63,8 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) : m_pComboElevate->hide(); #endif - m_pCheckBoxEnableCrypto->setChecked(m_AppConfig.getCryptoEnabled()); - m_pCheckBoxEnableCrypto->setEnabled(m_AppConfig.edition() == Pro); + m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled()); + m_pCheckBoxEnableCrypto->setEnabled(m_appConfig.edition() == Pro); } void SettingsDialog::accept() @@ -144,8 +144,8 @@ void SettingsDialog::on_m_pComboLanguage_currentIndexChanged(int index) void SettingsDialog::on_m_pCheckBoxEnableCrypto_toggled(bool checked) { - m_AppConfig.setCryptoEnabled(checked); - m_AppConfig.saveSettings(); + m_appConfig.setCryptoEnabled(checked); + m_appConfig.saveSettings(); if (checked) { SslCertificate sslCertificate; sslCertificate.generateCertificate(); diff --git a/src/gui/src/SettingsDialog.h b/src/gui/src/SettingsDialog.h index 656323a6b..841ffff82 100644 --- a/src/gui/src/SettingsDialog.h +++ b/src/gui/src/SettingsDialog.h @@ -40,10 +40,10 @@ class SettingsDialog : public QDialog, public Ui::SettingsDialogBase void accept(); void reject(); void changeEvent(QEvent* event); - AppConfig& appConfig() { return m_AppConfig; } + AppConfig& appConfig() { return m_appConfig; } private: - AppConfig& m_AppConfig; + AppConfig& m_appConfig; SynergyLocale m_Locale; CoreInterface m_CoreInterface; From c288918d68d7fd67d99dfefe8b7cde87ccb52948 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 4 Oct 2016 15:12:27 +0100 Subject: [PATCH 060/241] #5629 Fix height of settings dialog --- src/gui/res/SettingsDialogBase.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/res/SettingsDialogBase.ui b/src/gui/res/SettingsDialogBase.ui index edfabaf91..dca1bdadd 100644 --- a/src/gui/res/SettingsDialogBase.ui +++ b/src/gui/res/SettingsDialogBase.ui @@ -7,7 +7,7 @@ 0 0 368 - 470 + 380 From b345eb4067ee9cba62598a5a73e159ce7f8f81b0 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 4 Oct 2016 15:30:53 +0100 Subject: [PATCH 061/241] v1.8.4-rc1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fe96198ba..a86fee803 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 4) -set(VERSION_STAGE beta) +set(VERSION_STAGE rc1) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From e6a3caaf75d88441072fde03b507929786c11a2d Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 4 Oct 2016 15:51:07 +0100 Subject: [PATCH 062/241] #5628 Fix net lib linkage on Ubuntu --- src/lib/net/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/net/CMakeLists.txt b/src/lib/net/CMakeLists.txt index 3e64fe5d1..10e2cae1e 100644 --- a/src/lib/net/CMakeLists.txt +++ b/src/lib/net/CMakeLists.txt @@ -52,5 +52,5 @@ if (WIN32) endif() if (UNIX) - target_link_libraries(net mt io) + target_link_libraries(net mt io ${OPENSSL_LIBS}) endif() From f3d1470e58b5a7f76e388b2bf170a9e5d2dd4729 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 19 Sep 2016 17:22:41 +0100 Subject: [PATCH 063/241] Brutally replace all reinterpret_casts with static_casts --- .../synergyc/MSWindowsClientTaskBarReceiver.cpp | 12 +++--- .../synergyp/MSWindowsPortableTaskBarReceiver.cpp | 16 +++---- .../synergys/MSWindowsServerTaskBarReceiver.cpp | 16 +++---- src/gui/src/SynergyLocale.cpp | 2 +- src/lib/arch/unix/ArchMultithreadPosix.cpp | 2 +- src/lib/arch/unix/ArchNetworkBSD.cpp | 12 +++--- src/lib/arch/win32/ArchMiscWindows.cpp | 14 +++--- src/lib/arch/win32/ArchMultithreadWindows.cpp | 4 +- src/lib/arch/win32/ArchNetworkWinsock.cpp | 10 ++--- src/lib/arch/win32/ArchTaskBarWindows.cpp | 22 +++++----- src/lib/arch/win32/ArchTimeWindows.cpp | 2 +- src/lib/base/EventQueue.cpp | 2 +- src/lib/base/Unicode.cpp | 50 +++++++++++----------- src/lib/client/Client.cpp | 10 ++--- src/lib/io/StreamBuffer.cpp | 4 +- src/lib/io/StreamFilter.cpp | 2 +- src/lib/mt/Thread.cpp | 2 +- src/lib/net/SecureSocket.cpp | 2 +- src/lib/net/SocketMultiplexer.cpp | 2 +- src/lib/net/TCPListenSocket.cpp | 2 +- src/lib/net/TCPSocket.cpp | 2 +- .../platform/MSWindowsClipboardBitmapConverter.cpp | 2 +- src/lib/platform/MSWindowsDesks.cpp | 10 ++--- src/lib/platform/MSWindowsScreen.cpp | 8 ++-- src/lib/platform/MSWindowsScreenSaver.cpp | 4 +- src/lib/platform/OSXClipboardBMPConverter.cpp | 4 +- src/lib/platform/OSXKeyState.cpp | 2 +- src/lib/platform/OSXScreen.cpp | 2 +- src/lib/platform/OSXUchrKeyResource.cpp | 26 +++++------ src/lib/platform/XWindowsClipboard.cpp | 20 ++++----- .../XWindowsClipboardAnyBitmapConverter.cpp | 4 +- src/lib/platform/XWindowsClipboardBMPConverter.cpp | 4 +- src/lib/platform/XWindowsKeyState.cpp | 2 +- src/lib/platform/XWindowsScreen.cpp | 8 ++-- src/lib/platform/XWindowsUtil.cpp | 14 +++--- src/lib/server/ClientListener.cpp | 4 +- src/lib/server/InputFilter.cpp | 8 ++-- src/lib/server/Server.cpp | 42 +++++++++--------- src/lib/synergy/ClientApp.cpp | 4 +- src/lib/synergy/ClipboardChunk.cpp | 10 ++--- src/lib/synergy/IClipboard.cpp | 2 +- src/lib/synergy/KeyState.cpp | 2 +- src/lib/synergy/ProtocolUtil.cpp | 28 ++++++------ src/lib/synergy/ServerApp.cpp | 2 +- src/lib/synergy/StreamChunker.cpp | 2 +- src/test/integtests/net/NetworkTests.cpp | 22 +++++----- 46 files changed, 213 insertions(+), 213 deletions(-) diff --git a/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp b/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp index 039246cc0..529a54dca 100644 --- a/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp +++ b/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp @@ -221,7 +221,7 @@ MSWindowsClientTaskBarReceiver::primaryAction() const IArchTaskBarReceiver::Icon MSWindowsClientTaskBarReceiver::getIcon() const { - return reinterpret_cast(m_icon[getStatus()]); + return static_cast(m_icon[getStatus()]); } void @@ -263,7 +263,7 @@ MSWindowsClientTaskBarReceiver::loadIcon(UINT id) IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR); - return reinterpret_cast(icon); + return static_cast(icon); } void @@ -287,8 +287,8 @@ MSWindowsClientTaskBarReceiver::createWindow() MAKEINTRESOURCE(IDD_TASKBAR_STATUS), NULL, (DLGPROC)&MSWindowsClientTaskBarReceiver::staticDlgProc, - reinterpret_cast( - reinterpret_cast(this))); + static_cast( + static_cast(this))); // window should appear on top of everything, including (especially) // the task bar. @@ -337,8 +337,8 @@ MSWindowsClientTaskBarReceiver::staticDlgProc(HWND hwnd, // and put it in the extra window data then forward the call. MSWindowsClientTaskBarReceiver* self = NULL; if (msg == WM_INITDIALOG) { - self = reinterpret_cast( - reinterpret_cast(lParam)); + self = static_cast( + static_cast(lParam)); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) lParam); } else { diff --git a/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp b/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp index 4f35fe4f2..b42f28b32 100644 --- a/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp +++ b/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp @@ -238,7 +238,7 @@ MSWindowsPortableTaskBarReceiver::primaryAction() const IArchTaskBarReceiver::Icon MSWindowsPortableTaskBarReceiver::getIcon() const { - return reinterpret_cast(m_icon[getStatus()]); + return static_cast(m_icon[getStatus()]); } void @@ -280,7 +280,7 @@ MSWindowsPortableTaskBarReceiver::loadIcon(UINT id) IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR); - return reinterpret_cast(icon); + return static_cast(icon); } void @@ -304,8 +304,8 @@ MSWindowsPortableTaskBarReceiver::createWindow() MAKEINTRESOURCE(IDD_TASKBAR_STATUS), NULL, (DLGPROC)&MSWindowsPortableTaskBarReceiver::staticDlgProc, - reinterpret_cast( - reinterpret_cast(this))); + static_cast( + static_cast(this))); // window should appear on top of everything, including (especially) // the task bar. @@ -354,16 +354,16 @@ MSWindowsPortableTaskBarReceiver::staticDlgProc(HWND hwnd, // and put it in the extra window data then forward the call. MSWindowsPortableTaskBarReceiver* self = NULL; if (msg == WM_INITDIALOG) { - self = reinterpret_cast( - reinterpret_cast(lParam)); + self = static_cast( + static_cast(lParam)); SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam); } else { // get the extra window data and forward the call LONG data = (LONG)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (data != 0) { - self = reinterpret_cast( - reinterpret_cast(data)); + self = static_cast( + static_cast(data)); } } diff --git a/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp b/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp index f65e2a352..83fc539c8 100644 --- a/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp +++ b/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp @@ -252,7 +252,7 @@ MSWindowsServerTaskBarReceiver::primaryAction() const IArchTaskBarReceiver::Icon MSWindowsServerTaskBarReceiver::getIcon() const { - return reinterpret_cast(m_icon[getStatus()]); + return static_cast(m_icon[getStatus()]); } void @@ -294,7 +294,7 @@ MSWindowsServerTaskBarReceiver::loadIcon(UINT id) IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR); - return reinterpret_cast(icon); + return static_cast(icon); } void @@ -318,8 +318,8 @@ MSWindowsServerTaskBarReceiver::createWindow() MAKEINTRESOURCE(IDD_TASKBAR_STATUS), NULL, (DLGPROC)&MSWindowsServerTaskBarReceiver::staticDlgProc, - reinterpret_cast( - reinterpret_cast(this))); + static_cast( + static_cast(this))); // window should appear on top of everything, including (especially) // the task bar. @@ -368,16 +368,16 @@ MSWindowsServerTaskBarReceiver::staticDlgProc(HWND hwnd, // and put it in the extra window data then forward the call. MSWindowsServerTaskBarReceiver* self = NULL; if (msg == WM_INITDIALOG) { - self = reinterpret_cast( - reinterpret_cast(lParam)); + self = static_cast( + static_cast(lParam)); SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam); } else { // get the extra window data and forward the call LONG data = (LONG)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (data != 0) { - self = reinterpret_cast( - reinterpret_cast(data)); + self = static_cast( + static_cast(data)); } } diff --git a/src/gui/src/SynergyLocale.cpp b/src/gui/src/SynergyLocale.cpp index fa980ed43..13c8d2156 100644 --- a/src/gui/src/SynergyLocale.cpp +++ b/src/gui/src/SynergyLocale.cpp @@ -29,7 +29,7 @@ SynergyLocale::SynergyLocale() void SynergyLocale::loadLanguages() { QResource resource(":/res/lang/Languages.xml"); - QByteArray bytes(reinterpret_cast(resource.data()), resource.size()); + QByteArray bytes(static_cast(resource.data()), resource.size()); QXmlStreamReader xml(bytes); while (!xml.atEnd()) diff --git a/src/lib/arch/unix/ArchMultithreadPosix.cpp b/src/lib/arch/unix/ArchMultithreadPosix.cpp index 807894e23..33da29b48 100644 --- a/src/lib/arch/unix/ArchMultithreadPosix.cpp +++ b/src/lib/arch/unix/ArchMultithreadPosix.cpp @@ -697,7 +697,7 @@ void* ArchMultithreadPosix::threadFunc(void* vrep) { // get the thread - ArchThreadImpl* thread = reinterpret_cast(vrep); + ArchThreadImpl* thread = static_cast(vrep); // setup pthreads pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); diff --git a/src/lib/arch/unix/ArchNetworkBSD.cpp b/src/lib/arch/unix/ArchNetworkBSD.cpp index c2d692fcf..f324a7f11 100644 --- a/src/lib/arch/unix/ArchNetworkBSD.cpp +++ b/src/lib/arch/unix/ArchNetworkBSD.cpp @@ -646,7 +646,7 @@ ArchNetworkBSD::newAnyAddr(EAddressFamily family) switch (family) { case kINET: { struct sockaddr_in* ipAddr = - reinterpret_cast(&addr->m_addr); + static_cast(&addr->m_addr); ipAddr->sin_family = AF_INET; ipAddr->sin_port = 0; ipAddr->sin_addr.s_addr = INADDR_ANY; @@ -738,7 +738,7 @@ ArchNetworkBSD::addrToName(ArchNetAddress addr) // mutexed name lookup (ugh) ARCH->lockMutex(m_mutex); struct hostent* info = gethostbyaddr( - reinterpret_cast(&addr->m_addr), + static_cast(&addr->m_addr), addr->m_len, addr->m_addr.sa_family); if (info == NULL) { ARCH->unlockMutex(m_mutex); @@ -762,7 +762,7 @@ ArchNetworkBSD::addrToString(ArchNetAddress addr) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - reinterpret_cast(&addr->m_addr); + static_cast(&addr->m_addr); ARCH->lockMutex(m_mutex); std::string s = inet_ntoa(ipAddr->sin_addr); ARCH->unlockMutex(m_mutex); @@ -797,7 +797,7 @@ ArchNetworkBSD::setAddrPort(ArchNetAddress addr, int port) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - reinterpret_cast(&addr->m_addr); + static_cast(&addr->m_addr); ipAddr->sin_port = htons(port); break; } @@ -816,7 +816,7 @@ ArchNetworkBSD::getAddrPort(ArchNetAddress addr) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - reinterpret_cast(&addr->m_addr); + static_cast(&addr->m_addr); return ntohs(ipAddr->sin_port); } @@ -834,7 +834,7 @@ ArchNetworkBSD::isAnyAddr(ArchNetAddress addr) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - reinterpret_cast(&addr->m_addr); + static_cast(&addr->m_addr); return (ipAddr->sin_addr.s_addr == INADDR_ANY && addr->m_len == (socklen_t)sizeof(struct sockaddr_in)); } diff --git a/src/lib/arch/win32/ArchMiscWindows.cpp b/src/lib/arch/win32/ArchMiscWindows.cpp index d70861d26..53b71286e 100644 --- a/src/lib/arch/win32/ArchMiscWindows.cpp +++ b/src/lib/arch/win32/ArchMiscWindows.cpp @@ -234,7 +234,7 @@ ArchMiscWindows::setValue(HKEY key, return; } RegSetValueEx(key, name, 0, REG_SZ, - reinterpret_cast(value.c_str()), + static_cast(value.c_str()), (DWORD)value.size() + 1); } @@ -247,7 +247,7 @@ ArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value) return; } RegSetValueEx(key, name, 0, REG_DWORD, - reinterpret_cast(&value), + static_cast(&value), sizeof(DWORD)); } @@ -262,7 +262,7 @@ ArchMiscWindows::setValueBinary(HKEY key, return; } RegSetValueEx(key, name, 0, REG_BINARY, - reinterpret_cast(value.data()), + static_cast(value.data()), (DWORD)value.size()); } @@ -287,7 +287,7 @@ ArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR* name, DWORD type) // read it result = RegQueryValueEx(key, name, 0, &actualType, - reinterpret_cast(buffer), &size); + static_cast(buffer), &size); if (result != ERROR_SUCCESS || actualType != type) { delete[] buffer; return std::string(); @@ -322,7 +322,7 @@ ArchMiscWindows::readValueInt(HKEY key, const TCHAR* name) DWORD value; DWORD size = sizeof(value); LONG result = RegQueryValueEx(key, name, 0, &type, - reinterpret_cast(&value), &size); + static_cast(&value), &size); if (result != ERROR_SUCCESS || type != REG_DWORD) { return 0; } @@ -374,7 +374,7 @@ ArchMiscWindows::setThreadExecutionState(DWORD busyModes) if (s_stes == NULL) { HINSTANCE kernel = LoadLibrary("kernel32.dll"); if (kernel != NULL) { - s_stes = reinterpret_cast(GetProcAddress(kernel, + s_stes = static_cast(GetProcAddress(kernel, "SetThreadExecutionState")); } if (s_stes == NULL) { @@ -414,7 +414,7 @@ ArchMiscWindows::wakeupDisplay() if (s_stes == NULL) { HINSTANCE kernel = LoadLibrary("kernel32.dll"); if (kernel != NULL) { - s_stes = reinterpret_cast(GetProcAddress(kernel, + s_stes = static_cast(GetProcAddress(kernel, "SetThreadExecutionState")); } if (s_stes == NULL) { diff --git a/src/lib/arch/win32/ArchMultithreadWindows.cpp b/src/lib/arch/win32/ArchMultithreadWindows.cpp index 0768492de..70aafa2df 100644 --- a/src/lib/arch/win32/ArchMultithreadWindows.cpp +++ b/src/lib/arch/win32/ArchMultithreadWindows.cpp @@ -303,7 +303,7 @@ ArchMultithreadWindows::newThread(ThreadFunc func, void* data) // create thread unsigned int id = 0; - thread->m_thread = reinterpret_cast(_beginthreadex(NULL, 0, + thread->m_thread = static_cast(_beginthreadex(NULL, 0, threadFunc, (void*)thread, 0, &id)); thread->m_id = static_cast(id); @@ -661,7 +661,7 @@ unsigned int __stdcall ArchMultithreadWindows::threadFunc(void* vrep) { // get the thread - ArchThreadImpl* thread = reinterpret_cast(vrep); + ArchThreadImpl* thread = static_cast(vrep); // run thread s_instance->doThreadFunc(thread); diff --git a/src/lib/arch/win32/ArchNetworkWinsock.cpp b/src/lib/arch/win32/ArchNetworkWinsock.cpp index 712e1bdc5..265924d58 100644 --- a/src/lib/arch/win32/ArchNetworkWinsock.cpp +++ b/src/lib/arch/win32/ArchNetworkWinsock.cpp @@ -754,7 +754,7 @@ ArchNetworkWinsock::addrToName(ArchNetAddress addr) // name lookup struct hostent* info = gethostbyaddr_winsock( - reinterpret_cast(&addr->m_addr), + static_cast(&addr->m_addr), addr->m_len, addr->m_addr.sa_family); if (info == NULL) { throwNameError(getsockerror_winsock()); @@ -772,7 +772,7 @@ ArchNetworkWinsock::addrToString(ArchNetAddress addr) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - reinterpret_cast(&addr->m_addr); + static_cast(&addr->m_addr); return inet_ntoa_winsock(ipAddr->sin_addr); } @@ -804,7 +804,7 @@ ArchNetworkWinsock::setAddrPort(ArchNetAddress addr, int port) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - reinterpret_cast(&addr->m_addr); + static_cast(&addr->m_addr); ipAddr->sin_port = htons_winsock(static_cast(port)); break; } @@ -823,7 +823,7 @@ ArchNetworkWinsock::getAddrPort(ArchNetAddress addr) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - reinterpret_cast(&addr->m_addr); + static_cast(&addr->m_addr); return ntohs_winsock(ipAddr->sin_port); } @@ -841,7 +841,7 @@ ArchNetworkWinsock::isAnyAddr(ArchNetAddress addr) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - reinterpret_cast(&addr->m_addr); + static_cast(&addr->m_addr); return (addr->m_len == sizeof(struct sockaddr_in) && ipAddr->sin_addr.s_addr == INADDR_ANY); } diff --git a/src/lib/arch/win32/ArchTaskBarWindows.cpp b/src/lib/arch/win32/ArchTaskBarWindows.cpp index e4154ab25..eac283a86 100644 --- a/src/lib/arch/win32/ArchTaskBarWindows.cpp +++ b/src/lib/arch/win32/ArchTaskBarWindows.cpp @@ -245,7 +245,7 @@ ArchTaskBarWindows::modifyIconNoLock( receiver->lock(); // get icon data - HICON icon = reinterpret_cast( + HICON icon = static_cast( const_cast(receiver->getIcon())); // get tool tip @@ -414,17 +414,17 @@ ArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg, ArchTaskBarWindows* self = NULL; if (msg == WM_NCCREATE) { CREATESTRUCT* createInfo; - createInfo = reinterpret_cast(lParam); - self = reinterpret_cast( + createInfo = static_cast(lParam); + self = static_cast( createInfo->lpCreateParams); - SetWindowLong(hwnd, 0, reinterpret_cast(self)); + SetWindowLong(hwnd, 0, static_cast(self)); } else { // get the extra window data and forward the call LONG data = GetWindowLong(hwnd, 0); if (data != 0) { - self = reinterpret_cast( - reinterpret_cast(data)); + self = static_cast( + static_cast(data)); } } @@ -461,14 +461,14 @@ ArchTaskBarWindows::threadMainLoop() // create window m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, - reinterpret_cast(windowClass), + static_cast(windowClass), TEXT("Synergy Task Bar"), WS_POPUP, 0, 0, 1, 1, NULL, NULL, instanceWin32(), - reinterpret_cast(this)); + static_cast(this)); // signal ready ARCH->lockMutex(m_mutex); @@ -478,7 +478,7 @@ ArchTaskBarWindows::threadMainLoop() // handle failure if (m_hwnd == NULL) { - UnregisterClass(reinterpret_cast(windowClass), instanceWin32()); + UnregisterClass(static_cast(windowClass), instanceWin32()); return; } @@ -494,13 +494,13 @@ ArchTaskBarWindows::threadMainLoop() // clean up removeAllIcons(); DestroyWindow(m_hwnd); - UnregisterClass(reinterpret_cast(windowClass), instanceWin32()); + UnregisterClass(static_cast(windowClass), instanceWin32()); } void* ArchTaskBarWindows::threadEntry(void* self) { - reinterpret_cast(self)->threadMainLoop(); + static_cast(self)->threadMainLoop(); return NULL; } diff --git a/src/lib/arch/win32/ArchTimeWindows.cpp b/src/lib/arch/win32/ArchTimeWindows.cpp index ef3f9ef83..cf48c2a20 100644 --- a/src/lib/arch/win32/ArchTimeWindows.cpp +++ b/src/lib/arch/win32/ArchTimeWindows.cpp @@ -65,7 +65,7 @@ ArchTimeWindows::~ArchTimeWindows() { s_freq = 0.0; if (s_mmInstance == NULL) { - FreeLibrary(reinterpret_cast(s_mmInstance)); + FreeLibrary(static_cast(s_mmInstance)); s_tgt = NULL; s_mmInstance = NULL; } diff --git a/src/lib/base/EventQueue.cpp b/src/lib/base/EventQueue.cpp index 60a163177..5f15293fa 100644 --- a/src/lib/base/EventQueue.cpp +++ b/src/lib/base/EventQueue.cpp @@ -54,7 +54,7 @@ static void interrupt(Arch::ESignal, void* data) { - EventQueue* events = reinterpret_cast(data); + EventQueue* events = static_cast(data); events->addEvent(Event(Event::kQuit)); } diff --git a/src/lib/base/Unicode.cpp b/src/lib/base/Unicode.cpp index ad7a266db..fd4ad6941 100644 --- a/src/lib/base/Unicode.cpp +++ b/src/lib/base/Unicode.cpp @@ -101,7 +101,7 @@ bool Unicode::isUTF8(const String& src) { // convert and test each character - const UInt8* data = reinterpret_cast(src.c_str()); + const UInt8* data = static_cast(src.c_str()); for (UInt32 n = (UInt32)src.size(); n > 0; ) { if (fromUTF8(data, n) == s_invalid) { return false; @@ -122,7 +122,7 @@ Unicode::UTF8ToUCS2(const String& src, bool* errors) dst.reserve(2 * n); // convert each character - const UInt8* data = reinterpret_cast(src.c_str()); + const UInt8* data = static_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); if (c == s_invalid) { @@ -133,7 +133,7 @@ Unicode::UTF8ToUCS2(const String& src, bool* errors) c = s_replacement; } UInt16 ucs2 = static_cast(c); - dst.append(reinterpret_cast(&ucs2), 2); + dst.append(static_cast(&ucs2), 2); } return dst; @@ -151,13 +151,13 @@ Unicode::UTF8ToUCS4(const String& src, bool* errors) dst.reserve(4 * n); // convert each character - const UInt8* data = reinterpret_cast(src.c_str()); + const UInt8* data = static_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); if (c == s_invalid) { c = s_replacement; } - dst.append(reinterpret_cast(&c), 4); + dst.append(static_cast(&c), 4); } return dst; @@ -175,7 +175,7 @@ Unicode::UTF8ToUTF16(const String& src, bool* errors) dst.reserve(2 * n); // convert each character - const UInt8* data = reinterpret_cast(src.c_str()); + const UInt8* data = static_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); if (c == s_invalid) { @@ -187,14 +187,14 @@ Unicode::UTF8ToUTF16(const String& src, bool* errors) } if (c < 0x00010000) { UInt16 ucs2 = static_cast(c); - dst.append(reinterpret_cast(&ucs2), 2); + dst.append(static_cast(&ucs2), 2); } else { c -= 0x00010000; UInt16 utf16h = static_cast((c >> 10) + 0xd800); UInt16 utf16l = static_cast((c & 0x03ff) + 0xdc00); - dst.append(reinterpret_cast(&utf16h), 2); - dst.append(reinterpret_cast(&utf16l), 2); + dst.append(static_cast(&utf16h), 2); + dst.append(static_cast(&utf16l), 2); } } @@ -213,7 +213,7 @@ Unicode::UTF8ToUTF32(const String& src, bool* errors) dst.reserve(4 * n); // convert each character - const UInt8* data = reinterpret_cast(src.c_str()); + const UInt8* data = static_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); if (c == s_invalid) { @@ -223,7 +223,7 @@ Unicode::UTF8ToUTF32(const String& src, bool* errors) setError(errors); c = s_replacement; } - dst.append(reinterpret_cast(&c), 4); + dst.append(static_cast(&c), 4); } return dst; @@ -260,7 +260,7 @@ Unicode::UCS2ToUTF8(const String& src, bool* errors) // convert UInt32 n = (UInt32)src.size() >> 1; - return doUCS2ToUTF8(reinterpret_cast(src.data()), n, errors); + return doUCS2ToUTF8(static_cast(src.data()), n, errors); } String @@ -271,7 +271,7 @@ Unicode::UCS4ToUTF8(const String& src, bool* errors) // convert UInt32 n = (UInt32)src.size() >> 2; - return doUCS4ToUTF8(reinterpret_cast(src.data()), n, errors); + return doUCS4ToUTF8(static_cast(src.data()), n, errors); } String @@ -282,7 +282,7 @@ Unicode::UTF16ToUTF8(const String& src, bool* errors) // convert UInt32 n = (UInt32)src.size() >> 1; - return doUTF16ToUTF8(reinterpret_cast(src.data()), n, errors); + return doUTF16ToUTF8(static_cast(src.data()), n, errors); } String @@ -293,7 +293,7 @@ Unicode::UTF32ToUTF8(const String& src, bool* errors) // convert UInt32 n = (UInt32)src.size() >> 2; - return doUTF32ToUTF8(reinterpret_cast(src.data()), n, errors); + return doUTF32ToUTF8(static_cast(src.data()), n, errors); } String @@ -361,16 +361,16 @@ Unicode::wideCharToUTF8(const wchar_t* src, UInt32 size, bool* errors) // the String's nul character). switch (ARCH->getWideCharEncoding()) { case IArchString::kUCS2: - return doUCS2ToUTF8(reinterpret_cast(src), size, errors); + return doUCS2ToUTF8(static_cast(src), size, errors); case IArchString::kUCS4: - return doUCS4ToUTF8(reinterpret_cast(src), size, errors); + return doUCS4ToUTF8(static_cast(src), size, errors); case IArchString::kUTF16: - return doUTF16ToUTF8(reinterpret_cast(src), size, errors); + return doUTF16ToUTF8(static_cast(src), size, errors); case IArchString::kUTF32: - return doUTF32ToUTF8(reinterpret_cast(src), size, errors); + return doUTF32ToUTF8(static_cast(src), size, errors); default: assert(0 && "unknown wide character encoding"); @@ -741,25 +741,25 @@ Unicode::toUTF8(String& dst, UInt32 c, bool* errors) // convert to UTF-8 if (c < 0x00000080) { data[0] = static_cast(c); - dst.append(reinterpret_cast(data), 1); + dst.append(static_cast(data), 1); } else if (c < 0x00000800) { data[0] = static_cast(((c >> 6) & 0x0000001f) + 0xc0); data[1] = static_cast((c & 0x0000003f) + 0x80); - dst.append(reinterpret_cast(data), 2); + dst.append(static_cast(data), 2); } else if (c < 0x00010000) { data[0] = static_cast(((c >> 12) & 0x0000000f) + 0xe0); data[1] = static_cast(((c >> 6) & 0x0000003f) + 0x80); data[2] = static_cast((c & 0x0000003f) + 0x80); - dst.append(reinterpret_cast(data), 3); + dst.append(static_cast(data), 3); } else if (c < 0x00200000) { data[0] = static_cast(((c >> 18) & 0x00000007) + 0xf0); data[1] = static_cast(((c >> 12) & 0x0000003f) + 0x80); data[2] = static_cast(((c >> 6) & 0x0000003f) + 0x80); data[3] = static_cast((c & 0x0000003f) + 0x80); - dst.append(reinterpret_cast(data), 4); + dst.append(static_cast(data), 4); } else if (c < 0x04000000) { data[0] = static_cast(((c >> 24) & 0x00000003) + 0xf8); @@ -767,7 +767,7 @@ Unicode::toUTF8(String& dst, UInt32 c, bool* errors) data[2] = static_cast(((c >> 12) & 0x0000003f) + 0x80); data[3] = static_cast(((c >> 6) & 0x0000003f) + 0x80); data[4] = static_cast((c & 0x0000003f) + 0x80); - dst.append(reinterpret_cast(data), 5); + dst.append(static_cast(data), 5); } else if (c < 0x80000000) { data[0] = static_cast(((c >> 30) & 0x00000001) + 0xfc); @@ -776,7 +776,7 @@ Unicode::toUTF8(String& dst, UInt32 c, bool* errors) data[3] = static_cast(((c >> 12) & 0x0000003f) + 0x80); data[4] = static_cast(((c >> 6) & 0x0000003f) + 0x80); data[5] = static_cast((c & 0x0000003f) + 0x80); - dst.append(reinterpret_cast(data), 6); + dst.append(static_cast(data), 6); } else { assert(0 && "character out of range"); diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index d984be4cd..e0ef9e8b7 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -430,7 +430,7 @@ Client::sendConnectionFailedEvent(const char* msg) void Client::sendFileChunk(const void* data) { - FileChunk* chunk = reinterpret_cast(const_cast(data)); + FileChunk* chunk = static_cast(const_cast(data)); LOG((CLOG_DEBUG1 "send file chunk")); assert(m_server != NULL); @@ -605,7 +605,7 @@ void Client::handleConnectionFailed(const Event& event, void*) { IDataSocket::ConnectionFailedInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); cleanupTimer(); cleanupConnecting(); @@ -661,7 +661,7 @@ Client::handleClipboardGrabbed(const Event& event, void*) } const IScreen::ClipboardInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); // grab ownership m_server->onGrabClipboard(info->m_id); @@ -810,14 +810,14 @@ Client::sendFileToServer(const char* filename) m_sendFileThread = new Thread( new TMethodJob( this, &Client::sendFileThread, - reinterpret_cast(const_cast(filename)))); + static_cast(const_cast(filename)))); } void Client::sendFileThread(void* filename) { try { - char* name = reinterpret_cast(filename); + char* name = static_cast(filename); StreamChunker::sendFile(name, m_events, this); } catch (std::runtime_error error) { diff --git a/src/lib/io/StreamBuffer.cpp b/src/lib/io/StreamBuffer.cpp index 050adae18..80903ada9 100644 --- a/src/lib/io/StreamBuffer.cpp +++ b/src/lib/io/StreamBuffer.cpp @@ -59,7 +59,7 @@ StreamBuffer::peek(UInt32 n) scan = m_chunks.erase(scan); } - return reinterpret_cast(&(head->begin()[m_headUsed])); + return static_cast(&(head->begin()[m_headUsed])); } void @@ -104,7 +104,7 @@ StreamBuffer::write(const void* vdata, UInt32 n) m_size += n; // cast data to bytes - const UInt8* data = reinterpret_cast(vdata); + const UInt8* data = static_cast(vdata); // point to last chunk if it has space, otherwise append an empty chunk ChunkList::iterator scan = m_chunks.end(); diff --git a/src/lib/io/StreamFilter.cpp b/src/lib/io/StreamFilter.cpp index eb66b9735..468aff847 100644 --- a/src/lib/io/StreamFilter.cpp +++ b/src/lib/io/StreamFilter.cpp @@ -83,7 +83,7 @@ StreamFilter::shutdownOutput() void* StreamFilter::getEventTarget() const { - return const_cast(reinterpret_cast(this)); + return const_cast(static_cast(this)); } bool diff --git a/src/lib/mt/Thread.cpp b/src/lib/mt/Thread.cpp index 0c67e8a99..752aace85 100644 --- a/src/lib/mt/Thread.cpp +++ b/src/lib/mt/Thread.cpp @@ -147,7 +147,7 @@ Thread::threadFunc(void* vjob) } // get job - IJob* job = reinterpret_cast(vjob); + IJob* job = static_cast(vjob); // run job void* result = NULL; diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index 7076c764e..cd0068a5f 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -683,7 +683,7 @@ SecureSocket::verifyCertFingerprint() } // format fingerprint into hexdecimal format with colon separator - String fingerprint(reinterpret_cast(tempFingerprint), tempFingerprintLen); + String fingerprint(static_cast(tempFingerprint), tempFingerprintLen); formatFingerprint(fingerprint); LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); diff --git a/src/lib/net/SocketMultiplexer.cpp b/src/lib/net/SocketMultiplexer.cpp index 9ba352a67..b7f9236f0 100644 --- a/src/lib/net/SocketMultiplexer.cpp +++ b/src/lib/net/SocketMultiplexer.cpp @@ -46,7 +46,7 @@ SocketMultiplexer::SocketMultiplexer() : // this pointer just has to be unique and not NULL. it will // never be dereferenced. it's used to identify cursor nodes // in the jobs list. - m_cursorMark = reinterpret_cast(this); + m_cursorMark = static_cast(this); // start thread m_thread = new Thread(new TMethodJob( diff --git a/src/lib/net/TCPListenSocket.cpp b/src/lib/net/TCPListenSocket.cpp index 94760d534..2b20c1249 100644 --- a/src/lib/net/TCPListenSocket.cpp +++ b/src/lib/net/TCPListenSocket.cpp @@ -102,7 +102,7 @@ TCPListenSocket::close() void* TCPListenSocket::getEventTarget() const { - return const_cast(reinterpret_cast(this)); + return const_cast(static_cast(this)); } IDataSocket* diff --git a/src/lib/net/TCPSocket.cpp b/src/lib/net/TCPSocket.cpp index ce2cdd099..680e49e57 100644 --- a/src/lib/net/TCPSocket.cpp +++ b/src/lib/net/TCPSocket.cpp @@ -125,7 +125,7 @@ TCPSocket::close() void* TCPSocket::getEventTarget() const { - return const_cast(reinterpret_cast(this)); + return const_cast(static_cast(this)); } UInt32 diff --git a/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp b/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp index c78bd3c49..9b39ca7e0 100644 --- a/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp +++ b/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp @@ -78,7 +78,7 @@ MSWindowsClipboardBitmapConverter::toIClipboard(HANDLE data) const UInt32 srcSize = (UInt32)GlobalSize(data); // check image type - const BITMAPINFO* bitmap = reinterpret_cast(src); + const BITMAPINFO* bitmap = static_cast(src); LOG((CLOG_INFO "bitmap: %dx%d %d", bitmap->bmiHeader.biWidth, bitmap->bmiHeader.biHeight, (int)bitmap->bmiHeader.biBitCount)); if (bitmap->bmiHeader.biPlanes == 1 && (bitmap->bmiHeader.biBitCount == 24 || diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index cb1ed9e98..555d2e79c 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -241,7 +241,7 @@ void MSWindowsDesks::getCursorPos(SInt32& x, SInt32& y) const { POINT pos; - sendMessage(SYNERGY_MSG_CURSOR_POS, reinterpret_cast(&pos), 0); + sendMessage(SYNERGY_MSG_CURSOR_POS, static_cast(&pos), 0); x = pos.x; y = pos.y; } @@ -427,7 +427,7 @@ void MSWindowsDesks::destroyClass(ATOM windowClass) const { if (windowClass != 0) { - UnregisterClass(reinterpret_cast(windowClass), + UnregisterClass(static_cast(windowClass), MSWindowsScreen::getWindowInstance()); } } @@ -437,7 +437,7 @@ MSWindowsDesks::createWindow(ATOM windowClass, const char* name) const { HWND window = CreateWindowEx(WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - reinterpret_cast(windowClass), + static_cast(windowClass), name, WS_POPUP, 0, 0, 1, 1, @@ -656,7 +656,7 @@ MSWindowsDesks::deskThread(void* vdesk) MSG msg; // use given desktop for this thread - Desk* desk = reinterpret_cast(vdesk); + Desk* desk = static_cast(vdesk); desk->m_threadID = GetCurrentThreadId(); desk->m_window = NULL; desk->m_foregroundWindow = NULL; @@ -757,7 +757,7 @@ MSWindowsDesks::deskThread(void* vdesk) break; case SYNERGY_MSG_CURSOR_POS: { - POINT* pos = reinterpret_cast(msg.wParam); + POINT* pos = static_cast(msg.wParam); if (!GetCursorPos(pos)) { pos->x = m_xCenter; pos->y = m_yCenter; diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index de0c7c268..68c628cc1 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -864,7 +864,7 @@ void MSWindowsScreen::destroyClass(ATOM windowClass) const { if (windowClass != 0) { - UnregisterClass(reinterpret_cast(windowClass), s_windowInstance); + UnregisterClass(static_cast(windowClass), s_windowInstance); } } @@ -874,7 +874,7 @@ MSWindowsScreen::createWindow(ATOM windowClass, const char* name) const HWND window = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - reinterpret_cast(windowClass), + static_cast(windowClass), name, WS_POPUP, 0, 0, 1, 1, @@ -895,7 +895,7 @@ MSWindowsScreen::createDropWindow(ATOM windowClass, const char* name) const WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_ACCEPTFILES, - reinterpret_cast(m_class), + static_cast(m_class), name, WS_POPUP, 0, 0, m_dropWindowSize, m_dropWindowSize, @@ -941,7 +941,7 @@ MSWindowsScreen::sendClipboardEvent(Event::Type type, ClipboardID id) void MSWindowsScreen::handleSystemEvent(const Event& event, void*) { - MSG* msg = reinterpret_cast(event.getData()); + MSG* msg = static_cast(event.getData()); assert(msg != NULL); if (ArchMiscWindows::processDialog(msg)) { diff --git a/src/lib/platform/MSWindowsScreenSaver.cpp b/src/lib/platform/MSWindowsScreenSaver.cpp index f2ab7334f..4289bb01a 100644 --- a/src/lib/platform/MSWindowsScreenSaver.cpp +++ b/src/lib/platform/MSWindowsScreenSaver.cpp @@ -162,7 +162,7 @@ MSWindowsScreenSaver::deactivate() if (desktop != NULL) { EnumDesktopWindows(desktop, &MSWindowsScreenSaver::killScreenSaverFunc, - reinterpret_cast(&killed)); + static_cast(&killed)); CloseDesktop(desktop); } @@ -205,7 +205,7 @@ MSWindowsScreenSaver::killScreenSaverFunc(HWND hwnd, LPARAM arg) HINSTANCE instance = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE); if (instance != MSWindowsScreen::getWindowInstance()) { PostMessage(hwnd, WM_CLOSE, 0, 0); - *reinterpret_cast(arg) = true; + *static_cast(arg) = true; } } return TRUE; diff --git a/src/lib/platform/OSXClipboardBMPConverter.cpp b/src/lib/platform/OSXClipboardBMPConverter.cpp index faeac2912..574b917ad 100644 --- a/src/lib/platform/OSXClipboardBMPConverter.cpp +++ b/src/lib/platform/OSXClipboardBMPConverter.cpp @@ -104,7 +104,7 @@ OSXClipboardBMPConverter::fromIClipboard(const String& bmp) const toLE(dst, static_cast(0)); toLE(dst, static_cast(0)); toLE(dst, static_cast(14 + 40)); - return String(reinterpret_cast(header), 14) + bmp; + return String(static_cast(header), 14) + bmp; } String @@ -116,7 +116,7 @@ OSXClipboardBMPConverter::toIClipboard(const String& bmp) const } // check BMP file header - const UInt8* rawBMPHeader = reinterpret_cast(bmp.data()); + const UInt8* rawBMPHeader = static_cast(bmp.data()); if (rawBMPHeader[0] != 'B' || rawBMPHeader[1] != 'M') { return String(); } diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 2071621bf..33d81fdf6 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -415,7 +415,7 @@ OSXKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const { ::KeyMap km; GetKeys(km); - const UInt8* m = reinterpret_cast(km); + const UInt8* m = static_cast(km); for (UInt32 i = 0; i < 16; ++i) { for (UInt32 j = 0; j < 8; ++j) { if ((m[i] & (1u << j)) != 0) { diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index 7ca761385..e81f1621d 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -986,7 +986,7 @@ OSXScreen::sendClipboardEvent(Event::Type type, ClipboardID id) const void OSXScreen::handleSystemEvent(const Event& event, void*) { - EventRef* carbonEvent = reinterpret_cast(event.getData()); + EventRef* carbonEvent = static_cast(event.getData()); assert(carbonEvent != NULL); UInt32 eventClass = GetEventClass(*carbonEvent); diff --git a/src/lib/platform/OSXUchrKeyResource.cpp b/src/lib/platform/OSXUchrKeyResource.cpp index 7c362d709..b3f785b84 100644 --- a/src/lib/platform/OSXUchrKeyResource.cpp +++ b/src/lib/platform/OSXUchrKeyResource.cpp @@ -31,7 +31,7 @@ OSXUchrKeyResource::OSXUchrKeyResource(const void* resource, m_sri(NULL), m_st(NULL) { - m_resource = reinterpret_cast(resource); + m_resource = static_cast(resource); if (m_resource == NULL) { return; } @@ -56,19 +56,19 @@ OSXUchrKeyResource::OSXUchrKeyResource(const void* resource, } // get tables for keyboard type - const UInt8* base = reinterpret_cast(m_resource); - m_m = reinterpret_cast(base + + const UInt8* base = static_cast(m_resource); + m_m = static_cast(base + th->keyModifiersToTableNumOffset); - m_cti = reinterpret_cast(base + + m_cti = static_cast(base + th->keyToCharTableIndexOffset); - m_sdi = reinterpret_cast(base + + m_sdi = static_cast(base + th->keySequenceDataIndexOffset); if (th->keyStateRecordsIndexOffset != 0) { - m_sri = reinterpret_cast(base + + m_sri = static_cast(base + th->keyStateRecordsIndexOffset); } if (th->keyStateTerminatorsOffset != 0) { - m_st = reinterpret_cast(base + + m_st = static_cast(base + th->keyStateTerminatorsOffset); } @@ -81,7 +81,7 @@ OSXUchrKeyResource::OSXUchrKeyResource(const void* resource, KeyID id = getKey(table, button); if (id == 0x20) { UCKeyOutput c = - reinterpret_cast(base + + static_cast(base + m_cti->keyToCharTableOffsets[table])[button]; if ((c & kUCKeyOutputTestForIndexMask) == kUCKeyOutputStateIndexMask) { @@ -134,8 +134,8 @@ OSXUchrKeyResource::getKey(UInt32 table, UInt32 button) const assert(table < getNumTables()); assert(button < getNumButtons()); - const UInt8* base = reinterpret_cast(m_resource); - const UCKeyOutput* cPtr = reinterpret_cast(base + + const UInt8* base = static_cast(m_resource); + const UCKeyOutput* cPtr = static_cast(base + m_cti->keyToCharTableOffsets[table]); const UCKeyOutput c = cPtr[button]; @@ -211,12 +211,12 @@ bool OSXUchrKeyResource::getKeyRecord( KeySequence& keys, UInt16 index, UInt16& state) const { - const UInt8* base = reinterpret_cast(m_resource); + const UInt8* base = static_cast(m_resource); const UCKeyStateRecord* sr = - reinterpret_cast(base + + static_cast(base + m_sri->keyStateRecordOffsets[index]); const UCKeyStateEntryTerminal* kset = - reinterpret_cast(sr->stateEntryData); + static_cast(sr->stateEntryData); UInt16 nextState = 0; bool found = false; diff --git a/src/lib/platform/XWindowsClipboard.cpp b/src/lib/platform/XWindowsClipboard.cpp index 6aa3a4cb8..bba70becd 100644 --- a/src/lib/platform/XWindowsClipboard.cpp +++ b/src/lib/platform/XWindowsClipboard.cpp @@ -516,7 +516,7 @@ XWindowsClipboard::icccmFillCache() } XWindowsUtil::convertAtomProperty(data); - const Atom* targets = reinterpret_cast(data.data()); + const Atom* targets = static_cast(data.data()); const UInt32 numTargets = data.size() / sizeof(Atom); LOG((CLOG_DEBUG " available targets: %s", XWindowsUtil::atomsToString(m_display, targets, numTargets).c_str())); @@ -594,7 +594,7 @@ XWindowsClipboard::icccmGetTime() const String data; if (icccmGetSelection(m_atomTimestamp, &actualTarget, &data) && actualTarget == m_atomInteger) { - Time time = *reinterpret_cast(data.data()); + Time time = *static_cast(data.data()); LOG((CLOG_DEBUG1 "got ICCCM time %d", time)); return time; } @@ -672,7 +672,7 @@ XWindowsClipboard::motifOwnsClipboard() const // check the owner window against the current clipboard owner const MotifClipHeader* header = - reinterpret_cast(data.data()); + static_cast(data.data()); if (data.size() >= sizeof(MotifClipHeader) && header->m_id == kMotifClipHeader) { if (static_cast(header->m_selectionOwner) == owner) { @@ -701,7 +701,7 @@ XWindowsClipboard::motifFillCache() // check that the header is okay const MotifClipHeader* header = - reinterpret_cast(data.data()); + static_cast(data.data()); if (data.size() < sizeof(MotifClipHeader) || header->m_id != kMotifClipHeader || header->m_numItems < 1) { @@ -721,7 +721,7 @@ XWindowsClipboard::motifFillCache() // check that the item is okay const MotifClipItem* item = - reinterpret_cast(data.data()); + static_cast(data.data()); if (data.size() < sizeof(MotifClipItem) || item->m_id != kMotifClipItem || item->m_numFormats - item->m_numDeletedFormats < 1) { @@ -730,8 +730,8 @@ XWindowsClipboard::motifFillCache() // format list is after static item structure elements const SInt32 numFormats = item->m_numFormats - item->m_numDeletedFormats; - const SInt32* formats = reinterpret_cast(item->m_size + - reinterpret_cast(data.data())); + const SInt32* formats = static_cast(item->m_size + + static_cast(data.data())); // get the available formats typedef std::map MotifFormatMap; @@ -749,7 +749,7 @@ XWindowsClipboard::motifFillCache() // check that the format is okay const MotifClipFormat* motifFormat = - reinterpret_cast(data.data()); + static_cast(data.data()); if (data.size() < sizeof(MotifClipFormat) || motifFormat->m_id != kMotifClipFormat || motifFormat->m_length < 0 || @@ -783,7 +783,7 @@ XWindowsClipboard::motifFillCache() // get format const MotifClipFormat* motifFormat = - reinterpret_cast( + static_cast( index2->second.data()); const Atom target = motifFormat->m_type; @@ -855,7 +855,7 @@ XWindowsClipboard::insertMultipleReply(Window requestor, // data is a list of atom pairs: target, property XWindowsUtil::convertAtomProperty(data); - const Atom* targets = reinterpret_cast(data.data()); + const Atom* targets = static_cast(data.data()); const UInt32 numTargets = data.size() / sizeof(Atom); // add replies for each target diff --git a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp index 07f89a3ab..94b335932 100644 --- a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp +++ b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp @@ -127,7 +127,7 @@ XWindowsClipboardAnyBitmapConverter::fromIClipboard(const String& bmp) const { // fill BMP info header with native-endian data CBMPInfoHeader infoHeader; - const UInt8* rawBMPInfoHeader = reinterpret_cast(bmp.data()); + const UInt8* rawBMPInfoHeader = static_cast(bmp.data()); infoHeader.biSize = fromLEU32(rawBMPInfoHeader + 0); infoHeader.biWidth = fromLES32(rawBMPInfoHeader + 4); infoHeader.biHeight = fromLES32(rawBMPInfoHeader + 8); @@ -186,6 +186,6 @@ XWindowsClipboardAnyBitmapConverter::toIClipboard(const String& image) const toLE(dst, static_cast(0)); // construct image - return String(reinterpret_cast(infoHeader), + return String(static_cast(infoHeader), sizeof(infoHeader)) + rawBMP; } diff --git a/src/lib/platform/XWindowsClipboardBMPConverter.cpp b/src/lib/platform/XWindowsClipboardBMPConverter.cpp index e1f35ff14..e28e81ded 100644 --- a/src/lib/platform/XWindowsClipboardBMPConverter.cpp +++ b/src/lib/platform/XWindowsClipboardBMPConverter.cpp @@ -113,7 +113,7 @@ XWindowsClipboardBMPConverter::fromIClipboard(const String& bmp) const toLE(dst, static_cast(0)); toLE(dst, static_cast(0)); toLE(dst, static_cast(14 + 40)); - return String(reinterpret_cast(header), 14) + bmp; + return String(static_cast(header), 14) + bmp; } String @@ -125,7 +125,7 @@ XWindowsClipboardBMPConverter::toIClipboard(const String& bmp) const } // check BMP file header - const UInt8* rawBMPHeader = reinterpret_cast(bmp.data()); + const UInt8* rawBMPHeader = static_cast(bmp.data()); if (rawBMPHeader[0] != 'B' || rawBMPHeader[1] != 'M') { return String(); } diff --git a/src/lib/platform/XWindowsKeyState.cpp b/src/lib/platform/XWindowsKeyState.cpp index 6f82d4dfb..f97ef531a 100644 --- a/src/lib/platform/XWindowsKeyState.cpp +++ b/src/lib/platform/XWindowsKeyState.cpp @@ -785,7 +785,7 @@ void XWindowsKeyState::remapKeyModifiers(KeyID id, SInt32 group, synergy::KeyMap::KeyItem& item, void* vself) { - XWindowsKeyState* self = reinterpret_cast(vself); + XWindowsKeyState* self = static_cast(vself); item.m_required = self->mapModifiersFromX(XkbBuildCoreState(item.m_required, group)); item.m_sensitive = diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index b16e2e516..6c3e1357a 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -1169,7 +1169,7 @@ XWindowsScreen::getKeyState() const Bool XWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg) { - KeyEventFilter* filter = reinterpret_cast(arg); + KeyEventFilter* filter = static_cast(arg); return (xevent->type == filter->m_event && xevent->xkey.window == filter->m_window && xevent->xkey.time == filter->m_time && @@ -1179,7 +1179,7 @@ XWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg) void XWindowsScreen::handleSystemEvent(const Event& event, void*) { - XEvent* xevent = reinterpret_cast(event.getData()); + XEvent* xevent = static_cast(event.getData()); assert(xevent != NULL); // update key state @@ -1408,7 +1408,7 @@ XWindowsScreen::handleSystemEvent(const Event& event, void*) default: #if HAVE_XKB_EXTENSION if (m_xkb && xevent->type == m_xkbEventBase) { - XkbEvent* xkbEvent = reinterpret_cast(xevent); + XkbEvent* xkbEvent = static_cast(xevent); switch (xkbEvent->any.xkb_type) { case XkbMapNotify: refreshKeyboard(xevent); @@ -1426,7 +1426,7 @@ XWindowsScreen::handleSystemEvent(const Event& event, void*) if (m_xrandr) { if (xevent->type == m_xrandrEventBase + RRScreenChangeNotify || xevent->type == m_xrandrEventBase + RRNotify - && reinterpret_cast(xevent)->subtype == RRNotify_CrtcChange) { + && static_cast(xevent)->subtype == RRNotify_CrtcChange) { LOG((CLOG_INFO "XRRScreenChangeNotifyEvent or RRNotify_CrtcChange received")); // we're required to call back into XLib so XLib can update its internal state diff --git a/src/lib/platform/XWindowsUtil.cpp b/src/lib/platform/XWindowsUtil.cpp index 6103cced8..304bca131 100644 --- a/src/lib/platform/XWindowsUtil.cpp +++ b/src/lib/platform/XWindowsUtil.cpp @@ -1386,7 +1386,7 @@ XWindowsUtil::setWindowProperty(Display* display, Window window, Atom type, SInt32 format) { const UInt32 length = 4 * XMaxRequestSize(display); - const unsigned char* data = reinterpret_cast(vdata); + const unsigned char* data = static_cast(vdata); UInt32 datumSize = static_cast(format / 8); // format 32 on 64bit systems is 8 bytes not 4. if (format == 32) { @@ -1665,35 +1665,35 @@ XWindowsUtil::convertAtomProperty(String& data) // 64-bit numbers we have to ensure the last number is a full 64 bits. if (sizeof(Atom) != 4 && ((data.size() / 4) & 1) != 0) { UInt32 zero = 0; - data.append(reinterpret_cast(&zero), sizeof(zero)); + data.append(static_cast(&zero), sizeof(zero)); } } void XWindowsUtil::appendAtomData(String& data, Atom atom) { - data.append(reinterpret_cast(&atom), sizeof(Atom)); + data.append(static_cast(&atom), sizeof(Atom)); } void XWindowsUtil::replaceAtomData(String& data, UInt32 index, Atom atom) { data.replace(index * sizeof(Atom), sizeof(Atom), - reinterpret_cast(&atom), + static_cast(&atom), sizeof(Atom)); } void XWindowsUtil::appendTimeData(String& data, Time time) { - data.append(reinterpret_cast(&time), sizeof(Time)); + data.append(static_cast(&time), sizeof(Time)); } Bool XWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) { PropertyNotifyPredicateInfo* filter = - reinterpret_cast(arg); + static_cast(arg); return (xevent->type == PropertyNotify && xevent->xproperty.window == filter->m_window && xevent->xproperty.atom == filter->m_property && @@ -1784,5 +1784,5 @@ void XWindowsUtil::ErrorLock::saveHandler(Display*, XErrorEvent* e, void* flag) { LOG((CLOG_DEBUG1 "flagging X error: %d", e->error_code)); - *reinterpret_cast(flag) = true; + *static_cast(flag) = true; } diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index a9777aae1..619ba2a0a 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -178,7 +178,7 @@ void ClientListener::handleUnknownClient(const Event&, void* vclient) { ClientProxyUnknown* unknownClient = - reinterpret_cast(vclient); + static_cast(vclient); // we should have the client in our new client list assert(m_newClients.count(unknownClient) == 1); @@ -222,7 +222,7 @@ ClientListener::handleUnknownClient(const Event&, void* vclient) void ClientListener::handleClientDisconnected(const Event&, void* vclient) { - ClientProxy* client = reinterpret_cast(vclient); + ClientProxy* client = static_cast(vclient); // find client in waiting clients queue for (WaitingClients::iterator i = m_waitingClients.begin(), diff --git a/src/lib/server/InputFilter.cpp b/src/lib/server/InputFilter.cpp index f03786a7b..0ab2fcaa6 100644 --- a/src/lib/server/InputFilter.cpp +++ b/src/lib/server/InputFilter.cpp @@ -121,7 +121,7 @@ InputFilter::KeystrokeCondition::match(const Event& event) // check if it's our hotkey IPrimaryScreen::HotKeyInfo* kinfo = - reinterpret_cast(event.getData()); + static_cast(event.getData()); if (kinfo->m_id != m_id) { return kNoMatch; } @@ -217,7 +217,7 @@ InputFilter::MouseButtonCondition::match(const Event& event) // check if it's the right button and modifiers. ignore modifiers // that cannot be combined with a mouse button. IPlatformScreen::ButtonInfo* minfo = - reinterpret_cast(event.getData()); + static_cast(event.getData()); if (minfo->m_button != m_button || (minfo->m_mask & ~s_ignoreMask) != m_mask) { return kNoMatch; @@ -256,7 +256,7 @@ InputFilter::ScreenConnectedCondition::match(const Event& event) { if (event.getType() == m_events->forServer().connected()) { Server::ScreenConnectedInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); if (m_screen == info->m_screen || m_screen.empty()) { return kActivate; } @@ -357,7 +357,7 @@ InputFilter::SwitchToScreenAction::perform(const Event& event) String screen = m_screen; if (screen.empty() && event.getType() == m_events->forServer().connected()) { Server::ScreenConnectedInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); screen = info->m_screen; } diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index b0ff56fc6..d9394ed57 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -1187,7 +1187,7 @@ void Server::handleShapeChanged(const Event&, void* vclient) { // ignore events from unknown clients - BaseClientProxy* client = reinterpret_cast(vclient); + BaseClientProxy* client = static_cast(vclient); if (m_clientSet.count(client) == 0) { return; } @@ -1224,12 +1224,12 @@ Server::handleClipboardGrabbed(const Event& event, void* vclient) } // ignore events from unknown clients - BaseClientProxy* grabber = reinterpret_cast(vclient); + BaseClientProxy* grabber = static_cast(vclient); if (m_clientSet.count(grabber) == 0) { return; } const IScreen::ClipboardInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); // ignore grab if sequence number is old. always allow primary // screen to grab. @@ -1270,12 +1270,12 @@ void Server::handleClipboardChanged(const Event& event, void* vclient) { // ignore events from unknown clients - BaseClientProxy* sender = reinterpret_cast(vclient); + BaseClientProxy* sender = static_cast(vclient); if (m_clientSet.count(sender) == 0) { return; } const IScreen::ClipboardInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); onClipboardChanged(sender, info->m_id, info->m_sequenceNumber); } @@ -1283,7 +1283,7 @@ void Server::handleKeyDownEvent(const Event& event, void*) { IPlatformScreen::KeyInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); onKeyDown(info->m_key, info->m_mask, info->m_button, info->m_screens); } @@ -1291,7 +1291,7 @@ void Server::handleKeyUpEvent(const Event& event, void*) { IPlatformScreen::KeyInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); onKeyUp(info->m_key, info->m_mask, info->m_button, info->m_screens); } @@ -1299,7 +1299,7 @@ void Server::handleKeyRepeatEvent(const Event& event, void*) { IPlatformScreen::KeyInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); onKeyRepeat(info->m_key, info->m_mask, info->m_count, info->m_button); } @@ -1307,7 +1307,7 @@ void Server::handleButtonDownEvent(const Event& event, void*) { IPlatformScreen::ButtonInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); onMouseDown(info->m_button); } @@ -1315,7 +1315,7 @@ void Server::handleButtonUpEvent(const Event& event, void*) { IPlatformScreen::ButtonInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); onMouseUp(info->m_button); } @@ -1323,7 +1323,7 @@ void Server::handleMotionPrimaryEvent(const Event& event, void*) { IPlatformScreen::MotionInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); onMouseMovePrimary(info->m_x, info->m_y); } @@ -1331,7 +1331,7 @@ void Server::handleMotionSecondaryEvent(const Event& event, void*) { IPlatformScreen::MotionInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); onMouseMoveSecondary(info->m_x, info->m_y); } @@ -1339,7 +1339,7 @@ void Server::handleWheelEvent(const Event& event, void*) { IPlatformScreen::WheelInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); onMouseWheel(info->m_xDelta, info->m_yDelta); } @@ -1374,7 +1374,7 @@ Server::handleClientDisconnected(const Event&, void* vclient) { // client has disconnected. it might be an old client or an // active client. we don't care so just handle it both ways. - BaseClientProxy* client = reinterpret_cast(vclient); + BaseClientProxy* client = static_cast(vclient); removeActiveClient(client); removeOldClient(client); @@ -1388,7 +1388,7 @@ void Server::handleClientCloseTimeout(const Event&, void* vclient) { // client took too long to disconnect. just dump it. - BaseClientProxy* client = reinterpret_cast(vclient); + BaseClientProxy* client = static_cast(vclient); LOG((CLOG_NOTE "forced disconnection of client \"%s\"", getName(client).c_str())); removeOldClient(client); PacketStreamFilter* streamFileter = dynamic_cast(client->getStream()); @@ -1401,7 +1401,7 @@ void Server::handleSwitchToScreenEvent(const Event& event, void*) { SwitchToScreenInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); ClientList::const_iterator index = m_clients.find(info->m_screen); if (index == m_clients.end()) { @@ -1416,7 +1416,7 @@ void Server::handleSwitchInDirectionEvent(const Event& event, void*) { SwitchInDirectionInfo* info = - reinterpret_cast(event.getData()); + static_cast(event.getData()); // jump to screen in chosen direction from center of this screen SInt32 x = m_x, y = m_y; @@ -1817,7 +1817,7 @@ Server::onMouseMovePrimary(SInt32 x, SInt32 y) void Server::sendDragInfoThread(void* arg) { - BaseClientProxy* newScreen = reinterpret_cast(arg); + BaseClientProxy* newScreen = static_cast(arg); m_dragFileList.clear(); String& dragFileList = m_screen->getDraggingFilename(); @@ -2053,7 +2053,7 @@ Server::onMouseWheel(SInt32 xDelta, SInt32 yDelta) void Server::onFileChunkSending(const void* data) { - FileChunk* chunk = reinterpret_cast(const_cast(data)); + FileChunk* chunk = static_cast(const_cast(data)); LOG((CLOG_DEBUG1 "sending file chunk")); assert(m_active != NULL); @@ -2372,14 +2372,14 @@ Server::sendFileToClient(const char* filename) m_sendFileThread = new Thread( new TMethodJob( this, &Server::sendFileThread, - reinterpret_cast(const_cast(filename)))); + static_cast(const_cast(filename)))); } void Server::sendFileThread(void* data) { try { - char* filename = reinterpret_cast(data); + char* filename = static_cast(data); LOG((CLOG_DEBUG "sending file to client, filename=%s", filename)); StreamChunker::sendFile(filename, m_events, this); } diff --git a/src/lib/synergy/ClientApp.cpp b/src/lib/synergy/ClientApp.cpp index 54f9f687e..10aba5f32 100644 --- a/src/lib/synergy/ClientApp.cpp +++ b/src/lib/synergy/ClientApp.cpp @@ -268,7 +268,7 @@ void ClientApp::handleClientRestart(const Event&, void* vtimer) { // discard old timer - EventQueueTimer* timer = reinterpret_cast(vtimer); + EventQueueTimer* timer = static_cast(vtimer); m_events->deleteTimer(timer); m_events->removeHandler(Event::kTimer, timer); @@ -301,7 +301,7 @@ void ClientApp::handleClientFailed(const Event& e, void*) { Client::FailInfo* info = - reinterpret_cast(e.getData()); + static_cast(e.getData()); updateStatus(String("Failed to connect to server: ") + info->m_what); if (!args().m_restartable || !info->m_retry) { diff --git a/src/lib/synergy/ClipboardChunk.cpp b/src/lib/synergy/ClipboardChunk.cpp index f7a5673db..781098d9d 100644 --- a/src/lib/synergy/ClipboardChunk.cpp +++ b/src/lib/synergy/ClipboardChunk.cpp @@ -41,7 +41,7 @@ ClipboardChunk::start( char* chunk = start->m_chunk; chunk[0] = id; - UInt32* seq = reinterpret_cast(&chunk[1]); + UInt32* seq = static_cast(&chunk[1]); *seq = sequence; chunk[5] = kDataStart; memcpy(&chunk[6], size.c_str(), sizeLength); @@ -61,7 +61,7 @@ ClipboardChunk::data( char* chunkData = chunk->m_chunk; chunkData[0] = id; - UInt32* seq = reinterpret_cast(&chunkData[1]); + UInt32* seq = static_cast(&chunkData[1]); *seq = sequence; chunkData[5] = kDataChunk; memcpy(&chunkData[6], data.c_str(), dataSize); @@ -77,7 +77,7 @@ ClipboardChunk::end(ClipboardID id, UInt32 sequence) char* chunk = end->m_chunk; chunk[0] = id; - UInt32* seq = reinterpret_cast(&chunk[1]); + UInt32* seq = static_cast(&chunk[1]); *seq = sequence; chunk[5] = kDataEnd; chunk[CLIPBOARD_CHUNK_META_SIZE - 1] = '\0'; @@ -127,13 +127,13 @@ ClipboardChunk::assemble(synergy::IStream* stream, void ClipboardChunk::send(synergy::IStream* stream, void* data) { - ClipboardChunk* clipboardData = reinterpret_cast(data); + ClipboardChunk* clipboardData = static_cast(data); LOG((CLOG_DEBUG1 "sending clipboard chunk")); char* chunk = clipboardData->m_chunk; ClipboardID id = chunk[0]; - UInt32* seq = reinterpret_cast(&chunk[1]); + UInt32* seq = static_cast(&chunk[1]); UInt32 sequence = *seq; UInt8 mark = chunk[5]; String dataChunk(&chunk[6], clipboardData->m_dataSize); diff --git a/src/lib/synergy/IClipboard.cpp b/src/lib/synergy/IClipboard.cpp index a8019184d..42ca0c85a 100644 --- a/src/lib/synergy/IClipboard.cpp +++ b/src/lib/synergy/IClipboard.cpp @@ -151,7 +151,7 @@ IClipboard::copy(IClipboard* dst, const IClipboard* src, Time time) UInt32 IClipboard::readUInt32(const char* buf) { - const unsigned char* ubuf = reinterpret_cast(buf); + const unsigned char* ubuf = static_cast(buf); return (static_cast(ubuf[0]) << 24) | (static_cast(ubuf[1]) << 16) | (static_cast(ubuf[2]) << 8) | diff --git a/src/lib/synergy/KeyState.cpp b/src/lib/synergy/KeyState.cpp index 3808b9449..bdb8706cf 100644 --- a/src/lib/synergy/KeyState.cpp +++ b/src/lib/synergy/KeyState.cpp @@ -524,7 +524,7 @@ KeyState::addActiveModifierCB(KeyID, SInt32 group, synergy::KeyMap::KeyItem& keyItem, void* vcontext) { AddActiveModifierContext* context = - reinterpret_cast(vcontext); + static_cast(vcontext); if (group == context->m_activeGroup && (keyItem.m_generates & context->m_mask) != 0) { context->m_activeModifiers.insert(std::make_pair( diff --git a/src/lib/synergy/ProtocolUtil.cpp b/src/lib/synergy/ProtocolUtil.cpp index ab361880d..ae796fa9c 100644 --- a/src/lib/synergy/ProtocolUtil.cpp +++ b/src/lib/synergy/ProtocolUtil.cpp @@ -120,27 +120,27 @@ ProtocolUtil::vreadf(synergy::IStream* stream, const char* fmt, va_list args) switch (len) { case 1: // 1 byte integer - *reinterpret_cast(v) = buffer[0]; - LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + *static_cast(v) = buffer[0]; + LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *static_cast(v), *static_cast(v))); break; case 2: // 2 byte integer - *reinterpret_cast(v) = + *static_cast(v) = static_cast( (static_cast(buffer[0]) << 8) | static_cast(buffer[1])); - LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *static_cast(v), *static_cast(v))); break; case 4: // 4 byte integer - *reinterpret_cast(v) = + *static_cast(v) = (static_cast(buffer[0]) << 24) | (static_cast(buffer[1]) << 16) | (static_cast(buffer[2]) << 8) | static_cast(buffer[3]); - LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *reinterpret_cast(v), *reinterpret_cast(v))); + LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *static_cast(v), *static_cast(v))); break; } break; @@ -165,9 +165,9 @@ ProtocolUtil::vreadf(synergy::IStream* stream, const char* fmt, va_list args) // 1 byte integer for (UInt32 i = 0; i < n; ++i) { read(stream, buffer, 1); - reinterpret_cast*>(v)->push_back( + static_cast*>(v)->push_back( buffer[0]); - LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast*>(v)->back(), reinterpret_cast*>(v)->back())); + LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, static_cast*>(v)->back(), static_cast*>(v)->back())); } break; @@ -175,11 +175,11 @@ ProtocolUtil::vreadf(synergy::IStream* stream, const char* fmt, va_list args) // 2 byte integer for (UInt32 i = 0; i < n; ++i) { read(stream, buffer, 2); - reinterpret_cast*>(v)->push_back( + static_cast*>(v)->push_back( static_cast( (static_cast(buffer[0]) << 8) | static_cast(buffer[1]))); - LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast*>(v)->back(), reinterpret_cast*>(v)->back())); + LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, static_cast*>(v)->back(), static_cast*>(v)->back())); } break; @@ -187,12 +187,12 @@ ProtocolUtil::vreadf(synergy::IStream* stream, const char* fmt, va_list args) // 4 byte integer for (UInt32 i = 0; i < n; ++i) { read(stream, buffer, 4); - reinterpret_cast*>(v)->push_back( + static_cast*>(v)->push_back( (static_cast(buffer[0]) << 24) | (static_cast(buffer[1]) << 16) | (static_cast(buffer[2]) << 8) | static_cast(buffer[3])); - LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, reinterpret_cast*>(v)->back(), reinterpret_cast*>(v)->back())); + LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, static_cast*>(v)->back(), static_cast*>(v)->back())); } break; } @@ -340,7 +340,7 @@ ProtocolUtil::getLength(const char* fmt, va_list args) void ProtocolUtil::writef(void* buffer, const char* fmt, va_list args) { - UInt8* dst = reinterpret_cast(buffer); + UInt8* dst = static_cast(buffer); while (*fmt) { if (*fmt == '%') { @@ -515,7 +515,7 @@ ProtocolUtil::read(synergy::IStream* stream, void* vbuffer, UInt32 count) assert(stream != NULL); assert(vbuffer != NULL); - UInt8* buffer = reinterpret_cast(vbuffer); + UInt8* buffer = static_cast(vbuffer); while (count > 0) { // read more UInt32 n = stream->read(buffer, count); diff --git a/src/lib/synergy/ServerApp.cpp b/src/lib/synergy/ServerApp.cpp index 52e4331fa..23884aeca 100644 --- a/src/lib/synergy/ServerApp.cpp +++ b/src/lib/synergy/ServerApp.cpp @@ -259,7 +259,7 @@ ServerApp::forceReconnect(const Event&, void*) void ServerApp::handleClientConnected(const Event&, void* vlistener) { - ClientListener* listener = reinterpret_cast(vlistener); + ClientListener* listener = static_cast(vlistener); ClientProxy* client = listener->getNextClient(); if (client != NULL) { m_server->adoptClient(client); diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index f53c39bc7..2ad64a032 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -49,7 +49,7 @@ StreamChunker::sendFile( { s_isChunkingFile = true; - std::fstream file(reinterpret_cast(filename), std::ios::in | std::ios::binary); + std::fstream file(static_cast(filename), std::ios::in | std::ios::binary); if (!file.is_open()) { throw runtime_error("failed to open file"); diff --git a/src/test/integtests/net/NetworkTests.cpp b/src/test/integtests/net/NetworkTests.cpp index dd7fb6b4a..cc346f31b 100644 --- a/src/test/integtests/net/NetworkTests.cpp +++ b/src/test/integtests/net/NetworkTests.cpp @@ -328,7 +328,7 @@ TEST_F(NetworkTests, sendToServer_mockFile) void NetworkTests::sendToClient_mockData_handleClientConnected(const Event&, void* vlistener) { - ClientListener* listener = reinterpret_cast(vlistener); + ClientListener* listener = static_cast(vlistener); Server* server = listener->getServer(); ClientProxy* client = listener->getNextClient(); @@ -336,7 +336,7 @@ NetworkTests::sendToClient_mockData_handleClientConnected(const Event&, void* vl throw runtime_error("client is null"); } - BaseClientProxy* bcp = reinterpret_cast(client); + BaseClientProxy* bcp = static_cast(client); server->adoptClient(bcp); server->setActive(bcp); @@ -346,7 +346,7 @@ NetworkTests::sendToClient_mockData_handleClientConnected(const Event&, void* vl void NetworkTests::sendToClient_mockData_fileRecieveCompleted(const Event& event, void*) { - Client* client = reinterpret_cast(event.getTarget()); + Client* client = static_cast(event.getTarget()); EXPECT_TRUE(client->isReceivedFileSizeValid()); m_events.raiseQuitEvent(); @@ -355,7 +355,7 @@ NetworkTests::sendToClient_mockData_fileRecieveCompleted(const Event& event, voi void NetworkTests::sendToClient_mockFile_handleClientConnected(const Event&, void* vlistener) { - ClientListener* listener = reinterpret_cast(vlistener); + ClientListener* listener = static_cast(vlistener); Server* server = listener->getServer(); ClientProxy* client = listener->getNextClient(); @@ -363,7 +363,7 @@ NetworkTests::sendToClient_mockFile_handleClientConnected(const Event&, void* vl throw runtime_error("client is null"); } - BaseClientProxy* bcp = reinterpret_cast(client); + BaseClientProxy* bcp = static_cast(client); server->adoptClient(bcp); server->setActive(bcp); @@ -373,7 +373,7 @@ NetworkTests::sendToClient_mockFile_handleClientConnected(const Event&, void* vl void NetworkTests::sendToClient_mockFile_fileRecieveCompleted(const Event& event, void*) { - Client* client = reinterpret_cast(event.getTarget()); + Client* client = static_cast(event.getTarget()); EXPECT_TRUE(client->isReceivedFileSizeValid()); m_events.raiseQuitEvent(); @@ -382,14 +382,14 @@ NetworkTests::sendToClient_mockFile_fileRecieveCompleted(const Event& event, voi void NetworkTests::sendToServer_mockData_handleClientConnected(const Event&, void* vclient) { - Client* client = reinterpret_cast(vclient); + Client* client = static_cast(vclient); sendMockData(client); } void NetworkTests::sendToServer_mockData_fileRecieveCompleted(const Event& event, void*) { - Server* server = reinterpret_cast(event.getTarget()); + Server* server = static_cast(event.getTarget()); EXPECT_TRUE(server->isReceivedFileSizeValid()); m_events.raiseQuitEvent(); @@ -398,14 +398,14 @@ NetworkTests::sendToServer_mockData_fileRecieveCompleted(const Event& event, voi void NetworkTests::sendToServer_mockFile_handleClientConnected(const Event&, void* vclient) { - Client* client = reinterpret_cast(vclient); + Client* client = static_cast(vclient); client->sendFileToServer(kMockFilename); } void NetworkTests::sendToServer_mockFile_fileRecieveCompleted(const Event& event, void*) { - Server* server = reinterpret_cast(event.getTarget()); + Server* server = static_cast(event.getTarget()); EXPECT_TRUE(server->isReceivedFileSizeValid()); m_events.raiseQuitEvent(); @@ -491,7 +491,7 @@ createFile(fstream& file, const char* filename, size_t size) throw runtime_error("file not open"); } - file.write(reinterpret_cast(buffer), size); + file.write(static_cast(buffer), size); file.close(); delete[] buffer; From 9ed9bde4e724795caf8c26be4507435cd4c76c7e Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 13:08:59 +0100 Subject: [PATCH 064/241] Restore use of reinterpret_cast for sockaddr_in --- src/lib/arch/unix/ArchNetworkBSD.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/arch/unix/ArchNetworkBSD.cpp b/src/lib/arch/unix/ArchNetworkBSD.cpp index f324a7f11..cf061bc9e 100644 --- a/src/lib/arch/unix/ArchNetworkBSD.cpp +++ b/src/lib/arch/unix/ArchNetworkBSD.cpp @@ -646,7 +646,7 @@ ArchNetworkBSD::newAnyAddr(EAddressFamily family) switch (family) { case kINET: { struct sockaddr_in* ipAddr = - static_cast(&addr->m_addr); + reinterpret_cast(&addr->m_addr); ipAddr->sin_family = AF_INET; ipAddr->sin_port = 0; ipAddr->sin_addr.s_addr = INADDR_ANY; @@ -762,7 +762,7 @@ ArchNetworkBSD::addrToString(ArchNetAddress addr) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - static_cast(&addr->m_addr); + reinterpret_cast(&addr->m_addr); ARCH->lockMutex(m_mutex); std::string s = inet_ntoa(ipAddr->sin_addr); ARCH->unlockMutex(m_mutex); @@ -797,7 +797,7 @@ ArchNetworkBSD::setAddrPort(ArchNetAddress addr, int port) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - static_cast(&addr->m_addr); + reinterpret_cast(&addr->m_addr); ipAddr->sin_port = htons(port); break; } @@ -816,7 +816,7 @@ ArchNetworkBSD::getAddrPort(ArchNetAddress addr) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - static_cast(&addr->m_addr); + reinterpret_cast(&addr->m_addr); return ntohs(ipAddr->sin_port); } @@ -834,7 +834,7 @@ ArchNetworkBSD::isAnyAddr(ArchNetAddress addr) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - static_cast(&addr->m_addr); + reinterpret_cast(&addr->m_addr); return (ipAddr->sin_addr.s_addr == INADDR_ANY && addr->m_len == (socklen_t)sizeof(struct sockaddr_in)); } From f17461465566566698cd86e00f8c6596e16a1d51 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 13:14:07 +0100 Subject: [PATCH 065/241] Remove bizarre cast of sockaddr to char* --- src/lib/arch/unix/ArchNetworkBSD.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/arch/unix/ArchNetworkBSD.cpp b/src/lib/arch/unix/ArchNetworkBSD.cpp index cf061bc9e..8913313ec 100644 --- a/src/lib/arch/unix/ArchNetworkBSD.cpp +++ b/src/lib/arch/unix/ArchNetworkBSD.cpp @@ -737,8 +737,7 @@ ArchNetworkBSD::addrToName(ArchNetAddress addr) // mutexed name lookup (ugh) ARCH->lockMutex(m_mutex); - struct hostent* info = gethostbyaddr( - static_cast(&addr->m_addr), + struct hostent* info = gethostbyaddr(&addr->m_addr, addr->m_len, addr->m_addr.sa_family); if (info == NULL) { ARCH->unlockMutex(m_mutex); From 0371002497026383235f85a076f516b484f612c2 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 13:22:15 +0100 Subject: [PATCH 066/241] Restore use of reinterpret_cast in unicode routines --- src/lib/base/Unicode.cpp | 50 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/lib/base/Unicode.cpp b/src/lib/base/Unicode.cpp index fd4ad6941..ad7a266db 100644 --- a/src/lib/base/Unicode.cpp +++ b/src/lib/base/Unicode.cpp @@ -101,7 +101,7 @@ bool Unicode::isUTF8(const String& src) { // convert and test each character - const UInt8* data = static_cast(src.c_str()); + const UInt8* data = reinterpret_cast(src.c_str()); for (UInt32 n = (UInt32)src.size(); n > 0; ) { if (fromUTF8(data, n) == s_invalid) { return false; @@ -122,7 +122,7 @@ Unicode::UTF8ToUCS2(const String& src, bool* errors) dst.reserve(2 * n); // convert each character - const UInt8* data = static_cast(src.c_str()); + const UInt8* data = reinterpret_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); if (c == s_invalid) { @@ -133,7 +133,7 @@ Unicode::UTF8ToUCS2(const String& src, bool* errors) c = s_replacement; } UInt16 ucs2 = static_cast(c); - dst.append(static_cast(&ucs2), 2); + dst.append(reinterpret_cast(&ucs2), 2); } return dst; @@ -151,13 +151,13 @@ Unicode::UTF8ToUCS4(const String& src, bool* errors) dst.reserve(4 * n); // convert each character - const UInt8* data = static_cast(src.c_str()); + const UInt8* data = reinterpret_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); if (c == s_invalid) { c = s_replacement; } - dst.append(static_cast(&c), 4); + dst.append(reinterpret_cast(&c), 4); } return dst; @@ -175,7 +175,7 @@ Unicode::UTF8ToUTF16(const String& src, bool* errors) dst.reserve(2 * n); // convert each character - const UInt8* data = static_cast(src.c_str()); + const UInt8* data = reinterpret_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); if (c == s_invalid) { @@ -187,14 +187,14 @@ Unicode::UTF8ToUTF16(const String& src, bool* errors) } if (c < 0x00010000) { UInt16 ucs2 = static_cast(c); - dst.append(static_cast(&ucs2), 2); + dst.append(reinterpret_cast(&ucs2), 2); } else { c -= 0x00010000; UInt16 utf16h = static_cast((c >> 10) + 0xd800); UInt16 utf16l = static_cast((c & 0x03ff) + 0xdc00); - dst.append(static_cast(&utf16h), 2); - dst.append(static_cast(&utf16l), 2); + dst.append(reinterpret_cast(&utf16h), 2); + dst.append(reinterpret_cast(&utf16l), 2); } } @@ -213,7 +213,7 @@ Unicode::UTF8ToUTF32(const String& src, bool* errors) dst.reserve(4 * n); // convert each character - const UInt8* data = static_cast(src.c_str()); + const UInt8* data = reinterpret_cast(src.c_str()); while (n > 0) { UInt32 c = fromUTF8(data, n); if (c == s_invalid) { @@ -223,7 +223,7 @@ Unicode::UTF8ToUTF32(const String& src, bool* errors) setError(errors); c = s_replacement; } - dst.append(static_cast(&c), 4); + dst.append(reinterpret_cast(&c), 4); } return dst; @@ -260,7 +260,7 @@ Unicode::UCS2ToUTF8(const String& src, bool* errors) // convert UInt32 n = (UInt32)src.size() >> 1; - return doUCS2ToUTF8(static_cast(src.data()), n, errors); + return doUCS2ToUTF8(reinterpret_cast(src.data()), n, errors); } String @@ -271,7 +271,7 @@ Unicode::UCS4ToUTF8(const String& src, bool* errors) // convert UInt32 n = (UInt32)src.size() >> 2; - return doUCS4ToUTF8(static_cast(src.data()), n, errors); + return doUCS4ToUTF8(reinterpret_cast(src.data()), n, errors); } String @@ -282,7 +282,7 @@ Unicode::UTF16ToUTF8(const String& src, bool* errors) // convert UInt32 n = (UInt32)src.size() >> 1; - return doUTF16ToUTF8(static_cast(src.data()), n, errors); + return doUTF16ToUTF8(reinterpret_cast(src.data()), n, errors); } String @@ -293,7 +293,7 @@ Unicode::UTF32ToUTF8(const String& src, bool* errors) // convert UInt32 n = (UInt32)src.size() >> 2; - return doUTF32ToUTF8(static_cast(src.data()), n, errors); + return doUTF32ToUTF8(reinterpret_cast(src.data()), n, errors); } String @@ -361,16 +361,16 @@ Unicode::wideCharToUTF8(const wchar_t* src, UInt32 size, bool* errors) // the String's nul character). switch (ARCH->getWideCharEncoding()) { case IArchString::kUCS2: - return doUCS2ToUTF8(static_cast(src), size, errors); + return doUCS2ToUTF8(reinterpret_cast(src), size, errors); case IArchString::kUCS4: - return doUCS4ToUTF8(static_cast(src), size, errors); + return doUCS4ToUTF8(reinterpret_cast(src), size, errors); case IArchString::kUTF16: - return doUTF16ToUTF8(static_cast(src), size, errors); + return doUTF16ToUTF8(reinterpret_cast(src), size, errors); case IArchString::kUTF32: - return doUTF32ToUTF8(static_cast(src), size, errors); + return doUTF32ToUTF8(reinterpret_cast(src), size, errors); default: assert(0 && "unknown wide character encoding"); @@ -741,25 +741,25 @@ Unicode::toUTF8(String& dst, UInt32 c, bool* errors) // convert to UTF-8 if (c < 0x00000080) { data[0] = static_cast(c); - dst.append(static_cast(data), 1); + dst.append(reinterpret_cast(data), 1); } else if (c < 0x00000800) { data[0] = static_cast(((c >> 6) & 0x0000001f) + 0xc0); data[1] = static_cast((c & 0x0000003f) + 0x80); - dst.append(static_cast(data), 2); + dst.append(reinterpret_cast(data), 2); } else if (c < 0x00010000) { data[0] = static_cast(((c >> 12) & 0x0000000f) + 0xe0); data[1] = static_cast(((c >> 6) & 0x0000003f) + 0x80); data[2] = static_cast((c & 0x0000003f) + 0x80); - dst.append(static_cast(data), 3); + dst.append(reinterpret_cast(data), 3); } else if (c < 0x00200000) { data[0] = static_cast(((c >> 18) & 0x00000007) + 0xf0); data[1] = static_cast(((c >> 12) & 0x0000003f) + 0x80); data[2] = static_cast(((c >> 6) & 0x0000003f) + 0x80); data[3] = static_cast((c & 0x0000003f) + 0x80); - dst.append(static_cast(data), 4); + dst.append(reinterpret_cast(data), 4); } else if (c < 0x04000000) { data[0] = static_cast(((c >> 24) & 0x00000003) + 0xf8); @@ -767,7 +767,7 @@ Unicode::toUTF8(String& dst, UInt32 c, bool* errors) data[2] = static_cast(((c >> 12) & 0x0000003f) + 0x80); data[3] = static_cast(((c >> 6) & 0x0000003f) + 0x80); data[4] = static_cast((c & 0x0000003f) + 0x80); - dst.append(static_cast(data), 5); + dst.append(reinterpret_cast(data), 5); } else if (c < 0x80000000) { data[0] = static_cast(((c >> 30) & 0x00000001) + 0xfc); @@ -776,7 +776,7 @@ Unicode::toUTF8(String& dst, UInt32 c, bool* errors) data[3] = static_cast(((c >> 12) & 0x0000003f) + 0x80); data[4] = static_cast(((c >> 6) & 0x0000003f) + 0x80); data[5] = static_cast((c & 0x0000003f) + 0x80); - dst.append(static_cast(data), 6); + dst.append(reinterpret_cast(data), 6); } else { assert(0 && "character out of range"); From 90c3dd6622e25ebe33cfcb3593d2607a50a50b1a Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 13:38:08 +0100 Subject: [PATCH 067/241] Restore evil-enabling reinterpret_cast in SocketMultiplexer --- src/lib/net/SocketMultiplexer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/net/SocketMultiplexer.cpp b/src/lib/net/SocketMultiplexer.cpp index b7f9236f0..bab76b206 100644 --- a/src/lib/net/SocketMultiplexer.cpp +++ b/src/lib/net/SocketMultiplexer.cpp @@ -46,7 +46,8 @@ SocketMultiplexer::SocketMultiplexer() : // this pointer just has to be unique and not NULL. it will // never be dereferenced. it's used to identify cursor nodes // in the jobs list. - m_cursorMark = static_cast(this); + // TODO: Remove this evilness + m_cursorMark = reinterpret_cast(this); // start thread m_thread = new Thread(new TMethodJob( From e81f7ab8c734301e4d3282d5414c0c05aedfb073 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 13:47:41 +0100 Subject: [PATCH 068/241] Replace unsafe casts with memcpy ops --- src/lib/synergy/ClipboardChunk.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/lib/synergy/ClipboardChunk.cpp b/src/lib/synergy/ClipboardChunk.cpp index 781098d9d..25c0e2d86 100644 --- a/src/lib/synergy/ClipboardChunk.cpp +++ b/src/lib/synergy/ClipboardChunk.cpp @@ -21,6 +21,7 @@ #include "synergy/protocol_types.h" #include "io/IStream.h" #include "base/Log.h" +#include size_t ClipboardChunk::s_expectedSize = 0; @@ -41,8 +42,7 @@ ClipboardChunk::start( char* chunk = start->m_chunk; chunk[0] = id; - UInt32* seq = static_cast(&chunk[1]); - *seq = sequence; + std::memcpy (&chunk[1], &sequence, 4); chunk[5] = kDataStart; memcpy(&chunk[6], size.c_str(), sizeLength); chunk[sizeLength + CLIPBOARD_CHUNK_META_SIZE - 1] = '\0'; @@ -61,8 +61,7 @@ ClipboardChunk::data( char* chunkData = chunk->m_chunk; chunkData[0] = id; - UInt32* seq = static_cast(&chunkData[1]); - *seq = sequence; + std::memcpy (&chunkData[1], &sequence, 4); chunkData[5] = kDataChunk; memcpy(&chunkData[6], data.c_str(), dataSize); chunkData[dataSize + CLIPBOARD_CHUNK_META_SIZE - 1] = '\0'; @@ -77,8 +76,7 @@ ClipboardChunk::end(ClipboardID id, UInt32 sequence) char* chunk = end->m_chunk; chunk[0] = id; - UInt32* seq = static_cast(&chunk[1]); - *seq = sequence; + std::memcpy (&chunk[1], &sequence, 4); chunk[5] = kDataEnd; chunk[CLIPBOARD_CHUNK_META_SIZE - 1] = '\0'; @@ -133,8 +131,8 @@ ClipboardChunk::send(synergy::IStream* stream, void* data) char* chunk = clipboardData->m_chunk; ClipboardID id = chunk[0]; - UInt32* seq = static_cast(&chunk[1]); - UInt32 sequence = *seq; + UInt32 sequence; + std::memcpy (&sequence, &chunk[1], 4); UInt8 mark = chunk[5]; String dataChunk(&chunk[6], clipboardData->m_dataSize); From fb5e2bb1715ff181cf5864dceedde29b5ddc8131 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 15:27:30 +0100 Subject: [PATCH 069/241] Restore safe reinterpret_cast in readUInt32 --- src/lib/synergy/IClipboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/synergy/IClipboard.cpp b/src/lib/synergy/IClipboard.cpp index 42ca0c85a..a8019184d 100644 --- a/src/lib/synergy/IClipboard.cpp +++ b/src/lib/synergy/IClipboard.cpp @@ -151,7 +151,7 @@ IClipboard::copy(IClipboard* dst, const IClipboard* src, Time time) UInt32 IClipboard::readUInt32(const char* buf) { - const unsigned char* ubuf = static_cast(buf); + const unsigned char* ubuf = reinterpret_cast(buf); return (static_cast(ubuf[0]) << 24) | (static_cast(ubuf[1]) << 16) | (static_cast(ubuf[2]) << 8) | From 055370412c0da21b05a4230884e45a10b19105d0 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 15:35:21 +0100 Subject: [PATCH 070/241] Restore safe reinterpret_cast in clipboard converter --- src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp index 94b335932..07f89a3ab 100644 --- a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp +++ b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp @@ -127,7 +127,7 @@ XWindowsClipboardAnyBitmapConverter::fromIClipboard(const String& bmp) const { // fill BMP info header with native-endian data CBMPInfoHeader infoHeader; - const UInt8* rawBMPInfoHeader = static_cast(bmp.data()); + const UInt8* rawBMPInfoHeader = reinterpret_cast(bmp.data()); infoHeader.biSize = fromLEU32(rawBMPInfoHeader + 0); infoHeader.biWidth = fromLES32(rawBMPInfoHeader + 4); infoHeader.biHeight = fromLES32(rawBMPInfoHeader + 8); @@ -186,6 +186,6 @@ XWindowsClipboardAnyBitmapConverter::toIClipboard(const String& image) const toLE(dst, static_cast(0)); // construct image - return String(static_cast(infoHeader), + return String(reinterpret_cast(infoHeader), sizeof(infoHeader)) + rawBMP; } From 50807bfcb67417e9e85ae57e23e72e6f24cebb7a Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 15:48:07 +0100 Subject: [PATCH 071/241] Restore safe reinterpret_casts in XWindowsScreen --- src/lib/platform/XWindowsScreen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index 6c3e1357a..ce20109c7 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -1169,7 +1169,7 @@ XWindowsScreen::getKeyState() const Bool XWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg) { - KeyEventFilter* filter = static_cast(arg); + KeyEventFilter* filter = reinterpret_cast(arg); return (xevent->type == filter->m_event && xevent->xkey.window == filter->m_window && xevent->xkey.time == filter->m_time && @@ -1408,7 +1408,7 @@ XWindowsScreen::handleSystemEvent(const Event& event, void*) default: #if HAVE_XKB_EXTENSION if (m_xkb && xevent->type == m_xkbEventBase) { - XkbEvent* xkbEvent = static_cast(xevent); + XkbEvent* xkbEvent = reinterpret_cast(xevent); switch (xkbEvent->any.xkb_type) { case XkbMapNotify: refreshKeyboard(xevent); From 5272c9dde4e1b5b178e56a469a8f4e38c7d3bd5f Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 15:51:42 +0100 Subject: [PATCH 072/241] Restore safe reinterpret_casts in XWindowsUtil --- src/lib/platform/XWindowsUtil.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/platform/XWindowsUtil.cpp b/src/lib/platform/XWindowsUtil.cpp index 304bca131..10b35096a 100644 --- a/src/lib/platform/XWindowsUtil.cpp +++ b/src/lib/platform/XWindowsUtil.cpp @@ -1665,35 +1665,35 @@ XWindowsUtil::convertAtomProperty(String& data) // 64-bit numbers we have to ensure the last number is a full 64 bits. if (sizeof(Atom) != 4 && ((data.size() / 4) & 1) != 0) { UInt32 zero = 0; - data.append(static_cast(&zero), sizeof(zero)); + data.append(reinterpret_cast(&zero), sizeof(zero)); } } void XWindowsUtil::appendAtomData(String& data, Atom atom) { - data.append(static_cast(&atom), sizeof(Atom)); + data.append(reinterpret_cast(&atom), sizeof(Atom)); } void XWindowsUtil::replaceAtomData(String& data, UInt32 index, Atom atom) { data.replace(index * sizeof(Atom), sizeof(Atom), - static_cast(&atom), + reinterpret_cast(&atom), sizeof(Atom)); } void XWindowsUtil::appendTimeData(String& data, Time time) { - data.append(static_cast(&time), sizeof(Time)); + data.append(reinterpret_cast(&time), sizeof(Time)); } Bool XWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg) { PropertyNotifyPredicateInfo* filter = - static_cast(arg); + reinterpret_cast(arg); return (xevent->type == PropertyNotify && xevent->xproperty.window == filter->m_window && xevent->xproperty.atom == filter->m_property && From 5b8fb69124f281c778a5cbdfa5d11f4f90c6b8b1 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 16:18:05 +0100 Subject: [PATCH 073/241] Partially de-reinterpret_cast XWindowsClipboard --- src/lib/platform/XWindowsClipboard.cpp | 75 ++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/src/lib/platform/XWindowsClipboard.cpp b/src/lib/platform/XWindowsClipboard.cpp index bba70becd..f30cc2c96 100644 --- a/src/lib/platform/XWindowsClipboard.cpp +++ b/src/lib/platform/XWindowsClipboard.cpp @@ -31,6 +31,7 @@ #include "common/stdvector.h" #include +#include #include // @@ -516,7 +517,7 @@ XWindowsClipboard::icccmFillCache() } XWindowsUtil::convertAtomProperty(data); - const Atom* targets = static_cast(data.data()); + const Atom* targets = reinterpret_cast(data.data()); // TODO: Safe? const UInt32 numTargets = data.size() / sizeof(Atom); LOG((CLOG_DEBUG " available targets: %s", XWindowsUtil::atomsToString(m_display, targets, numTargets).c_str())); @@ -594,7 +595,7 @@ XWindowsClipboard::icccmGetTime() const String data; if (icccmGetSelection(m_atomTimestamp, &actualTarget, &data) && actualTarget == m_atomInteger) { - Time time = *static_cast(data.data()); + Time time = *reinterpret_cast(data.data()); LOG((CLOG_DEBUG1 "got ICCCM time %d", time)); return time; } @@ -671,11 +672,11 @@ XWindowsClipboard::motifOwnsClipboard() const } // check the owner window against the current clipboard owner - const MotifClipHeader* header = - static_cast(data.data()); - if (data.size() >= sizeof(MotifClipHeader) && - header->m_id == kMotifClipHeader) { - if (static_cast(header->m_selectionOwner) == owner) { + if (data.size() >= sizeof(MotifClipHeader)) { + MotifClipHeader header; + std::memcpy (&header, data.data(), sizeof(header)); + if ((header.m_id == kMotifClipHeader) && + (static_cast(header.m_selectionOwner) == owner)) { return true; } } @@ -699,18 +700,18 @@ XWindowsClipboard::motifFillCache() return; } - // check that the header is okay - const MotifClipHeader* header = - static_cast(data.data()); - if (data.size() < sizeof(MotifClipHeader) || - header->m_id != kMotifClipHeader || - header->m_numItems < 1) { + MotifClipHeader header; + if (data.size() < sizeof(header)) { // check that the header is okay + return; + } + std::memcpy (&header, data.data(), sizeof(header)); + if (header.m_id != kMotifClipHeader || header.m_numItems < 1) { return; } // get the Motif item property from the root window char name[18 + 20]; - sprintf(name, "_MOTIF_CLIP_ITEM_%d", header->m_item); + sprintf(name, "_MOTIF_CLIP_ITEM_%d", header.m_item); Atom atomItem = XInternAtom(m_display, name, False); data = ""; if (!XWindowsUtil::getWindowProperty(m_display, root, @@ -719,18 +720,19 @@ XWindowsClipboard::motifFillCache() return; } - // check that the item is okay - const MotifClipItem* item = - static_cast(data.data()); - if (data.size() < sizeof(MotifClipItem) || - item->m_id != kMotifClipItem || - item->m_numFormats - item->m_numDeletedFormats < 1) { + MotifClipItem item; + if (data.size() < sizeof(item)) { // check that the item is okay + return; + } + std::memcpy (&item, data.data(), sizeof(item)); + if (item.m_id != kMotifClipItem || + item.m_numFormats - item.m_numDeletedFormats < 1) { return; } // format list is after static item structure elements - const SInt32 numFormats = item->m_numFormats - item->m_numDeletedFormats; - const SInt32* formats = static_cast(item->m_size + + const SInt32 numFormats = item.m_numFormats - item.m_numDeletedFormats; + const SInt32* formats = reinterpret_cast(item.m_size + static_cast(data.data())); // get the available formats @@ -748,18 +750,20 @@ XWindowsClipboard::motifFillCache() } // check that the format is okay - const MotifClipFormat* motifFormat = - static_cast(data.data()); - if (data.size() < sizeof(MotifClipFormat) || - motifFormat->m_id != kMotifClipFormat || - motifFormat->m_length < 0 || - motifFormat->m_type == None || - motifFormat->m_deleted != 0) { + MotifClipFormat motifFormat; + if (data.size() < sizeof(motifFormat)) { + continue; + } + std::memcpy (&motifFormat, data.data(), sizeof(motifFormat)); + if (motifFormat.m_id != kMotifClipFormat || + motifFormat.m_length < 0 || + motifFormat.m_type == None || + motifFormat.m_deleted != 0) { continue; } // save it - motifFormats.insert(std::make_pair(motifFormat->m_type, data)); + motifFormats.insert(std::make_pair(motifFormat.m_type, data)); } //const UInt32 numMotifFormats = motifFormats.size(); @@ -782,15 +786,14 @@ XWindowsClipboard::motifFillCache() } // get format - const MotifClipFormat* motifFormat = - static_cast( - index2->second.data()); - const Atom target = motifFormat->m_type; + MotifClipFormat motifFormat; + std::memcpy (&motifFormat, index2->second.data(), sizeof(motifFormat)); + const Atom target = motifFormat.m_type; // get the data (finally) Atom actualTarget; String targetData; - if (!motifGetSelection(motifFormat, &actualTarget, &targetData)) { + if (!motifGetSelection(&motifFormat, &actualTarget, &targetData)) { LOG((CLOG_DEBUG1 " no data for target %s", XWindowsUtil::atomToString(m_display, target).c_str())); continue; } @@ -855,7 +858,7 @@ XWindowsClipboard::insertMultipleReply(Window requestor, // data is a list of atom pairs: target, property XWindowsUtil::convertAtomProperty(data); - const Atom* targets = static_cast(data.data()); + const Atom* targets = reinterpret_cast(data.data()); const UInt32 numTargets = data.size() / sizeof(Atom); // add replies for each target From 16977788d3406cbbef7823f07a81b2cc15d18f96 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 16:32:30 +0100 Subject: [PATCH 074/241] Restore safe reinterpret_casts in XWindowsClipboardBMPConverter --- src/lib/platform/XWindowsClipboardBMPConverter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/platform/XWindowsClipboardBMPConverter.cpp b/src/lib/platform/XWindowsClipboardBMPConverter.cpp index e28e81ded..e1f35ff14 100644 --- a/src/lib/platform/XWindowsClipboardBMPConverter.cpp +++ b/src/lib/platform/XWindowsClipboardBMPConverter.cpp @@ -113,7 +113,7 @@ XWindowsClipboardBMPConverter::fromIClipboard(const String& bmp) const toLE(dst, static_cast(0)); toLE(dst, static_cast(0)); toLE(dst, static_cast(14 + 40)); - return String(static_cast(header), 14) + bmp; + return String(reinterpret_cast(header), 14) + bmp; } String @@ -125,7 +125,7 @@ XWindowsClipboardBMPConverter::toIClipboard(const String& bmp) const } // check BMP file header - const UInt8* rawBMPHeader = static_cast(bmp.data()); + const UInt8* rawBMPHeader = reinterpret_cast(bmp.data()); if (rawBMPHeader[0] != 'B' || rawBMPHeader[1] != 'M') { return String(); } From a13dc92f2e42b40ba3b23baded1da3426000f6f5 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 16:36:26 +0100 Subject: [PATCH 075/241] Restore safe reinterpret_casts in SecureSocket --- src/lib/net/SecureSocket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp index cd0068a5f..7076c764e 100644 --- a/src/lib/net/SecureSocket.cpp +++ b/src/lib/net/SecureSocket.cpp @@ -683,7 +683,7 @@ SecureSocket::verifyCertFingerprint() } // format fingerprint into hexdecimal format with colon separator - String fingerprint(static_cast(tempFingerprint), tempFingerprintLen); + String fingerprint(reinterpret_cast(tempFingerprint), tempFingerprintLen); formatFingerprint(fingerprint); LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); From 0568271506797f1d2b8b129a32c7001844c47608 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 16:46:48 +0100 Subject: [PATCH 076/241] Partially remove reinterpret_casts in network tests --- src/test/integtests/net/NetworkTests.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/integtests/net/NetworkTests.cpp b/src/test/integtests/net/NetworkTests.cpp index cc346f31b..79ef7c99c 100644 --- a/src/test/integtests/net/NetworkTests.cpp +++ b/src/test/integtests/net/NetworkTests.cpp @@ -27,6 +27,7 @@ #include "test/global/TestEventQueue.h" #include "server/Server.h" #include "server/ClientListener.h" +#include "server/ClientProxy.h" #include "client/Client.h" #include "synergy/FileChunk.h" #include "synergy/StreamChunker.h" @@ -336,7 +337,7 @@ NetworkTests::sendToClient_mockData_handleClientConnected(const Event&, void* vl throw runtime_error("client is null"); } - BaseClientProxy* bcp = static_cast(client); + BaseClientProxy* bcp = client; server->adoptClient(bcp); server->setActive(bcp); @@ -363,7 +364,7 @@ NetworkTests::sendToClient_mockFile_handleClientConnected(const Event&, void* vl throw runtime_error("client is null"); } - BaseClientProxy* bcp = static_cast(client); + BaseClientProxy* bcp = client; server->adoptClient(bcp); server->setActive(bcp); @@ -491,7 +492,7 @@ createFile(fstream& file, const char* filename, size_t size) throw runtime_error("file not open"); } - file.write(static_cast(buffer), size); + file.write(reinterpret_cast(buffer), size); file.close(); delete[] buffer; From f1cd215f28b01f263189c831a0fbce9ee2578fb2 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 9 Sep 2016 16:54:06 +0100 Subject: [PATCH 077/241] Restore safe reinterpret_cast in Synergy GUI --- src/gui/src/SynergyLocale.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/src/SynergyLocale.cpp b/src/gui/src/SynergyLocale.cpp index 13c8d2156..fa980ed43 100644 --- a/src/gui/src/SynergyLocale.cpp +++ b/src/gui/src/SynergyLocale.cpp @@ -29,7 +29,7 @@ SynergyLocale::SynergyLocale() void SynergyLocale::loadLanguages() { QResource resource(":/res/lang/Languages.xml"); - QByteArray bytes(static_cast(resource.data()), resource.size()); + QByteArray bytes(reinterpret_cast(resource.data()), resource.size()); QXmlStreamReader xml(bytes); while (!xml.atEnd()) From 5a03e37d154560951b86bae76d1753bc8c4820ea Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 12 Sep 2016 13:32:50 +0100 Subject: [PATCH 078/241] Restore safe reinterpret_casts in misc Windows code --- src/lib/arch/win32/ArchMiscWindows.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/arch/win32/ArchMiscWindows.cpp b/src/lib/arch/win32/ArchMiscWindows.cpp index 53b71286e..d70861d26 100644 --- a/src/lib/arch/win32/ArchMiscWindows.cpp +++ b/src/lib/arch/win32/ArchMiscWindows.cpp @@ -234,7 +234,7 @@ ArchMiscWindows::setValue(HKEY key, return; } RegSetValueEx(key, name, 0, REG_SZ, - static_cast(value.c_str()), + reinterpret_cast(value.c_str()), (DWORD)value.size() + 1); } @@ -247,7 +247,7 @@ ArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value) return; } RegSetValueEx(key, name, 0, REG_DWORD, - static_cast(&value), + reinterpret_cast(&value), sizeof(DWORD)); } @@ -262,7 +262,7 @@ ArchMiscWindows::setValueBinary(HKEY key, return; } RegSetValueEx(key, name, 0, REG_BINARY, - static_cast(value.data()), + reinterpret_cast(value.data()), (DWORD)value.size()); } @@ -287,7 +287,7 @@ ArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR* name, DWORD type) // read it result = RegQueryValueEx(key, name, 0, &actualType, - static_cast(buffer), &size); + reinterpret_cast(buffer), &size); if (result != ERROR_SUCCESS || actualType != type) { delete[] buffer; return std::string(); @@ -322,7 +322,7 @@ ArchMiscWindows::readValueInt(HKEY key, const TCHAR* name) DWORD value; DWORD size = sizeof(value); LONG result = RegQueryValueEx(key, name, 0, &type, - static_cast(&value), &size); + reinterpret_cast(&value), &size); if (result != ERROR_SUCCESS || type != REG_DWORD) { return 0; } @@ -374,7 +374,7 @@ ArchMiscWindows::setThreadExecutionState(DWORD busyModes) if (s_stes == NULL) { HINSTANCE kernel = LoadLibrary("kernel32.dll"); if (kernel != NULL) { - s_stes = static_cast(GetProcAddress(kernel, + s_stes = reinterpret_cast(GetProcAddress(kernel, "SetThreadExecutionState")); } if (s_stes == NULL) { @@ -414,7 +414,7 @@ ArchMiscWindows::wakeupDisplay() if (s_stes == NULL) { HINSTANCE kernel = LoadLibrary("kernel32.dll"); if (kernel != NULL) { - s_stes = static_cast(GetProcAddress(kernel, + s_stes = reinterpret_cast(GetProcAddress(kernel, "SetThreadExecutionState")); } if (s_stes == NULL) { From d77b5f1176fb101fb15811521046cc752256f07f Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 12 Sep 2016 16:03:24 +0100 Subject: [PATCH 079/241] Restore safe reinterpret_casts in Windows TaskBar --- src/lib/arch/win32/ArchNetworkWinsock.cpp | 12 ++++++------ src/lib/arch/win32/ArchTaskBarWindows.cpp | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib/arch/win32/ArchNetworkWinsock.cpp b/src/lib/arch/win32/ArchNetworkWinsock.cpp index 265924d58..c6f0a1792 100644 --- a/src/lib/arch/win32/ArchNetworkWinsock.cpp +++ b/src/lib/arch/win32/ArchNetworkWinsock.cpp @@ -754,7 +754,7 @@ ArchNetworkWinsock::addrToName(ArchNetAddress addr) // name lookup struct hostent* info = gethostbyaddr_winsock( - static_cast(&addr->m_addr), + reinterpret_cast(&addr->m_addr), addr->m_len, addr->m_addr.sa_family); if (info == NULL) { throwNameError(getsockerror_winsock()); @@ -772,7 +772,7 @@ ArchNetworkWinsock::addrToString(ArchNetAddress addr) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - static_cast(&addr->m_addr); + reinterpret_cast(&addr->m_addr); return inet_ntoa_winsock(ipAddr->sin_addr); } @@ -804,8 +804,8 @@ ArchNetworkWinsock::setAddrPort(ArchNetAddress addr, int port) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - static_cast(&addr->m_addr); - ipAddr->sin_port = htons_winsock(static_cast(port)); + reinterpret_cast(&addr->m_addr); + ipAddr->sin_port = htons_winsock(reinterpret_cast(port)); break; } @@ -823,7 +823,7 @@ ArchNetworkWinsock::getAddrPort(ArchNetAddress addr) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - static_cast(&addr->m_addr); + reinterpret_cast(&addr->m_addr); return ntohs_winsock(ipAddr->sin_port); } @@ -841,7 +841,7 @@ ArchNetworkWinsock::isAnyAddr(ArchNetAddress addr) switch (getAddrFamily(addr)) { case kINET: { struct sockaddr_in* ipAddr = - static_cast(&addr->m_addr); + reinterpret_cast(&addr->m_addr); return (addr->m_len == sizeof(struct sockaddr_in) && ipAddr->sin_addr.s_addr == INADDR_ANY); } diff --git a/src/lib/arch/win32/ArchTaskBarWindows.cpp b/src/lib/arch/win32/ArchTaskBarWindows.cpp index eac283a86..7de57de23 100644 --- a/src/lib/arch/win32/ArchTaskBarWindows.cpp +++ b/src/lib/arch/win32/ArchTaskBarWindows.cpp @@ -414,17 +414,16 @@ ArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg, ArchTaskBarWindows* self = NULL; if (msg == WM_NCCREATE) { CREATESTRUCT* createInfo; - createInfo = static_cast(lParam); + createInfo = reinterpret_cast(lParam); self = static_cast( createInfo->lpCreateParams); - SetWindowLong(hwnd, 0, static_cast(self)); + SetWindowLongPtr(hwnd, 0, self); } else { // get the extra window data and forward the call - LONG data = GetWindowLong(hwnd, 0); + LONG_PTR data = GetWindowLongPtr(hwnd, 0); if (data != 0) { - self = static_cast( - static_cast(data)); + self = static_cast(reinterpret_cast(data)); } } @@ -444,6 +443,7 @@ ArchTaskBarWindows::threadMainLoop() m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); // register a window class + LPCTSTR className = TEXT("SynergyTaskBar"); WNDCLASSEX classInfo; classInfo.cbSize = sizeof(classInfo); classInfo.style = CS_NOCLOSE; @@ -455,13 +455,13 @@ ArchTaskBarWindows::threadMainLoop() classInfo.hCursor = NULL; classInfo.hbrBackground = NULL; classInfo.lpszMenuName = NULL; - classInfo.lpszClassName = TEXT("SynergyTaskBar"); + classInfo.lpszClassName = className; classInfo.hIconSm = NULL; ATOM windowClass = RegisterClassEx(&classInfo); // create window m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, - static_cast(windowClass), + className, TEXT("Synergy Task Bar"), WS_POPUP, 0, 0, 1, 1, @@ -478,7 +478,7 @@ ArchTaskBarWindows::threadMainLoop() // handle failure if (m_hwnd == NULL) { - UnregisterClass(static_cast(windowClass), instanceWin32()); + UnregisterClass(className, instanceWin32()); return; } @@ -494,7 +494,7 @@ ArchTaskBarWindows::threadMainLoop() // clean up removeAllIcons(); DestroyWindow(m_hwnd); - UnregisterClass(static_cast(windowClass), instanceWin32()); + UnregisterClass(className, instanceWin32()); } void* From 7e386c0bf9f007f74b3c72c7196ee04696223be4 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 12 Sep 2016 16:10:24 +0100 Subject: [PATCH 080/241] Fix cast of port number in Winsock --- src/lib/arch/win32/ArchNetworkWinsock.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/arch/win32/ArchNetworkWinsock.cpp b/src/lib/arch/win32/ArchNetworkWinsock.cpp index c6f0a1792..b1470aa2c 100644 --- a/src/lib/arch/win32/ArchNetworkWinsock.cpp +++ b/src/lib/arch/win32/ArchNetworkWinsock.cpp @@ -805,7 +805,7 @@ ArchNetworkWinsock::setAddrPort(ArchNetAddress addr, int port) case kINET: { struct sockaddr_in* ipAddr = reinterpret_cast(&addr->m_addr); - ipAddr->sin_port = htons_winsock(reinterpret_cast(port)); + ipAddr->sin_port = htons_winsock(port); break; } From 702f095efda04a8dd1cb1d4c3fbaa81aa200b025 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 12 Sep 2016 16:13:35 +0100 Subject: [PATCH 081/241] Fix cast in Windows TaskBar --- src/lib/arch/win32/ArchTaskBarWindows.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/arch/win32/ArchTaskBarWindows.cpp b/src/lib/arch/win32/ArchTaskBarWindows.cpp index 7de57de23..f87a595b0 100644 --- a/src/lib/arch/win32/ArchTaskBarWindows.cpp +++ b/src/lib/arch/win32/ArchTaskBarWindows.cpp @@ -417,7 +417,7 @@ ArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg, createInfo = reinterpret_cast(lParam); self = static_cast( createInfo->lpCreateParams); - SetWindowLongPtr(hwnd, 0, self); + SetWindowLongPtr(hwnd, 0, reinterpret_cast(createInfo->lpCreateParams)); } else { // get the extra window data and forward the call From 2e30dc2c68b2a99a562ca0ec93e1a39bfbcadef2 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 12 Sep 2016 16:21:33 +0100 Subject: [PATCH 082/241] Restore safe reinterpret_cast of Windows thread handle --- src/lib/arch/win32/ArchMultithreadWindows.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/arch/win32/ArchMultithreadWindows.cpp b/src/lib/arch/win32/ArchMultithreadWindows.cpp index 70aafa2df..3c131780b 100644 --- a/src/lib/arch/win32/ArchMultithreadWindows.cpp +++ b/src/lib/arch/win32/ArchMultithreadWindows.cpp @@ -303,7 +303,7 @@ ArchMultithreadWindows::newThread(ThreadFunc func, void* data) // create thread unsigned int id = 0; - thread->m_thread = static_cast(_beginthreadex(NULL, 0, + thread->m_thread = reinterpret_cast(_beginthreadex(NULL, 0, threadFunc, (void*)thread, 0, &id)); thread->m_id = static_cast(id); From f7ad16263468ba9df584831774914ba4902484c2 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 13 Sep 2016 10:32:13 +0100 Subject: [PATCH 083/241] Fix up casts in MSWindowsClipboardBitmapConverter --- src/lib/platform/MSWindowsClipboardBitmapConverter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp b/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp index 9b39ca7e0..d1676bb0d 100644 --- a/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp +++ b/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp @@ -71,7 +71,7 @@ String MSWindowsClipboardBitmapConverter::toIClipboard(HANDLE data) const { // get datator - const char* src = (const char*)GlobalLock(data); + LPVOID src = GlobalLock(data); if (src == NULL) { return String(); } @@ -85,7 +85,7 @@ MSWindowsClipboardBitmapConverter::toIClipboard(HANDLE data) const bitmap->bmiHeader.biBitCount == 32) && bitmap->bmiHeader.biCompression == BI_RGB) { // already in canonical form - String image(src, srcSize); + String image(static_cast(src), srcSize); GlobalUnlock(data); return image; } From 2a5dc62747b69abdaec93005ecf34733d55275be Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 13 Sep 2016 10:35:53 +0100 Subject: [PATCH 084/241] Restore safe reinterpret_casts in MSWindowsDesks --- src/lib/platform/MSWindowsDesks.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index 555d2e79c..a5cf4e624 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -241,7 +241,7 @@ void MSWindowsDesks::getCursorPos(SInt32& x, SInt32& y) const { POINT pos; - sendMessage(SYNERGY_MSG_CURSOR_POS, static_cast(&pos), 0); + sendMessage(SYNERGY_MSG_CURSOR_POS, reinterpret_cast(&pos), 0); x = pos.x; y = pos.y; } @@ -427,7 +427,7 @@ void MSWindowsDesks::destroyClass(ATOM windowClass) const { if (windowClass != 0) { - UnregisterClass(static_cast(windowClass), + UnregisterClass(reinterpret_cast(windowClass), MSWindowsScreen::getWindowInstance()); } } @@ -437,7 +437,7 @@ MSWindowsDesks::createWindow(ATOM windowClass, const char* name) const { HWND window = CreateWindowEx(WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - static_cast(windowClass), + reinterpret_cast(windowClass), name, WS_POPUP, 0, 0, 1, 1, @@ -757,7 +757,7 @@ MSWindowsDesks::deskThread(void* vdesk) break; case SYNERGY_MSG_CURSOR_POS: { - POINT* pos = static_cast(msg.wParam); + POINT* pos = reinterpret_cast(msg.wParam); if (!GetCursorPos(pos)) { pos->x = m_xCenter; pos->y = m_yCenter; From 788f6eab9f9c71adbec0b1b08fa831781a2a6231 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 13 Sep 2016 10:37:01 +0100 Subject: [PATCH 085/241] Restore safe reinterpret_casts in MSWindowsScreen --- src/lib/platform/MSWindowsScreen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 68c628cc1..1011cb0c9 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -864,7 +864,7 @@ void MSWindowsScreen::destroyClass(ATOM windowClass) const { if (windowClass != 0) { - UnregisterClass(static_cast(windowClass), s_windowInstance); + UnregisterClass(reinterpret_cast(windowClass), s_windowInstance); } } @@ -874,7 +874,7 @@ MSWindowsScreen::createWindow(ATOM windowClass, const char* name) const HWND window = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - static_cast(windowClass), + reinterpret_cast(windowClass), name, WS_POPUP, 0, 0, 1, 1, @@ -895,7 +895,7 @@ MSWindowsScreen::createDropWindow(ATOM windowClass, const char* name) const WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_ACCEPTFILES, - static_cast(m_class), + reinterpret_cast(m_class), name, WS_POPUP, 0, 0, m_dropWindowSize, m_dropWindowSize, From 19b9be45930e62def36926d2a0fe1bee3ee1d3f7 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 13 Sep 2016 10:39:06 +0100 Subject: [PATCH 086/241] Restore safe reinterpret_casts in MSWindowsScreenSaver --- src/lib/platform/MSWindowsScreenSaver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/platform/MSWindowsScreenSaver.cpp b/src/lib/platform/MSWindowsScreenSaver.cpp index 4289bb01a..f2ab7334f 100644 --- a/src/lib/platform/MSWindowsScreenSaver.cpp +++ b/src/lib/platform/MSWindowsScreenSaver.cpp @@ -162,7 +162,7 @@ MSWindowsScreenSaver::deactivate() if (desktop != NULL) { EnumDesktopWindows(desktop, &MSWindowsScreenSaver::killScreenSaverFunc, - static_cast(&killed)); + reinterpret_cast(&killed)); CloseDesktop(desktop); } @@ -205,7 +205,7 @@ MSWindowsScreenSaver::killScreenSaverFunc(HWND hwnd, LPARAM arg) HINSTANCE instance = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE); if (instance != MSWindowsScreen::getWindowInstance()) { PostMessage(hwnd, WM_CLOSE, 0, 0); - *static_cast(arg) = true; + *reinterpret_cast(arg) = true; } } return TRUE; From 23cf284a66617bfe551e02355fa9382ddde734f5 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 13 Sep 2016 10:53:27 +0100 Subject: [PATCH 087/241] Restore safe reinterpret_casts in MSWindowsClientTaskBarReceiver --- src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp | 4 ++-- src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp | 8 ++++---- src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp b/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp index 529a54dca..fc3ed94ee 100644 --- a/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp +++ b/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp @@ -287,7 +287,7 @@ MSWindowsClientTaskBarReceiver::createWindow() MAKEINTRESOURCE(IDD_TASKBAR_STATUS), NULL, (DLGPROC)&MSWindowsClientTaskBarReceiver::staticDlgProc, - static_cast( + reinterpret_cast( static_cast(this))); // window should appear on top of everything, including (especially) @@ -338,7 +338,7 @@ MSWindowsClientTaskBarReceiver::staticDlgProc(HWND hwnd, MSWindowsClientTaskBarReceiver* self = NULL; if (msg == WM_INITDIALOG) { self = static_cast( - static_cast(lParam)); + reinterpret_cast(lParam)); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) lParam); } else { diff --git a/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp b/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp index b42f28b32..ae7b0ee49 100644 --- a/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp +++ b/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp @@ -304,7 +304,7 @@ MSWindowsPortableTaskBarReceiver::createWindow() MAKEINTRESOURCE(IDD_TASKBAR_STATUS), NULL, (DLGPROC)&MSWindowsPortableTaskBarReceiver::staticDlgProc, - static_cast( + reinterpret_cast( static_cast(this))); // window should appear on top of everything, including (especially) @@ -355,15 +355,15 @@ MSWindowsPortableTaskBarReceiver::staticDlgProc(HWND hwnd, MSWindowsPortableTaskBarReceiver* self = NULL; if (msg == WM_INITDIALOG) { self = static_cast( - static_cast(lParam)); + reinterpret_cast(lParam)); SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam); } else { // get the extra window data and forward the call - LONG data = (LONG)GetWindowLongPtr(hwnd, GWLP_USERDATA); + LONG_PTR data = GetWindowLongPtr(hwnd, GWLP_USERDATA); if (data != 0) { self = static_cast( - static_cast(data)); + reinterpret_cast(data)); } } diff --git a/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp b/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp index 83fc539c8..b2d304a15 100644 --- a/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp +++ b/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp @@ -318,7 +318,7 @@ MSWindowsServerTaskBarReceiver::createWindow() MAKEINTRESOURCE(IDD_TASKBAR_STATUS), NULL, (DLGPROC)&MSWindowsServerTaskBarReceiver::staticDlgProc, - static_cast( + reinterpret_cast( static_cast(this))); // window should appear on top of everything, including (especially) @@ -369,15 +369,15 @@ MSWindowsServerTaskBarReceiver::staticDlgProc(HWND hwnd, MSWindowsServerTaskBarReceiver* self = NULL; if (msg == WM_INITDIALOG) { self = static_cast( - static_cast(lParam)); + reinterpret_cast(lParam)); SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam); } else { // get the extra window data and forward the call - LONG data = (LONG)GetWindowLongPtr(hwnd, GWLP_USERDATA); + LONG_PTR data = GetWindowLongPtr(hwnd, GWLP_USERDATA); if (data != 0) { self = static_cast( - static_cast(data)); + reinterpret_cast(data)); } } From 8072594008e040b9a49668e8024dcf359bfaa43a Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 13 Sep 2016 11:14:40 +0100 Subject: [PATCH 088/241] Restore safe reinterpret_casts in OSXClipboardBMPConverter --- src/lib/platform/OSXClipboardBMPConverter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/platform/OSXClipboardBMPConverter.cpp b/src/lib/platform/OSXClipboardBMPConverter.cpp index 574b917ad..faeac2912 100644 --- a/src/lib/platform/OSXClipboardBMPConverter.cpp +++ b/src/lib/platform/OSXClipboardBMPConverter.cpp @@ -104,7 +104,7 @@ OSXClipboardBMPConverter::fromIClipboard(const String& bmp) const toLE(dst, static_cast(0)); toLE(dst, static_cast(0)); toLE(dst, static_cast(14 + 40)); - return String(static_cast(header), 14) + bmp; + return String(reinterpret_cast(header), 14) + bmp; } String @@ -116,7 +116,7 @@ OSXClipboardBMPConverter::toIClipboard(const String& bmp) const } // check BMP file header - const UInt8* rawBMPHeader = static_cast(bmp.data()); + const UInt8* rawBMPHeader = reinterpret_cast(bmp.data()); if (rawBMPHeader[0] != 'B' || rawBMPHeader[1] != 'M') { return String(); } From 602fd3f64932685be62471e2688a49757bf7b70a Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 13 Sep 2016 11:18:59 +0100 Subject: [PATCH 089/241] Restore safe reinterpret_casts in OSXKeyState --- src/lib/platform/OSXKeyState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 33d81fdf6..2071621bf 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -415,7 +415,7 @@ OSXKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const { ::KeyMap km; GetKeys(km); - const UInt8* m = static_cast(km); + const UInt8* m = reinterpret_cast(km); for (UInt32 i = 0; i < 16; ++i) { for (UInt32 j = 0; j < 8; ++j) { if ((m[i] & (1u << j)) != 0) { From 26c11ec3c9cfee31749b64cf1a3346cc49a1c649 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 13 Sep 2016 11:26:34 +0100 Subject: [PATCH 090/241] Restore horrible reinterpret_casts in OSXUchrKeyResource --- src/lib/platform/OSXUchrKeyResource.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/platform/OSXUchrKeyResource.cpp b/src/lib/platform/OSXUchrKeyResource.cpp index b3f785b84..603ba04a9 100644 --- a/src/lib/platform/OSXUchrKeyResource.cpp +++ b/src/lib/platform/OSXUchrKeyResource.cpp @@ -56,19 +56,19 @@ OSXUchrKeyResource::OSXUchrKeyResource(const void* resource, } // get tables for keyboard type - const UInt8* base = static_cast(m_resource); - m_m = static_cast(base + + const UInt8* const base = reinterpret_cast(m_resource); + m_m = reinterpret_cast(base + th->keyModifiersToTableNumOffset); - m_cti = static_cast(base + + m_cti = reinterpret_cast(base + th->keyToCharTableIndexOffset); - m_sdi = static_cast(base + + m_sdi = reinterpret_cast(base + th->keySequenceDataIndexOffset); if (th->keyStateRecordsIndexOffset != 0) { - m_sri = static_cast(base + + m_sri = reinterpret_cast(base + th->keyStateRecordsIndexOffset); } if (th->keyStateTerminatorsOffset != 0) { - m_st = static_cast(base + + m_st = reinterpret_cast(base + th->keyStateTerminatorsOffset); } @@ -81,7 +81,7 @@ OSXUchrKeyResource::OSXUchrKeyResource(const void* resource, KeyID id = getKey(table, button); if (id == 0x20) { UCKeyOutput c = - static_cast(base + + reinterpret_cast(base + m_cti->keyToCharTableOffsets[table])[button]; if ((c & kUCKeyOutputTestForIndexMask) == kUCKeyOutputStateIndexMask) { @@ -134,8 +134,8 @@ OSXUchrKeyResource::getKey(UInt32 table, UInt32 button) const assert(table < getNumTables()); assert(button < getNumButtons()); - const UInt8* base = static_cast(m_resource); - const UCKeyOutput* cPtr = static_cast(base + + const UInt8* const base = reinterpret_cast(m_resource); + const UCKeyOutput* cPtr = reinterpret_cast(base + m_cti->keyToCharTableOffsets[table]); const UCKeyOutput c = cPtr[button]; @@ -211,12 +211,12 @@ bool OSXUchrKeyResource::getKeyRecord( KeySequence& keys, UInt16 index, UInt16& state) const { - const UInt8* base = static_cast(m_resource); + const UInt8* const base = reinterpret_cast(m_resource); const UCKeyStateRecord* sr = - static_cast(base + + reinterpret_cast(base + m_sri->keyStateRecordOffsets[index]); const UCKeyStateEntryTerminal* kset = - static_cast(sr->stateEntryData); + reinterpret_cast(sr->stateEntryData); UInt16 nextState = 0; bool found = false; From 5b7392d302a8524a6b9228b46b6f1f2597980118 Mon Sep 17 00:00:00 2001 From: Benedikt Morbach Date: Tue, 28 Oct 2014 16:24:57 +0100 Subject: [PATCH 091/241] #4420 Fix check for XRRNotifyEvent use CheckTypeSize instead of CheckSymbolExists From http://www.cmake.org/cmake/help/v3.0/module/CheckSymbolExists.html : If the symbol is a type or enum value it will not be recognized (consider using CheckTypeSize or CheckCSourceCompiles). --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a86fee803..b3e066a88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,7 +205,10 @@ if (UNIX) set(CMAKE_INCLUDE_PATH "${CMAKE_INCLUDE_PATH}:/usr/local/include") set(XKBlib "X11/Xlib.h;X11/XKBlib.h") - check_symbol_exists("XRRNotifyEvent" "${XKBlib};X11/extensions/Xrandr.h" HAVE_X11_EXTENSIONS_XRANDR_H) + set(CMAKE_EXTRA_INCLUDE_FILES "${XKBlib};X11/extensions/Xrandr.h") + check_type_size("XRRNotifyEvent" X11_EXTENSIONS_XRANDR_H) + set(HAVE_X11_EXTENSIONS_XRANDR_H "${X11_EXTENSIONS_XRANDR_H}") + set(CMAKE_EXTRA_INCLUDE_FILES) check_include_files("${XKBlib};X11/extensions/dpms.h" HAVE_X11_EXTENSIONS_DPMS_H) check_include_files("X11/extensions/Xinerama.h" HAVE_X11_EXTENSIONS_XINERAMA_H) From dd88e324d721536a1d03a2897e4b3c84675aa632 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 23 Sep 2016 15:30:22 +0100 Subject: [PATCH 092/241] #4420 Send screen shape change event on Linux (xrandr) --- src/lib/platform/XWindowsScreen.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index b16e2e516..b7b248c90 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -1442,6 +1442,8 @@ XWindowsScreen::handleSystemEvent(const Event& event, void*) XMoveWindow(m_display, m_window, m_x, m_y); XResizeWindow(m_display, m_window, m_w, m_h); } + + sendEvent(m_events->forIScreen().shapeChanged()); } } #endif From 6892664f4a575a57f5e437aef74187b9b3bd8278 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 5 Oct 2016 15:10:40 +0100 Subject: [PATCH 093/241] Fix cast of XRRNotifyEvent --- src/lib/platform/XWindowsScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index 6e0ac4f8e..c70324705 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -1426,7 +1426,7 @@ XWindowsScreen::handleSystemEvent(const Event& event, void*) if (m_xrandr) { if (xevent->type == m_xrandrEventBase + RRScreenChangeNotify || xevent->type == m_xrandrEventBase + RRNotify - && static_cast(xevent)->subtype == RRNotify_CrtcChange) { + && reinterpret_cast(xevent)->subtype == RRNotify_CrtcChange) { LOG((CLOG_INFO "XRRScreenChangeNotifyEvent or RRNotify_CrtcChange received")); // we're required to call back into XLib so XLib can update its internal state From 833c73f1bdb4ade0b89b9ca93da61db26654a548 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 5 Oct 2016 20:53:23 +0100 Subject: [PATCH 094/241] #5640 Update icon to the new hotness --- res/synergy.ico | Bin 287934 -> 24277 bytes src/gui/res/icons/256x256/synergy.ico | Bin 287934 -> 24277 bytes src/gui/res/image/about.png | Bin 4941 -> 5701 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/res/synergy.ico b/res/synergy.ico index fc2e41468ec60a88e0da4194f288a18572f3f36f..9e3d57100883a631db42ac5c9ef323228f84b43f 100644 GIT binary patch literal 24277 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 z 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 literal 287934 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 diff --git a/src/gui/res/icons/256x256/synergy.ico b/src/gui/res/icons/256x256/synergy.ico index fc2e41468ec60a88e0da4194f288a18572f3f36f..9e3d57100883a631db42ac5c9ef323228f84b43f 100644 GIT binary patch literal 24277 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 z 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 literal 287934 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 diff --git a/src/gui/res/image/about.png b/src/gui/res/image/about.png index 4630bd32a5d36f7588a3d8b4bbf9b8706e80adec..66307bb077761806aace467307b7b7ae93e58a95 100644 GIT binary patch literal 5701 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 literal 4941 zcmV-T6SC}yP)Vj`Dzw2pNlXI1A$LKEE=|zRV`mxT3WKH$Yxk=w;2~zSFEya;e4)V5==kUbJ{CWg;A0<%zTk0?w4wR#{(l4>PM|s?vdJ> zs@qIDAT{VtgcrbZ0Gh-A$5_x(g<$35Sj>_v3OUM(h89%1`$q7o-n@GA&X#8?Dr+}ejDSmM*!=-?4xJZdU?DS55&}>Zx&zn16ApqF zp-E3tpd~cmw3aYhjp6Qd1D7_DrLYr|q#vO0bRMMn&i{r12Ln-(c}W6rf_O|{vF1md zQD;r?09QrX3#x|>2)&Z3G40S*QNaAN2x(D691s1g&Z$3fFz*D_$|?ApF0 z_U%3)SkGuQe}SUk2a+htad3VVzK*~V<-wZ@Oc0MnL93y`Y+dj@eM#d#$Kn_>I0g_# zz!6Yb(jbtqdgNtv$k3}^15ixkKoKS=Aq$}k0Vxy=!ALL&g9u3HM!i7C;$SC9up$8Y zqf(`F>z)6lW!S&`13FGpUbZr4rJ}feDgzolYpZ) znI{NwA|8epKYA>i1Nr_uSZhzIxo@;zs<<=4Af{`z>S(>aXp zi^8P9`jRN=^?KY<=onqvP~Y2ospsTd?XUe|;N5qhSh;rX%3~G|f+$=O@dQXo0Y=9I z;BXb*UALz3J7p?syp1kACLMpL?aei->VC5A-W{JKzyuUEE@o2k$Pze?3kw%j_I|hJ zDetSl{qNxuhmSs0-?VbwM+^(ea0D=$;5NURlD)xeSCXPEnNifkz%b#=qExeSY=}uH03z?3~v-0UUzBAP@>e z%vH3qsIvTy&klb0I>l(gAMoef{@p6jyrg{2cHMlKx`_oZEvp5mvs_zLwf3$+6v-+{ zSz1A+)1oQd``M6z3j7Rtno|mB5_y0|U1aYQy#G57ePk7|UlawsejOrCqWl2)ae@dV zkvO9(Dc<6$E+-c)T?{Y(>OUteADW}`G`n+L*M_Wfezv1wNbeIUk-ACSf_`XetX<8Fi!qaR@oMG9Z`}Pe7UViV5&GzDwd;I7LI7~jzABuvR zVH%rOHQB9Z>)`f#w&z=KgNgwEUllbO@0&qeI92<7vj8@yJhwAP-*C$N&YSIVaV;f5 zQE7FpKo;0}e!|gAtQ^ZcCp^~UH!;FnOX`++6i*}0@;NdN`NfNC2RNL5TN@4vviuei z5te0o1Su?D!z^TUdRJkAVc2B|(xN#z2Xq}bkRuI*H=>r49{vb8F8$^5Dp*+Ip6 zm1EpAZIG|=s_r|f3~Wn|{!2Z#O!1&z<-XRmYg?7NPO9qnD)(@zy4W4=`L--ZxL}Mur&VI$va0^a)A~!@Z~i>`L_X(A-Gkrm-h>pD*->Dk z!D^6@iBO;u!EOWydiuJI2IGhV8u13bn?%vHq-WSZDjMxCx7wh{Y=M%30w}Rqz;3nz zS70fQgu|)p^V!7OtLm6~)ePjOxd)U^XvjgP)q{~wWT?W0yHTDwIR#B z-&c8qOzm2)0Ahz?DUi0P0Ch^KvrJY0J{4fq#is()_8gwOSq1hseA%*FC>=9usCITj zjq^BEmwp6Q&JWS+Ifw_(4r3S|ogo>{sUu4;hg z6?LH3nz*DS%1g*I(d2HWQ&Qz=#ieEgkb2g&Oaq*ka&Uv@w7UAzz~asTSd;2pN7}t^ z)y&TpGGwqnqYQ4lVjZdwmGUy~v=EzW+o@?LQ#P-|sg6%fBwR=Wg_DL(zO z&x1gqSK>LK*e>GD_kM!~gF(n<@kA7oQUa1uJMZ9hk5|eq*1Z{!8|F?T#$3vTdkJLfWH62tqilCUKAmsO6 z@1_u!`G>vFLB--)7U!&%t|31J15w1K2*9)=;DiJVaXg}9SYnoZs!g%oN(Z?VJE(xF zE$!OvN@t{dj@_!6+mQ#i$^;%)25w_g@u$`t+zK+>uO8rB%M#PIt#g|GdsZbh`ZDy% zq117b)v~})Rxh5UB?_0Ygfz)z1Cb!uoP|pb2F4JNC&tyliG&hhG%>Jjr335>7~pwf zB6nDe81=`Ohv3L-zk&TP{Tv92mUDWhBphPj<<&Js3%Bj5zU+x2RV6|a!+|C#ASGVp z^sEo@pc_rlX;V6{RpASAc@eJPa%fk8;mQD<=4rgmYzmh~p4ZQ^o{l^{@N_CP7jQr| z`BGqBpWY{=(PR*{guP!9xCqOR>k=q-iE^@5lm|y4U)kiYXgc%h;O}vKYCLN<7emd` zVpy}a7z|b|x;6>4f)5hML95lkstudr!l{!Gjf5vtCJ?aXzv8O8rZ{D^^6^p7pqmJq z1lFJhT_P}6V9qzmJPJq=>U^Y5?q-cSML4&3tF(A=E%Yz+=591rZUAiY46(A8umJlEw6QFik5mYa; z!`N^b_(U>e5?}d}x(TpEf&qvkZW9XnfubpW^}0JBy*3^O0+$e{A%N7-U}R}v6UB7DPi~ zu$7d;+Pm(B!QO5V5;1Ttxn)t~2?N{b$lG+J4PM(Ec#CXmFB6B#)y`B1->tOMm1#Xomc7Y^m9B9yQgQ^3 z777N20-Xl&4g#N$Hs07(=GLw34^3uWmApWxH)%3v>;3VnjRZI;hasb!B?iS6mAAPz z-rw3YTS)_Bc|Hfl1_@%z!8ilUCdcrcl27f?OcK00R{~qBT(2PAsUN8km{}3 z^iaVs40@&_9+_DbiG_ktSn67__}(X8JU_f((Dgkyt4_BovMkkUbVfT_B0(i z)LJIR=*lG&aAlwpO}DMSj$((?Oxx2;6ADk0apKy88MnFqy6~ z(-mr$0-%#HwdW!!pVcWBd#8!(Fvq}YSK5(USw5%?cn3`FUA+ji&mC$}O#*e|nryww zGk=^%J?eEXQ#vVD14k|iQMW6@_ zXcC2!CZ^?RFodZq8mB*{@JO#H@riIk)C5AY>cUl9ZVgHF!f^qk z`4~U(Yfpw_fI&o$RdzDM{-<{y$cICMzb+UG$iKP#QS}-EeR1*Lr@BHqIVx#V-cW%8)4k_Uql;C|g;3?*s z6z3we9dlH7XBH4P95#(;1yFJL)1Z`g#)_* zgA$~u9C``#8Xr<#Q7B;bP;6R=j@LuEr5x%?Y*1co1_Q@JR6{~QR!&wun2IdG*o;tG zWQ95@2K}$TblQwdKYH+yN8i_IH2zt7o10O}U}{IX4Zhih%Lfo?N;GSV2bSUAjL2Z3 zM~262ZEY`a3B*o6dB+aTt)=C|L?m(r;_@OOVj@w22VRgs!cgd;*8(o>gcF_uI2}aC zkz*y1Dx(o_a0(G@trjX68i?+*{MmQjI8syL`q{&~9y_Gh>;1DN#FO99eZ%Wy?TQrl zDnehy`ZrC?v&kWHAtuJcx4A7rp`U;V0l&kb_ga2y%8GO%lK&v!jZ%g9Fm#Bk%VF z1~0$8dDG@s?%2BZbBdxuF!g^6;7NCdWg<4f^?PHP zlKMMfDzzikCp&V5ZY;+?Ho_DRq&;`8t9akOx0)^woY~@91S@MB$OXlv37XYKfTjgt z=-UAkAArvzI=B$wflS1K3i=^&tv@<=`eeWV%D~aOB})!$-FEkhva)h{bvTt#cthc7 zmcZ#q>&PGHv5@$6D6Oiss7%GLOpZ*-Dt#RGOc%Oy0Vh=m#n@Awr;86CIkvc`r)#;0 z`D`kI={mIO^~vdm(yk)T8U|?Ju}`^;&^ve4Gmg zy?Qa3AZUsd40?UgX0v%qU1eiVr_*QRxUd428}=~s<*QTvSAYQkmSi5mNaD5k00000 LNkvXXu0mjfO~zre From 03b8788660af08290f74cc4812ac32f29932010c Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 5 Oct 2016 21:53:22 +0100 Subject: [PATCH 095/241] #5640 About dialog tweaks --- src/gui/res/AboutDialogBase.ui | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gui/res/AboutDialogBase.ui b/src/gui/res/AboutDialogBase.ui index ba18e3091..52d8330fe 100644 --- a/src/gui/res/AboutDialogBase.ui +++ b/src/gui/res/AboutDialogBase.ui @@ -13,7 +13,7 @@ 0 0 450 - 300 + 350 @@ -25,13 +25,13 @@ 450 - 300 + 350 450 - 300 + 350 @@ -51,14 +51,14 @@ <p> -Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> +Keyboard and mouse sharing application. Cross platform, open source, and totally awesome.<br /><br /> Copyright © 2012-2016 Symless Ltd.<br /> -Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br /><br /> -Synergy is released under the GNU General Public License (GPLv2).<br /><br /> +Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> The Synergy GUI is based on QSynergy by Volker Lanz.<br /><br /> Visit our website for help and info (symless.com). -</p> +</p> +Synergy is released under the GNU General Public License (GPLv2).<br /><br /> 1 From 176d7c9286e416ede12523d5ad6b2ec596f70920 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 5 Oct 2016 22:12:00 +0100 Subject: [PATCH 096/241] Update installer graphics --- res/banner.bmp | Bin 85896 -> 114514 bytes res/dialog.bmp | Bin 461816 -> 615402 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/res/banner.bmp b/res/banner.bmp index f5838d857ed2559b208458787c3977aba575d97f..418746c867fee0398e6a7497c19df880bfb1f8ba 100644 GIT binary patch literal 114514 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 diff --git a/res/dialog.bmp b/res/dialog.bmp index 4899e58333408c60b19861b738882e3ba81b43b7..683f4fe5e12d8f1df4a9a8d9332f51cf7ed5332c 100644 GIT binary patch literal 615402 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 O0uX=z1R$^w1pXhybvg(D literal 461816 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 From 742cd70f986cdeb5a4d6df7aa865857782aaf649 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 6 Oct 2016 11:29:34 +0100 Subject: [PATCH 097/241] #5627 Toggle fingerprint visibility when toggling SSL state --- src/gui/res/MainWindowBase.ui | 2 +- src/gui/src/AppConfig.cpp | 5 ++++- src/gui/src/AppConfig.h | 7 ++++--- src/gui/src/MainWindow.cpp | 13 ++++++++++++- src/gui/src/MainWindow.h | 1 + src/gui/src/VersionChecker.cpp | 6 ++++-- 6 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/gui/res/MainWindowBase.ui b/src/gui/res/MainWindowBase.ui index 5a852c8ca..87144cd13 100644 --- a/src/gui/res/MainWindowBase.ui +++ b/src/gui/res/MainWindowBase.ui @@ -128,7 +128,7 @@ - Fingerprint: + SSL Fingerprint: diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index a6d081f96..9848e6b45 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -281,7 +281,10 @@ ElevateMode AppConfig::elevateMode() return m_ElevateMode; } -void AppConfig::setCryptoEnabled(bool e) { m_CryptoEnabled = e; } +void AppConfig::setCryptoEnabled(bool e) { + m_CryptoEnabled = e; + emit sslToggled(e); +} bool AppConfig::getCryptoEnabled() const { return (edition() == Pro) && m_CryptoEnabled; diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 3d15ec6e4..271ef6834 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -98,14 +98,15 @@ class AppConfig: public QObject void setCryptoEnabled(bool e); bool getCryptoEnabled() const; + void setAutoHide(bool b); bool getAutoHide(); - void saveSettings(); - bool activationHasRun() const; AppConfig& activationHasRun(bool value); + void saveSettings(); + protected: QSettings& settings(); void setScreenName(const QString& s); @@ -118,7 +119,6 @@ class AppConfig: public QObject void setLanguage(const QString language); void setStartedBefore(bool b); void setElevateMode(ElevateMode em); - void loadSettings(); private: @@ -150,6 +150,7 @@ class AppConfig: public QObject signals: void editionSet(int); + void sslToggled(bool enabled); }; #endif diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 64271c142..f9903a125 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -140,6 +140,7 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : m_pLabelPadlock->hide(); connect (this, SIGNAL(windowShown()), this, SLOT(on_windowShown()), Qt::QueuedConnection); connect (&m_AppConfig, SIGNAL(editionSet(int)), this, SLOT(setEdition(int)), Qt::QueuedConnection); + connect (&m_AppConfig, SIGNAL(sslToggled(bool)), this, SLOT(sslToggled(bool)), Qt::QueuedConnection); } MainWindow::~MainWindow() @@ -497,7 +498,7 @@ void MainWindow::restartSynergy() void MainWindow::proofreadInfo() { - setEdition(m_AppConfig.edition()); + setEdition(m_AppConfig.edition()); // Why is this here? int oldState = m_SynergyState; m_SynergyState = synergyDisconnected; @@ -628,6 +629,16 @@ void MainWindow::startSynergy() } } +void +MainWindow::sslToggled (bool enabled) +{ + if (enabled) { + m_pSslCertificate = new SslCertificate(this); + m_pSslCertificate->generateCertificate(); + } + updateLocalFingerprint(); +} + bool MainWindow::clientArgs(QStringList& args, QString& app) { app = appPath(appConfig().synergycName()); diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 2f0b2c5bc..efd83dcca 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -126,6 +126,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase void startSynergy(); protected slots: + void sslToggled(bool enabled); void on_m_pGroupClient_toggled(bool on); void on_m_pGroupServer_toggled(bool on); bool on_m_pButtonBrowseConfigFile_clicked(); diff --git a/src/gui/src/VersionChecker.cpp b/src/gui/src/VersionChecker.cpp index 1f6980fc8..4ce274332 100644 --- a/src/gui/src/VersionChecker.cpp +++ b/src/gui/src/VersionChecker.cpp @@ -51,8 +51,10 @@ void VersionChecker::replyFinished(QNetworkReply* reply) if (!newestVersion.isEmpty()) { QString currentVersion = getVersion(); - if (compareVersions(currentVersion, newestVersion) > 0) - emit updateFound(newestVersion); + if (currentVersion != "Unknown") { + if (compareVersions(currentVersion, newestVersion) > 0) + emit updateFound(newestVersion); + } } } From 9837c982cd2ff0ec89b9683eaabd6657868f858a Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 6 Oct 2016 12:58:57 +0100 Subject: [PATCH 098/241] #5640 About dialog tweaks --- src/gui/res/AboutDialogBase.ui | 87 +++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/src/gui/res/AboutDialogBase.ui b/src/gui/res/AboutDialogBase.ui index 52d8330fe..01df3dd95 100644 --- a/src/gui/res/AboutDialogBase.ui +++ b/src/gui/res/AboutDialogBase.ui @@ -13,25 +13,19 @@ 0 0 450 - 350 + 378 - + 0 0 - - - 450 - 350 - - 450 - 350 + 378 @@ -41,48 +35,20 @@ true - - + + - + 0 0 - - <p> -Keyboard and mouse sharing application. Cross platform, open source, and totally awesome.<br /><br /> -Copyright © 2012-2016 Symless Ltd.<br /> -Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br /> -Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> -The Synergy GUI is based on QSynergy by Volker Lanz.<br /><br /> -Visit our website for help and info (symless.com). -</p> -Synergy is released under the GNU General Public License (GPLv2).<br /><br /> - - - 1 - - - - - - - Qt::Vertical - - - QSizePolicy::Preferred - - + - 20 - 100 + 450 + 16777215 - - - - @@ -197,6 +163,41 @@ Synergy is released under the GNU General Public License (GPLv2).<br />< + + + + + 0 + 0 + + + + <html><head/><body><p>Keyboard and mouse sharing application. <br/><br/>Copyright © 2012-2016 Symless Ltd.<br/>Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.</p><p>Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br/>The Synergy GUI is based on QSynergy by Volker Lanz. </p><p>Synergy is released under the GNU General Public License (GPLv2).</p></body></html> + + + false + + + 1 + + + + + + + Qt::Vertical + + + QSizePolicy::Preferred + + + + 20 + 100 + + + + From 42ba77ae049f2235ea49317d34d9cc330f6e7f40 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 6 Oct 2016 13:11:07 +0100 Subject: [PATCH 099/241] v1.8.4 rc2 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b3e066a88..731f03fa2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 4) -set(VERSION_STAGE rc1) +set(VERSION_STAGE rc2) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From 217eb475dee83d4e736e7da9b553df315598ccb4 Mon Sep 17 00:00:00 2001 From: Andrew Magill Date: Tue, 19 Jan 2016 00:22:08 -0700 Subject: [PATCH 100/241] #5183 Accumulate fractional moves across updates. --- src/lib/platform/MSWindowsScreen.cpp | 32 ++++++++++++++++++++++++++------ src/lib/platform/MSWindowsScreen.h | 9 ++++++++- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 1011cb0c9..74abfcd75 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -105,6 +105,7 @@ MSWindowsScreen::MSWindowsScreen( m_xCenter(0), m_yCenter(0), m_multimon(false), m_xCursor(0), m_yCursor(0), + m_xFractionalMove(0.0f), m_yFractionalMove(0.0f), m_sequenceNumber(0), m_mark(0), m_markReceived(0), @@ -575,6 +576,21 @@ void MSWindowsScreen::saveMousePosition(SInt32 x, SInt32 y) { LOG((CLOG_DEBUG5 "saved mouse position for next delta: %+d,%+d", x,y)); } +void MSWindowsScreen::accumulateFractionalMove(float x, float y, SInt32& intX, SInt32& intY) +{ + // Accumulate together the move into the running total + m_xFractionalMove += x; + m_yFractionalMove += y; + + // Return the integer part + intX = (SInt32)m_xFractionalMove; + intY = (SInt32)m_yFractionalMove; + + // And keep only the fractional part + m_xFractionalMove -= intX; + m_yFractionalMove -= intY; +} + UInt32 MSWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask) { @@ -1355,16 +1371,18 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) { SInt32 originalMX = mx; SInt32 originalMY = my; + float scaledMX = (float)mx; + float scaledMY = (float)my; if (DpiHelper::s_dpiScaled) { - mx = (SInt32)(mx / DpiHelper::getDpi()); - my = (SInt32)(my / DpiHelper::getDpi()); + scaledMX /= DpiHelper::getDpi(); + scaledMY /= DpiHelper::getDpi(); } // compute motion delta (relative to the last known // mouse position) - SInt32 x = mx - m_xCursor; - SInt32 y = my - m_yCursor; + float x = scaledMX - m_xCursor; + float y = scaledMY - m_yCursor; LOG((CLOG_DEBUG3 "mouse move - motion delta: %+d=(%+d - %+d),%+d=(%+d - %+d)", @@ -1377,7 +1395,7 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) } // save position to compute delta of next motion - saveMousePosition(mx, my); + saveMousePosition((SInt32)scaledMX, (SInt32)scaledMY); if (m_isOnScreen) { @@ -1415,7 +1433,9 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) } else { // send motion - sendEvent(m_events->forIPrimaryScreen().motionOnSecondary(), MotionInfo::alloc(x, y)); + SInt32 ix, iy; + accumulateFractionalMove(x, y, ix, iy); + sendEvent(m_events->forIPrimaryScreen().motionOnSecondary(), MotionInfo::alloc(ix, iy)); } } diff --git a/src/lib/platform/MSWindowsScreen.h b/src/lib/platform/MSWindowsScreen.h index 7e734f85c..4d947be7f 100644 --- a/src/lib/platform/MSWindowsScreen.h +++ b/src/lib/platform/MSWindowsScreen.h @@ -215,7 +215,11 @@ class MSWindowsScreen : public PlatformScreen { // save last position of mouse to compute next delta movement void saveMousePosition(SInt32 x, SInt32 y); - + + // accumulates together a series of fractional pixel moves, each time + // taking away and returning just the integer part of the running total. + void accumulateFractionalMove(float x, float y, SInt32& intX, SInt32& intY); + // check if it is a modifier key repeating message bool isModifierRepeat(KeyModifierMask oldState, KeyModifierMask state, WPARAM wParam) const; @@ -266,6 +270,9 @@ class MSWindowsScreen : public PlatformScreen { // last mouse position SInt32 m_xCursor, m_yCursor; + // accumulated fractional pixel moves + float m_xFractionalMove, m_yFractionalMove; + // last clipboard UInt32 m_sequenceNumber; From fcd81530022a81fcf7d8bae626682e7c7a34f041 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 6 Oct 2016 15:49:44 +0100 Subject: [PATCH 101/241] v1.8.4 rc3 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 731f03fa2..fe16ecbd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 4) -set(VERSION_STAGE rc2) +set(VERSION_STAGE rc3) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From 0e209aa903a818e74731ebf93641e4e80f514d28 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 11 Oct 2016 10:13:50 +0100 Subject: [PATCH 102/241] Update Changelog --- ChangeLog | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ChangeLog b/ChangeLog index 270668f52..19d10d500 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +v1.8.4-stable +============= + +Bug #4041 UHD/4K DPI scaling broken on Windows servers +Bug #4420 When XRandR adds a screen, it is inaccessible +Bug #5603 Activation notification depends on existence of /etc/os-release +Bug #5624 Update notification sometimes requests a downgrade +Bug #5329 Current date is shown for build date in the about dialog +Bug #5640 Synergy branding is inconsistent across platforms +Enhancement #5617 Remove redundant plugin infrastructure +Enhancement #5627 Move SSL certificate generation to main window +Enhancement #5628 Move SSL implementation into core binary +Enhancement #5629 Move activation from wizard into new dialog window + v1.8.3-stable ============= Bug #2765 - A letter appears on macOS clients when the spacebar is pressed From a6ff90794fbf9a6d4a0bd7201f1443faabead8fa Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 11 Oct 2016 10:14:18 +0100 Subject: [PATCH 103/241] v1.8.4 stable --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fe16ecbd3..fc5a33114 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 4) -set(VERSION_STAGE rc3) +set(VERSION_STAGE stable) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From fc879323bc98e2452b9a9adf572524f5b893882e Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 12 Oct 2016 12:56:52 +0100 Subject: [PATCH 104/241] #5657 Removed password log in in activation window --- src/gui/res/ActivationDialog.ui | 94 ++++++++------------------------------- src/gui/src/ActivationDialog.cpp | 95 ++++++---------------------------------- src/gui/src/ActivationDialog.h | 4 -- 3 files changed, 33 insertions(+), 160 deletions(-) diff --git a/src/gui/res/ActivationDialog.ui b/src/gui/res/ActivationDialog.ui index fb700f919..d11a1a9f8 100644 --- a/src/gui/res/ActivationDialog.ui +++ b/src/gui/res/ActivationDialog.ui @@ -7,7 +7,7 @@ 0 0 440 - 314 + 214 @@ -15,7 +15,7 @@ - + 75 @@ -23,76 +23,7 @@ - &Account login - - - true - - - - - - - QFormLayout::AllNonFixedFieldsGrow - - - 20 - - - 10 - - - - - Email: - - - - - - - - 0 - 0 - - - - QLineEdit::Normal - - - - - - - Password: - - - - - - - - 0 - 0 - - - - QLineEdit::Password - - - - - - - - - - 75 - true - - - - &Serial key + Serial key @@ -109,20 +40,33 @@ - false + true <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:10pt; 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;"><br /></p></body></html> +</style></head><body style=" font-family:'.SF NS Text'; font-size:13pt; 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> false + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 402510190..2170ef864 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -20,26 +20,8 @@ ActivationDialog::ActivationDialog(QWidget* parent, AppConfig& appConfig) : m_appConfig (&appConfig) { ui->setupUi(this); - - ui->m_pLineEditEmail->setText(appConfig.activateEmail()); - ui->m_pTextEditSerialKey->setText(appConfig.serialKey()); - - if (!appConfig.serialKey().isEmpty()) { - ui->m_pRadioButtonActivate->setAutoExclusive(false); - ui->m_pRadioButtonSubscription->setAutoExclusive(false); - ui->m_pRadioButtonActivate->setChecked(false); - ui->m_pRadioButtonSubscription->setChecked(true); - ui->m_pRadioButtonActivate->setAutoExclusive(true); - ui->m_pRadioButtonSubscription->setAutoExclusive(true); - ui->m_pTextEditSerialKey->setFocus(); - ui->m_pTextEditSerialKey->moveCursor(QTextCursor::End); - } else { - if (ui->m_pLineEditEmail->text().isEmpty()) { - ui->m_pLineEditEmail->setFocus(); - } else { - ui->m_pLineEditPassword->setFocus(); - } - } + ui->m_pTextEditSerialKey->setFocus(); + ui->m_pTextEditSerialKey->moveCursor(QTextCursor::End); } ActivationDialog::~ActivationDialog() @@ -74,30 +56,6 @@ void ActivationDialog::reject() } } -void ActivationDialog::on_m_pRadioButtonSubscription_toggled(bool checked) -{ - if (checked) { - ui->m_pLineEditEmail->setEnabled(false); - ui->m_pLineEditPassword->setEnabled(false); - ui->m_pTextEditSerialKey->setEnabled(true); - ui->m_pTextEditSerialKey->setFocus(); - } -} - -void ActivationDialog::on_m_pRadioButtonActivate_toggled(bool checked) -{ - if (checked) { - ui->m_pLineEditEmail->setEnabled(true); - ui->m_pLineEditPassword->setEnabled(true); - ui->m_pTextEditSerialKey->setEnabled(false); - if (ui->m_pLineEditEmail->text().isEmpty()) { - ui->m_pLineEditEmail->setFocus(); - } else { - ui->m_pLineEditPassword->setFocus(); - } - } -} - void ActivationDialog::accept() { QMessageBox message; @@ -108,45 +66,20 @@ void ActivationDialog::accept() m_appConfig->saveSettings(); try { - if (ui->m_pRadioButtonActivate->isChecked()) { - WebClient webClient; - QString email = ui->m_pLineEditEmail->text(); - QString password = ui->m_pLineEditPassword->text(); - - if (!webClient.setEmail (email, error)) { - message.critical (this, "Invalid Email Address", tr("%1").arg(error)); - return; - } - else if (!webClient.setPassword (password, error)) { - message.critical (this, "Invalid Password", tr("%1").arg(error)); - return; - } - else if (!webClient.getEdition (edition, error)) { - FailedLoginDialog failedLoginDialog (this, error); - failedLoginDialog.exec(); - return; - } + QString serialKey = ui->m_pTextEditSerialKey->toPlainText(); - m_appConfig->setActivateEmail (email); - m_appConfig->clearSerialKey(); - ui->m_pTextEditSerialKey->clear(); - notifyActivation ("login:" + m_appConfig->activateEmail()); + if (!m_appConfig->setSerialKey (serialKey, error)) { + message.critical(this, "Invalid Serial Key", tr("%1").arg(error)); + return; } - else { - QString serialKey = ui->m_pTextEditSerialKey->toPlainText(); - if (!m_appConfig->setSerialKey (serialKey, error)) { - message.critical (this, "Invalid Serial Key", tr("%1").arg(error)); - return; - } - - SubscriptionManager subscriptionManager (this, *m_appConfig, edition); - if (!subscriptionManager.activateSerial (serialKey)) { - return; - } - m_appConfig->setActivateEmail(""); - notifyActivation ("serial:" + m_appConfig->serialKey()); + SubscriptionManager subscriptionManager (this, *m_appConfig, edition); + if (!subscriptionManager.activateSerial (serialKey)) { + return; } + m_appConfig->setActivateEmail(""); + notifyActivation("serial:" + m_appConfig->serialKey()); + } catch (std::exception& e) { message.critical (this, "Unknown Error", @@ -159,7 +92,7 @@ void ActivationDialog::accept() m_appConfig->setEdition(edition); m_appConfig->saveSettings(); - message.information (this, "Activated!", - tr("Thanks for activating %1!").arg (getEditionName (edition))); + message.information(this, "Activated!", + tr("Thanks for activating %1!").arg (getEditionName (edition))); QDialog::accept(); } diff --git a/src/gui/src/ActivationDialog.h b/src/gui/src/ActivationDialog.h index 6fb926cce..0f3328ac9 100644 --- a/src/gui/src/ActivationDialog.h +++ b/src/gui/src/ActivationDialog.h @@ -27,10 +27,6 @@ public slots: private: Ui::ActivationDialog *ui; AppConfig* m_appConfig; - -private slots: - void on_m_pRadioButtonSubscription_toggled(bool checked); - void on_m_pRadioButtonActivate_toggled(bool checked); }; #endif // ACTIVATIONDIALOG_H From a50ae2ad36fca1471bcf454156f1806b6008737e Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 12 Oct 2016 12:59:38 +0100 Subject: [PATCH 105/241] Fixed code style --- src/gui/src/ActivationDialog.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 2170ef864..9f2e3d51d 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -17,7 +17,7 @@ ActivationDialog::ActivationDialog(QWidget* parent, AppConfig& appConfig) : QDialog(parent), ui(new Ui::ActivationDialog), - m_appConfig (&appConfig) + m_appConfig(&appConfig) { ui->setupUi(this); ui->m_pTextEditSerialKey->setFocus(); @@ -68,13 +68,13 @@ void ActivationDialog::accept() try { QString serialKey = ui->m_pTextEditSerialKey->toPlainText(); - if (!m_appConfig->setSerialKey (serialKey, error)) { + if (!m_appConfig->setSerialKey(serialKey, error)) { message.critical(this, "Invalid Serial Key", tr("%1").arg(error)); return; } - SubscriptionManager subscriptionManager (this, *m_appConfig, edition); - if (!subscriptionManager.activateSerial (serialKey)) { + SubscriptionManager subscriptionManager(this, *m_appConfig, edition); + if (!subscriptionManager.activateSerial(serialKey)) { return; } m_appConfig->setActivateEmail(""); @@ -82,7 +82,7 @@ void ActivationDialog::accept() } catch (std::exception& e) { - message.critical (this, "Unknown Error", + message.critical(this, "Unknown Error", tr("An error occurred while trying to activate Synergy. " "Please contact the helpdesk, and provide the " "following details.\n\n%1").arg(e.what())); @@ -93,6 +93,6 @@ void ActivationDialog::accept() m_appConfig->saveSettings(); message.information(this, "Activated!", - tr("Thanks for activating %1!").arg (getEditionName (edition))); + tr("Thanks for activating %1!").arg(getEditionName(edition))); QDialog::accept(); } From 2b9f48602c13b5e783446e96ce348697bab7b38c Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 12 Oct 2016 15:09:29 +0100 Subject: [PATCH 106/241] #5620 Make Xcode 8 happy with null cast --- src/lib/platform/OSXClipboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/OSXClipboard.cpp b/src/lib/platform/OSXClipboard.cpp index 84f7c7cec..7dae20816 100644 --- a/src/lib/platform/OSXClipboard.cpp +++ b/src/lib/platform/OSXClipboard.cpp @@ -122,7 +122,7 @@ OSXClipboard::add(EFormat format, const String & data) PasteboardPutItemFlavor( m_pboard, - (PasteboardItemID) 0, + nullptr, flavorType, dataRef, kPasteboardFlavorNoFlags); From d1396c976727779accd7a7bac1bc61d8e154abfa Mon Sep 17 00:00:00 2001 From: rishubil Date: Tue, 28 Oct 2014 21:51:53 +0900 Subject: [PATCH 107/241] #3797 Fix "Unix Makefile" build on macOS --- ext/toolchain/commands1.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 7f3570297..78fd27e66 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -430,14 +430,16 @@ def configureCore(self, target="", extraArgs=""): if generator.cmakeName.find('Unix Makefiles') != -1: cmake_args += ' -DCMAKE_BUILD_TYPE=' + target.capitalize() - elif sys.platform == "darwin": + if sys.platform == "darwin": macSdkMatch = re.match("(\d+)\.(\d+)", self.macSdk) if not macSdkMatch: raise Exception("unknown osx version: " + self.macSdk) - sdkDir = self.getMacSdkDir() - cmake_args += " -DCMAKE_OSX_SYSROOT=" + sdkDir - cmake_args += " -DCMAKE_OSX_DEPLOYMENT_TARGET=" + self.macSdk + if generator.cmakeName.find('Unix Makefiles') == -1: + sdkDir = self.getMacSdkDir() + cmake_args += " -DCMAKE_OSX_SYSROOT=" + sdkDir + cmake_args += " -DCMAKE_OSX_DEPLOYMENT_TARGET=" + self.macSdk + cmake_args += " -DOSX_TARGET_MAJOR=" + macSdkMatch.group(1) cmake_args += " -DOSX_TARGET_MINOR=" + macSdkMatch.group(2) @@ -551,7 +553,7 @@ def getMacSdkDir(self): if os.path.exists(sdkPath): return sdkPath - return "/Developer/SDKs/" + sdkDirName + ".sdk" + return os.popen('xcodebuild -version -sdk macosx' + self.macSdk + ' Path').read().strip() # http://tinyurl.com/cs2rxxb def fixCmakeEclipseBug(self): From df88faaad8dccba38f91f026dc09b4984539d239 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 12 Oct 2016 15:43:01 +0100 Subject: [PATCH 108/241] #5620 Convert OSXScreen into Objective C++ --- src/lib/platform/OSXScreen.h | 2 ++ src/lib/platform/{OSXScreen.cpp => OSXScreen.mm} | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) rename src/lib/platform/{OSXScreen.cpp => OSXScreen.mm} (99%) diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h index dce5ac088..0357583e7 100644 --- a/src/lib/platform/OSXScreen.h +++ b/src/lib/platform/OSXScreen.h @@ -344,4 +344,6 @@ class OSXScreen : public PlatformScreen { Mutex* m_carbonLoopMutex; CondVar* m_carbonLoopReady; #endif + + class OSXScreenImpl* m_impl; }; diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.mm similarity index 99% rename from src/lib/platform/OSXScreen.cpp rename to src/lib/platform/OSXScreen.mm index e81f1621d..aa8f1e95b 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.mm @@ -112,7 +112,8 @@ OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCurso m_lastSingleClickYCursor(0), m_autoShowHideCursor(autoShowHideCursor), m_events(events), - m_getDropTargetThread(NULL) + m_getDropTargetThread(NULL), + m_impl(NULL) { try { m_displayID = CGMainDisplayID(); From 5ea1fdc7c69059a8f94cbb79e2a5e6bf53b2f1c0 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 12 Oct 2016 16:10:57 +0100 Subject: [PATCH 109/241] #5620 Remove deprecated NXClickTime call --- src/lib/platform/CMakeLists.txt | 2 +- src/lib/platform/OSXScreen.mm | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index 6c272c214..481d8ef95 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -19,7 +19,7 @@ if (WIN32) file(GLOB sources "MSWindows*.cpp") elseif (APPLE) file(GLOB headers "OSX*.h" "IOSX*.h") - file(GLOB sources "OSX*.cpp" "IOSX*.cpp" "OSX*.m") + file(GLOB sources "OSX*.cpp" "IOSX*.cpp" "OSX*.m" "OSX*.mm") elseif (UNIX) file(GLOB headers "XWindows*.h") file(GLOB sources "XWindows*.cpp") diff --git a/src/lib/platform/OSXScreen.mm b/src/lib/platform/OSXScreen.mm index aa8f1e95b..2c2e66d6b 100644 --- a/src/lib/platform/OSXScreen.mm +++ b/src/lib/platform/OSXScreen.mm @@ -45,6 +45,8 @@ #include #include +#import + // Set some enums for fast user switching if we're building with an SDK // from before such support was added. #if !defined(MAC_OS_X_VERSION_10_3) || \ @@ -527,9 +529,7 @@ // we define our own defaults. const double maxDiff = sqrt(2) + 0.0001; - - NXEventHandle handle = NXOpenEventStatus(); - double clickTime = NXClickTime(handle); + double clickTime = [NSEvent doubleClickInterval]; // As long as the click is within the time window and distance window // increase clickState (double click, triple click, etc) From c21fc4a6dd73bb8b1afa46ec551df3062d1aa86f Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 12 Oct 2016 16:50:11 +0100 Subject: [PATCH 110/241] #3797 Revert to using hardcoded SDK path for buildbot --- ext/toolchain/commands1.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 78fd27e66..9c28ab75f 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -553,7 +553,8 @@ def getMacSdkDir(self): if os.path.exists(sdkPath): return sdkPath - return os.popen('xcodebuild -version -sdk macosx' + self.macSdk + ' Path').read().strip() + # return os.popen('xcodebuild -version -sdk macosx' + self.macSdk + ' Path').read().strip() + return "/Developer/SDKs/" + sdkDirName + ".sdk" # http://tinyurl.com/cs2rxxb def fixCmakeEclipseBug(self): From 817f8f2bcb92f96e2fcd47792ba02a69898155ed Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 13 Oct 2016 11:11:37 +0100 Subject: [PATCH 111/241] Updated git ignore list --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index fae40b009..347b91b17 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ config.h +.DS_Store *.pyc +*.o *~ \.*.swp +*build-gui-Desktop_Qt* /bin /lib /build From c7cd74ab5fb9b2952eaab2df0cc799cc765a6f07 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 13 Oct 2016 11:11:56 +0100 Subject: [PATCH 112/241] Fixed code style --- src/gui/src/MainWindow.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index f9903a125..c927839f5 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -746,7 +746,6 @@ bool MainWindow::serverArgs(QStringList& args, QString& app) } } - app = appPath(appConfig().synergysName()); if (!QFile::exists(app)) From d92fcd2453ea32f70e20798b459ec82a671a11d1 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 13 Oct 2016 11:15:38 +0100 Subject: [PATCH 113/241] #5657 Added version 2 trial serial key support --- src/lib/synergy/SubscriptionKey.h | 1 + src/lib/synergy/SubscriptionManager.cpp | 45 +++++++++++++++++++++++++-------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/lib/synergy/SubscriptionKey.h b/src/lib/synergy/SubscriptionKey.h index 28744fed5..d63a7d8fe 100644 --- a/src/lib/synergy/SubscriptionKey.h +++ b/src/lib/synergy/SubscriptionKey.h @@ -27,4 +27,5 @@ struct SubscriptionKey { int m_userLimit; int m_warnTime; int m_expireTime; + bool m_trial; }; diff --git a/src/lib/synergy/SubscriptionManager.cpp b/src/lib/synergy/SubscriptionManager.cpp index 78207dc25..a6a68b5be 100644 --- a/src/lib/synergy/SubscriptionManager.cpp +++ b/src/lib/synergy/SubscriptionManager.cpp @@ -140,9 +140,11 @@ SubscriptionManager::parsePlainSerial(const String& plainText, SubscriptionKey& pos += 1; } - // e.g.: {v1;trial;Bob;1;email;company name;1398297600;1398384000} + bool validSerial = false; + if ((parts.size() == 8) && (parts.at(0).find("v1") != String::npos)) { + // e.g.: {v1;basic;Bob;1;email;company name;1398297600;1398384000} key.m_type = parts.at(1); key.m_name = parts.at(2); sscanf(parts.at(3).c_str(), "%d", &key.m_userLimit); @@ -150,9 +152,23 @@ SubscriptionManager::parsePlainSerial(const String& plainText, SubscriptionKey& 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); - + + validSerial = true; + } + else if ((parts.size() == 9) + && (parts.at(0).find("v2") != String::npos)) { + // e.g.: {v2;trial;basic;Bob;1;email;company name;1398297600;1398384000} + key.m_trial = parts.at(1) == "trial" ? true : false; + key.m_type = parts.at(2); + key.m_name = parts.at(3); + sscanf(parts.at(4).c_str(), "%d", &key.m_userLimit); + key.m_email = parts.at(5); + key.m_company = parts.at(6); + sscanf(parts.at(7).c_str(), "%d", &key.m_warnTime); + sscanf(parts.at(8).c_str(), "%d", &key.m_expireTime); + // only limit to trial version - if (key.m_type == "trial") { + if (key.m_trial) { if (time(0) > key.m_expireTime) { throw XSubscription("trial has expired"); } @@ -161,18 +177,25 @@ SubscriptionManager::parsePlainSerial(const String& plainText, SubscriptionKey& 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")); + dayLeft, + dayLeft == 1 ? "day" : "days")); + } + else { + } } - + + validSerial = true; + } + + if (validSerial) { const char* userText = (key.m_userLimit == 1) ? "user" : "users"; LOG((CLOG_INFO "%s subscription valid is for %d %s, registered to %s", - key.m_type.c_str(), - key.m_userLimit, - userText, - key.m_name.c_str())); - + key.m_type.c_str(), + key.m_userLimit, + userText, + key.m_name.c_str())); + return; } } From 4be9fc1800d4e6e4c379feecfe536ea6e3dbc8b5 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 13 Oct 2016 11:18:03 +0100 Subject: [PATCH 114/241] Removed unused code --- src/lib/synergy/ArgParser.cpp | 8 -------- src/lib/synergy/ToolApp.cpp | 11 ----------- src/lib/synergy/ToolApp.h | 1 - src/lib/synergy/ToolArgs.cpp | 2 -- src/lib/synergy/ToolArgs.h | 2 -- 5 files changed, 24 deletions(-) diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 431a91b73..4e1dafddc 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -189,18 +189,10 @@ ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv) args.m_loginAuthenticate = true; return true; } - else if (isArg(i, argc, argv, NULL, "--get-plugin-list", 0)) { - args.m_getPluginList = true; - return true; - } else if (isArg(i, argc, argv, NULL, "--get-installed-dir", 0)) { args.m_getInstalledDir = true; return true; } - else if (isArg(i, argc, argv, NULL, "--get-plugin-dir", 0)) { - args.m_getPluginDir = true; - return true; - } else if (isArg(i, argc, argv, NULL, "--get-profile-dir", 0)) { args.m_getProfileDir = true; return true; diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index e6695f518..2aafdea1d 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -72,15 +72,9 @@ ToolApp::run(int argc, char** argv) else if (m_args.m_loginAuthenticate) { loginAuth(); } - else if (m_args.m_getPluginList) { - getPluginList(); - } else if (m_args.m_getInstalledDir) { std::cout << ARCH->getInstalledDirectory() << std::endl; } - else if (m_args.m_getPluginDir) { - std::cout << ARCH->getPluginDirectory() << std::endl; - } else if (m_args.m_getProfileDir) { std::cout << ARCH->getProfileDirectory() << std::endl; } @@ -171,11 +165,6 @@ ToolApp::loginAuth() } } -void -ToolApp::getPluginList() -{ -} - void ToolApp::notifyActivation() { diff --git a/src/lib/synergy/ToolApp.h b/src/lib/synergy/ToolApp.h index 8706c79a9..39c87ca78 100644 --- a/src/lib/synergy/ToolApp.h +++ b/src/lib/synergy/ToolApp.h @@ -29,7 +29,6 @@ class ToolApp : public MinimalApp private: void loginAuth(); - void getPluginList(); void notifyActivation(); private: diff --git a/src/lib/synergy/ToolArgs.cpp b/src/lib/synergy/ToolArgs.cpp index f5d2524a6..500a16922 100644 --- a/src/lib/synergy/ToolArgs.cpp +++ b/src/lib/synergy/ToolArgs.cpp @@ -20,8 +20,6 @@ ToolArgs::ToolArgs() : m_printActiveDesktopName(false), m_loginAuthenticate(false), - m_getPluginList(false), - m_getPluginDir(false), m_getInstalledDir(false), m_getProfileDir(false), m_getArch(false), diff --git a/src/lib/synergy/ToolArgs.h b/src/lib/synergy/ToolArgs.h index 0ebc0a4aa..df126d89d 100644 --- a/src/lib/synergy/ToolArgs.h +++ b/src/lib/synergy/ToolArgs.h @@ -26,8 +26,6 @@ class ToolArgs { public: bool m_printActiveDesktopName; bool m_loginAuthenticate; - bool m_getPluginList; - bool m_getPluginDir; bool m_getInstalledDir; bool m_getProfileDir; bool m_getArch; From 82e55702ef031f6e28fcdfe4bdfe0ee5bb6d954e Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 13 Oct 2016 11:20:43 +0100 Subject: [PATCH 115/241] #5657 Removed serial key code from syntool --- src/lib/synergy/ArgParser.cpp | 16 ---------------- src/lib/synergy/ToolApp.cpp | 30 ------------------------------ src/lib/synergy/ToolArgs.cpp | 5 +---- src/lib/synergy/ToolArgs.h | 3 --- 4 files changed, 1 insertion(+), 53 deletions(-) diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 4e1dafddc..11adc2d5d 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -201,22 +201,6 @@ ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv) args.m_getArch = true; return true; } - else if (isArg(i, argc, argv, NULL, "--subscription-serial", 1)) { - args.m_subscriptionSerial = argv[++i]; - if (args.m_subscriptionSerial.empty()) { - LOG((CLOG_CRIT "subscription error: serial was not provided")); - return false; - } - return true; - } - else if (isArg(i, argc, argv, NULL, "--get-subscription-filename", 0)) { - args.m_getSubscriptionFilename = true; - return true; - } - else if (isArg(i, argc, argv, NULL, "--check-subscription", 0)) { - args.m_checkSubscription = true; - return true; - } else if (isArg(i, argc, argv, NULL, "--notify-activation", 0)) { args.m_notifyActivation = true; return true; diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 2aafdea1d..444997d85 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -81,36 +81,6 @@ ToolApp::run(int argc, char** argv) else if (m_args.m_getArch) { std::cout << ARCH->getPlatformName() << std::endl; } - else if (!m_args.m_subscriptionSerial.empty()) { - try { - SubscriptionManager subscriptionManager; - subscriptionManager.activate(m_args.m_subscriptionSerial); - } - catch (XSubscription& e) { - LOG((CLOG_CRIT "subscription error: %s", e.what())); - return kExitSubscription; - } - } - else if (m_args.m_getSubscriptionFilename) { - try { - SubscriptionManager subscriptionManager; - subscriptionManager.printFilename(); - } - catch (XSubscription& e) { - LOG((CLOG_CRIT "subscription error: %s", e.what())); - return kExitSubscription; - } - } - else if (m_args.m_checkSubscription) { - try { - SubscriptionManager subscriptionManager; - subscriptionManager.checkFile(""); - } - catch (XSubscription& e) { - LOG((CLOG_CRIT "subscription error: %s", e.what())); - return kExitSubscription; - } - } else if (m_args.m_notifyActivation) { notifyActivation(); } diff --git a/src/lib/synergy/ToolArgs.cpp b/src/lib/synergy/ToolArgs.cpp index 500a16922..5f67c6667 100644 --- a/src/lib/synergy/ToolArgs.cpp +++ b/src/lib/synergy/ToolArgs.cpp @@ -23,9 +23,6 @@ ToolArgs::ToolArgs() : m_getInstalledDir(false), m_getProfileDir(false), m_getArch(false), - m_getSubscriptionFilename(false), - m_checkSubscription(false), - m_notifyActivation(false), - m_subscriptionSerial() + m_notifyActivation(false) { } diff --git a/src/lib/synergy/ToolArgs.h b/src/lib/synergy/ToolArgs.h index df126d89d..5febab9e3 100644 --- a/src/lib/synergy/ToolArgs.h +++ b/src/lib/synergy/ToolArgs.h @@ -29,8 +29,5 @@ class ToolArgs { bool m_getInstalledDir; bool m_getProfileDir; bool m_getArch; - bool m_getSubscriptionFilename; - bool m_checkSubscription; bool m_notifyActivation; - String m_subscriptionSerial; }; From 92680b2877f45eb7956cb339667981fe26be50cd Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 13 Oct 2016 13:53:09 +0100 Subject: [PATCH 116/241] #5657 Extracted shared code between GUI and core --- src/lib/CMakeLists.txt | 2 +- src/lib/shared/CMakeLists.txt | 26 +++ .../SubscriptionKey.h => shared/SerialKey.h} | 22 +- src/lib/synergy/SubscriptionManager.cpp | 222 --------------------- src/lib/synergy/SubscriptionManager.h | 55 ----- src/lib/synergy/ToolApp.cpp | 1 - src/test/unittests/synergy/SubscriptionTests.cpp | 196 +++++++++--------- 7 files changed, 141 insertions(+), 383 deletions(-) create mode 100644 src/lib/shared/CMakeLists.txt rename src/lib/{synergy/SubscriptionKey.h => shared/SerialKey.h} (66%) delete mode 100644 src/lib/synergy/SubscriptionManager.cpp delete mode 100644 src/lib/synergy/SubscriptionManager.h diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 48beb80a8..f53d9db86 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -25,7 +25,7 @@ add_subdirectory(net) add_subdirectory(platform) add_subdirectory(server) add_subdirectory(synergy) - +add_subdirectory(shared) if (WIN32) add_subdirectory(synwinhk) diff --git a/src/lib/shared/CMakeLists.txt b/src/lib/shared/CMakeLists.txt new file mode 100644 index 000000000..042d866cd --- /dev/null +++ b/src/lib/shared/CMakeLists.txt @@ -0,0 +1,26 @@ +# 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 . + +file(GLOB headers "*.h") +file(GLOB sources "*.cpp") + +if (SYNERGY_ADD_HEADERS) + list(APPEND sources ${headers}) +endif() + +add_library(shared STATIC ${sources}) + +target_link_libraries(shared arch base) + diff --git a/src/lib/synergy/SubscriptionKey.h b/src/lib/shared/SerialKey.h similarity index 66% rename from src/lib/synergy/SubscriptionKey.h rename to src/lib/shared/SerialKey.h index d63a7d8fe..45d9728ee 100644 --- a/src/lib/synergy/SubscriptionKey.h +++ b/src/lib/shared/SerialKey.h @@ -17,13 +17,23 @@ #pragma once -#include "base/String.h" +#include -struct SubscriptionKey { - String m_name; - String m_type; - String m_email; - String m_company; +class SerialKey { +public: + SerialKey(std::string serial); + + bool isValid(unsigned long long currentTime) const; + bool isExpiring(unsigned long long currentTime) const; + bool isExpired(unsigned long long currentTime) const; + bool isTrial() const; + int edition() const; + +private: + std::string m_name; + std::string m_type; + std::string m_email; + std::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 deleted file mode 100644 index a6a68b5be..000000000 --- a/src/lib/synergy/SubscriptionManager.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* - * 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 . - */ - -#include "synergy/SubscriptionManager.h" - -#include "synergy/XSynergy.h" -#include "arch/Arch.h" -#include "base/Log.h" -#include "base/String.h" -#include "common/Version.h" - -#include -#include -#include -#include -#include -//#include - -#if SYSAPI_WIN32 -const char* kFile = "Synergy.subkey"; -#else -const char* kFile = ".synergy.subkey"; -#endif - -// -// SubscriptionManager -// - -SubscriptionManager::SubscriptionManager() : - m_key() -{ -} - -void -SubscriptionManager::checkFile(const String& filename_) -{ - String filename = filename_; - if (filename.empty()) { - filename = getFilename(); - } - - std::ifstream stream(filename.c_str()); - if (!stream.is_open()) { - throw XSubscription(synergy::string::sprintf( - "Could not open, path=%s", filename.c_str())); - } - - String serial; - stream >> serial; - - String plainText = decode(serial); - parsePlainSerial(plainText, m_key); - - LOG((CLOG_DEBUG "subscription is valid")); -} - -void -SubscriptionManager::activate(const String& serial) -{ - String plainText = decode(serial); - parsePlainSerial(plainText, m_key); - - String filename = getFilename(); - std::ofstream stream(filename.c_str()); - if (!stream.is_open()) { - throw XSubscription(synergy::string::sprintf( - "Could not open, file=%s", filename.c_str())); - } - - stream << serial << std::endl; - LOG((CLOG_DEBUG "subscription file created, path=%s", filename.c_str())); -} - -String -SubscriptionManager::decode(const String& input) -{ - static const char* const lut = "0123456789ABCDEF"; - size_t len = input.length(); - if (len & 1) { - throw XSubscription("Invalid serial, wrong length."); - } - - String output; - output.reserve(len / 2); - for (size_t i = 0; i < len; i += 2) { - - char a = input[i]; - char b = input[i + 1]; - - const char* p = std::lower_bound(lut, lut + 16, a); - const char* q = std::lower_bound(lut, lut + 16, b); - - if (*q != b || *p != a) { - throw XSubscription("Invalid serial, unrecognized digit."); - } - - output.push_back(static_cast(((p - lut) << 4) | (q - lut))); - } - - return output; -} - -void -SubscriptionManager::parsePlainSerial(const String& plainText, SubscriptionKey& key) -{ - String serial; - String parityStart = plainText.substr(0, 1); - String parityEnd = plainText.substr(plainText.length() - 1, 1); - - // check for parity chars { and }, record parity result, then remove them. - if (parityStart == "{" && parityEnd == "}") { - serial = plainText.substr(1, plainText.length() - 2); - - // tokenize serialised subscription. - std::vector parts; - std::string::size_type pos = 0; - bool look = true; - while (look) { - std::string::size_type start = pos; - pos = serial.find(";", pos); - if (pos == String::npos) { - pos = plainText.length(); - look = false; - } - parts.push_back(serial.substr(start, pos - start)); - pos += 1; - } - - bool validSerial = false; - - if ((parts.size() == 8) - && (parts.at(0).find("v1") != String::npos)) { - // e.g.: {v1;basic;Bob;1;email;company name;1398297600;1398384000} - key.m_type = parts.at(1); - key.m_name = parts.at(2); - sscanf(parts.at(3).c_str(), "%d", &key.m_userLimit); - 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); - - validSerial = true; - } - else if ((parts.size() == 9) - && (parts.at(0).find("v2") != String::npos)) { - // e.g.: {v2;trial;basic;Bob;1;email;company name;1398297600;1398384000} - key.m_trial = parts.at(1) == "trial" ? true : false; - key.m_type = parts.at(2); - key.m_name = parts.at(3); - sscanf(parts.at(4).c_str(), "%d", &key.m_userLimit); - key.m_email = parts.at(5); - key.m_company = parts.at(6); - sscanf(parts.at(7).c_str(), "%d", &key.m_warnTime); - sscanf(parts.at(8).c_str(), "%d", &key.m_expireTime); - - // only limit to trial version - if (key.m_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")); - } - else { - - } - } - - validSerial = true; - } - - if (validSerial) { - const char* userText = (key.m_userLimit == 1) ? "user" : "users"; - LOG((CLOG_INFO "%s subscription valid is for %d %s, registered to %s", - key.m_type.c_str(), - key.m_userLimit, - userText, - key.m_name.c_str())); - - return; - } - } - - throw XSubscription(synergy::string::sprintf("Serial is invalid.")); -} - -String -SubscriptionManager::getFilename() -{ - String path = ARCH->getProfileDirectory(); - path = ARCH->concatPath(path, kFile); - if (path.empty()) { - throw XSubscription("Could not get filename."); - } - - return path; -} - -void -SubscriptionManager::printFilename() -{ - std::cout << getFilename() << std::endl; -} diff --git a/src/lib/synergy/SubscriptionManager.h b/src/lib/synergy/SubscriptionManager.h deleted file mode 100644 index fb52701b1..000000000 --- a/src/lib/synergy/SubscriptionManager.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 . - */ - -#pragma once - -#include "SubscriptionKey.h" -#include "common/common.h" - -#include "gtest/gtest_prod.h" - -class SubscriptionManager { -public: - SubscriptionManager(); - - //! Check the subscription activation file - void checkFile(const String& filename); - - //! Create a subscription activation file based on a serial - void activate(const String& serial); - - //! Use standard output to return subscription filename to gui - void printFilename(); - -private: - FRIEND_TEST(SubscriptionTests, decode_invalidLength_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_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); - void parsePlainSerial(const String& plainText, SubscriptionKey& key); - String getFilename(); - - SubscriptionKey m_key; -}; diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 444997d85..bf3dfdce2 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -18,7 +18,6 @@ #include "synergy/ToolApp.h" #include "synergy/ArgParser.h" -#include "synergy/SubscriptionManager.h" #include "arch/Arch.h" #include "base/Log.h" #include "base/String.h" diff --git a/src/test/unittests/synergy/SubscriptionTests.cpp b/src/test/unittests/synergy/SubscriptionTests.cpp index eeda3e7be..2cab31b93 100644 --- a/src/test/unittests/synergy/SubscriptionTests.cpp +++ b/src/test/unittests/synergy/SubscriptionTests.cpp @@ -14,101 +14,101 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - -#include "synergy/SubscriptionManager.h" -#include "synergy/XSynergy.h" - -#include "test/global/gtest.h" - -TEST(SubscriptionTests, decode_invalidLength_throwException) -{ - SubscriptionManager subscriptionManager; - String serial("ABC"); - - EXPECT_THROW(subscriptionManager.decode(serial), XSubscription); -} - -TEST(SubscriptionTests, decode_unrecognizedDigit_throwException) -{ - SubscriptionManager subscriptionManager; - String serial("MOCK"); - - EXPECT_THROW(subscriptionManager.decode(serial), XSubscription); -} - -TEST(SubscriptionTests, parsePlainSerial_noParity_throwException) -{ - SubscriptionManager subscriptionManager; - String painText("MOCK"); - SubscriptionKey key; - - EXPECT_THROW(subscriptionManager.parsePlainSerial(painText, key), XSubscription); -} - -TEST(SubscriptionTests, parsePlainSerial_invalidSerial_throwException) -{ - SubscriptionManager subscriptionManager; - String painText("{MOCK}"); - SubscriptionKey key; - - EXPECT_THROW(subscriptionManager.parsePlainSerial(painText, key), XSubscription); -} - -TEST(SubscriptionTests, parsePlainSerial_validSerial_validSubscriptionKey) -{ - // valid until 2 March 2049 - SubscriptionManager subscriptionManager; - 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("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_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}"); - SubscriptionKey key; - - 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); -} +// +//#include "synergy/LicenseManager.h" +//#include "synergy/XSynergy.h" +// +//#include "test/global/gtest.h" +// +//TEST(SubscriptionTests, decode_invalidLength_throwException) +//{ +// LicenseManager LicenseManager; +// String serial("ABC"); +// +// EXPECT_THROW(LicenseManager.decode(serial), XSubscription); +//} +// +//TEST(SubscriptionTests, decode_unrecognizedDigit_throwException) +//{ +// LicenseManager LicenseManager; +// String serial("MOCK"); +// +// EXPECT_THROW(LicenseManager.decode(serial), XSubscription); +//} +// +//TEST(SubscriptionTests, parsePlainSerial_noParity_throwException) +//{ +// LicenseManager LicenseManager; +// String painText("MOCK"); +// SubscriptionKey key; +// +// EXPECT_THROW(LicenseManager.parsePlainSerial(painText, key), XSubscription); +//} +// +//TEST(SubscriptionTests, parsePlainSerial_invalidSerial_throwException) +//{ +// LicenseManager LicenseManager; +// String painText("{MOCK}"); +// SubscriptionKey key; +// +// EXPECT_THROW(LicenseManager.parsePlainSerial(painText, key), XSubscription); +//} +// +//TEST(SubscriptionTests, parsePlainSerial_validSerial_validSubscriptionKey) +//{ +// // valid until 2 March 2049 +// LicenseManager LicenseManager; +// String painText("{v1;trial;Bob;1;a@a.a;mock company;2147483647;2147483647}"); +// SubscriptionKey key; +// LicenseManager.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("mock company", key.m_company); +// EXPECT_EQ(2147483647, key.m_warnTime); +// EXPECT_EQ(2147483647, key.m_expireTime); +//} +// +//TEST(SubscriptionTests, parsePlainSerial_validSerialWithoutCompany_validSubscriptionKey) +//{ +// // valid until 2 March 2049 +// LicenseManager LicenseManager; +// String painText("{v1;trial;Bob;1;a@a.a;;2147483647;2147483647}"); +// SubscriptionKey key; +// LicenseManager.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) +//{ +// LicenseManager LicenseManager; +// String painText("{v1;trial;Bob;1;1398297600;1398384000}"); +// SubscriptionKey key; +// +// EXPECT_THROW(LicenseManager.parsePlainSerial(painText, key), XSubscription); +//} +// +//TEST(SubscriptionTests, parsePlainSerial_expiredBasicSerial_validSubscriptionKey) +//{ +// LicenseManager LicenseManager; +// String painText("{v1;basic;Bob;1;a@a.a;mock company;1398297600;1398384000}"); +// SubscriptionKey key; +// LicenseManager.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); +//} From 92a885524bfc2d47c6505fffa7c5540a691adae0 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 13 Oct 2016 14:00:19 +0100 Subject: [PATCH 117/241] #5657 Added temporary implementation for SerialKey --- src/lib/shared/SerialKey.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++ src/lib/shared/SerialKey.h | 4 +-- 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 src/lib/shared/SerialKey.cpp diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp new file mode 100644 index 000000000..2b5f60401 --- /dev/null +++ b/src/lib/shared/SerialKey.cpp @@ -0,0 +1,60 @@ +/* + * 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" + +SerialKey::SerialKey(std::string serial) : + m_userLimit(1), + m_warnTime(1), + m_expireTime(1), + m_trial(true) +{ + m_userLimit = 1; + m_warnTime = 1; + m_expireTime = 1; + m_trial = true; +} + +bool +SerialKey::isValid(unsigned long long currentTime) const +{ + return true; +} + +bool +SerialKey::isExpiring(unsigned long long currentTime) const +{ + return true; +} + +bool +SerialKey::isExpired(unsigned long long currentTime) const +{ + return true; +} + +bool +SerialKey::isTrial() const +{ + return true; +} + +int +SerialKey::edition() const +{ + return 1; +} diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h index 45d9728ee..d9101f41b 100644 --- a/src/lib/shared/SerialKey.h +++ b/src/lib/shared/SerialKey.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Seamless Inc. + * 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 @@ -31,7 +31,7 @@ class SerialKey { private: std::string m_name; - std::string m_type; + std::string m_edition; std::string m_email; std::string m_company; int m_userLimit; From 2a452307cd00181cc95a6e9e60124f6142bf0398 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 13 Oct 2016 17:55:09 +0100 Subject: [PATCH 118/241] #5657 Fleshed out the implementation of SerialKey --- src/lib/shared/SerialKey.cpp | 167 +++++++++++++++++++++++++++++++++++++++---- src/lib/shared/SerialKey.h | 35 +++++++-- 2 files changed, 185 insertions(+), 17 deletions(-) diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index 2b5f60401..5f50045ef 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -17,44 +17,185 @@ #include "SerialKey.h" +#include + +using namespace std; + SerialKey::SerialKey(std::string serial) : m_userLimit(1), - m_warnTime(1), - m_expireTime(1), - m_trial(true) + m_warnTime(0), + m_expireTime(0), + m_edition(kBasic), + m_trial(true), + m_valid(false) { - m_userLimit = 1; - m_warnTime = 1; - m_expireTime = 1; - m_trial = true; + string plainText = decode(serial); + if (!plainText.empty()) { + parse(serial); + } } - + bool SerialKey::isValid(unsigned long long currentTime) const { - return true; + bool result = false; + + if (m_valid) { + if (m_trial) { + if (currentTime < m_expireTime) { + result = true; + } + } + else { + result = true; + } + } + + return result; } bool SerialKey::isExpiring(unsigned long long currentTime) const { - return true; + bool result = false; + + if (m_valid) { + if (m_warnTime < currentTime && currentTime < m_expireTime) { + result = true; + } + } + + return result; } bool SerialKey::isExpired(unsigned long long currentTime) const { - return true; + bool result = false; + + if (m_valid) { + if (currentTime > m_expireTime) { + result = true; + } + } + + return result; } bool SerialKey::isTrial() const { - return true; + return m_trial; } int SerialKey::edition() const { - return 1; + return m_edition; +} + +unsigned long long +SerialKey::dayLeft(unsigned long long currentTime) const +{ + unsigned long long timeLeft = 0; + if (m_expireTime > currentTime) { + timeLeft = m_expireTime - currentTime; + } + + unsigned long long day = 60 * 60 * 24; + + return timeLeft / day; +} + +std::string +SerialKey::decode(const std::string& serial) const +{ + static const char* const lut = "0123456789ABCDEF"; + 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 char* p = std::lower_bound(lut, lut + 16, a); + const char* 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; +} + +void +SerialKey::parse(std::string plainSerial) +{ + string parityStart = plainSerial.substr(0, 1); + string parityEnd = plainSerial.substr(plainSerial.length() - 1, 1); + + // check for parity chars { and }, record parity result, then remove them. + if (parityStart == "{" && parityEnd == "}") { + plainSerial = plainSerial.substr(1, plainSerial.length() - 2); + + // tokenize serialised subscription. + vector parts; + std::string::size_type pos = 0; + bool look = true; + while (look) { + std::string::size_type start = pos; + pos = plainSerial.find(";", pos); + if (pos == string::npos) { + pos = plainSerial.length(); + look = false; + } + parts.push_back(plainSerial.substr(start, pos - start)); + pos += 1; + } + + if ((parts.size() == 8) + && (parts.at(0).find("v1") != string::npos)) { + // e.g.: {v1;basic;Bob;1;email;company name;1398297600;1398384000} + m_edition = getEdition(parts.at(1)); + m_name = parts.at(2); + sscanf(parts.at(3).c_str(), "%d", &m_userLimit); + m_email = parts.at(4); + m_company = parts.at(5); + sscanf(parts.at(6).c_str(), "%lld", &m_warnTime); + sscanf(parts.at(7).c_str(), "%lld", &m_expireTime); + m_valid = true; + } + else if ((parts.size() == 9) + && (parts.at(0).find("v2") != string::npos)) { + // e.g.: {v2;trial;basic;Bob;1;email;company name;1398297600;1398384000} + m_trial = parts.at(1) == "trial" ? true : false; + m_edition = getEdition(parts.at(2)); + m_name = parts.at(3); + sscanf(parts.at(4).c_str(), "%d", &m_userLimit); + m_email = parts.at(5); + m_company = parts.at(6); + sscanf(parts.at(7).c_str(), "%lld", &m_warnTime); + sscanf(parts.at(8).c_str(), "%lld", &m_expireTime); + m_valid = true; + } + } +} + +Edition +SerialKey::getEdition(std::string editionStr) +{ + Edition e = kBasic; + if (editionStr == "pro") { + e = kPro; + } + + return e; } diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h index d9101f41b..d920ba530 100644 --- a/src/lib/shared/SerialKey.h +++ b/src/lib/shared/SerialKey.h @@ -19,6 +19,15 @@ #include +#ifdef TEST_ENV +#include "gtest/gtest_prod.h" +#endif + +enum Edition{ + kBasic, + kPro +}; + class SerialKey { public: SerialKey(std::string serial); @@ -28,14 +37,32 @@ class SerialKey { bool isExpired(unsigned long long currentTime) const; bool isTrial() const; int edition() const; - + unsigned long long dayLeft(unsigned long long currentTime) const; + +private: + std::string decode(const std::string& serial) const; + void parse(std::string plainSerial); + Edition getEdition(std::string editionStr); + +#ifdef TEST_ENV +private: + FRIEND_TEST(SerialKeyTests, decode_empty_returnEmptyString); + FRIEND_TEST(SerialKeyTests, decode_invalidDigit_returnEmptyString); + FRIEND_TEST(SerialKeyTests, decode_validSerial_returnPlainText); + FRIEND_TEST(SerialKeyTests, parse_noParty_invalid); + FRIEND_TEST(SerialKeyTests, parse_invalidPartsLenghth_invalid); + FRIEND_TEST(SerialKeyTests, parse_validV1Serial_valid); + FRIEND_TEST(SerialKeyTests, parse_validV2Serial_valid); +#endif + private: std::string m_name; - std::string m_edition; std::string m_email; std::string m_company; int m_userLimit; - int m_warnTime; - int m_expireTime; + unsigned long long m_warnTime; + unsigned long long m_expireTime; + Edition m_edition; bool m_trial; + bool m_valid; }; From 235f528dd98fed31a3149ac5bae304814815fea5 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 13 Oct 2016 17:55:38 +0100 Subject: [PATCH 119/241] #5657 Added unit tests for SerialKey --- src/lib/shared/CMakeLists.txt | 6 ++ src/test/unittests/CMakeLists.txt | 2 +- src/test/unittests/shared/SerialKeyTests.cpp | 80 ++++++++++++++++ src/test/unittests/synergy/SubscriptionTests.cpp | 114 ----------------------- 4 files changed, 87 insertions(+), 115 deletions(-) create mode 100644 src/test/unittests/shared/SerialKeyTests.cpp delete mode 100644 src/test/unittests/synergy/SubscriptionTests.cpp diff --git a/src/lib/shared/CMakeLists.txt b/src/lib/shared/CMakeLists.txt index 042d866cd..891f4aa78 100644 --- a/src/lib/shared/CMakeLists.txt +++ b/src/lib/shared/CMakeLists.txt @@ -22,5 +22,11 @@ endif() add_library(shared STATIC ${sources}) +include_directories( + ../ + ../../../ext + ../../../ext/gtest-1.6.0/include +) + target_link_libraries(shared arch base) diff --git a/src/test/unittests/CMakeLists.txt b/src/test/unittests/CMakeLists.txt index 4cdab9bf1..3e49dc3c8 100644 --- a/src/test/unittests/CMakeLists.txt +++ b/src/test/unittests/CMakeLists.txt @@ -68,4 +68,4 @@ endif() add_executable(unittests ${sources}) target_link_libraries(unittests - arch base client server common io net platform server synergy mt ipc gtest gmock ${libs} ${OPENSSL_LIBS}) + arch base client server common io net platform server synergy mt ipc gtest gmock shared ${libs} ${OPENSSL_LIBS}) diff --git a/src/test/unittests/shared/SerialKeyTests.cpp b/src/test/unittests/shared/SerialKeyTests.cpp new file mode 100644 index 000000000..591994353 --- /dev/null +++ b/src/test/unittests/shared/SerialKeyTests.cpp @@ -0,0 +1,80 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless 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 . + */ + +#define TEST_ENV + +#include "shared/SerialKey.h" + +#include "test/global/gtest.h" + +TEST(SerialKeyTests, decode_empty_returnEmptyString) +{ + SerialKey serial(""); + std::string plainText = serial.decode(""); + EXPECT_EQ(0, plainText.size()); +} + +TEST(SerialKeyTests, decode_invalidDigit_returnEmptyString) +{ + SerialKey serial(""); + std::string plainText = serial.decode("MOCKZ"); + EXPECT_EQ(0, plainText.size()); +} + +TEST(SerialKeyTests, decode_validSerial_returnPlainText) +{ + SerialKey serial(""); + std::string plainText = serial.decode("53796E6572677920726F636B7321"); + EXPECT_EQ("Synergy rocks!", plainText); +} + +TEST(SerialKeyTests, parse_noParty_invalid) +{ + SerialKey serial(""); + serial.parse("MOCK"); + EXPECT_FALSE(serial.isValid(0)); +} + +TEST(SerialKeyTests, parse_invalidPartsLenghth_invalid) +{ + SerialKey serial(""); + serial.parse("{Synergy;Rocks}"); + EXPECT_FALSE(serial.isValid(0)); +} + +TEST(SerialKeyTests, parse_validV1Serial_valid) +{ + SerialKey serial(""); + serial.parse("{v1;basic;Bob;1;email;company name;0;86400}"); + EXPECT_EQ(true, serial.isValid(0)); + EXPECT_EQ(kBasic, serial.edition()); + EXPECT_FALSE(serial.isExpired(0)); + EXPECT_EQ(true, serial.dayLeft(0)); + EXPECT_EQ(true, serial.isExpiring(1)); +} + +TEST(SerialKeyTests, parse_validV2Serial_valid) +{ + SerialKey serial(""); + serial.parse("{v2;trial;pro;Bob;1;email;company name;0;86400}"); + EXPECT_EQ(true, serial.isValid(0)); + EXPECT_EQ(kPro, serial.edition()); + EXPECT_FALSE(serial.isExpired(0)); + EXPECT_EQ(true, serial.dayLeft(0)); + EXPECT_EQ(true, serial.isExpiring(1)); + EXPECT_EQ(true, serial.isTrial()); +} diff --git a/src/test/unittests/synergy/SubscriptionTests.cpp b/src/test/unittests/synergy/SubscriptionTests.cpp deleted file mode 100644 index 2cab31b93..000000000 --- a/src/test/unittests/synergy/SubscriptionTests.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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 . - */ -// -//#include "synergy/LicenseManager.h" -//#include "synergy/XSynergy.h" -// -//#include "test/global/gtest.h" -// -//TEST(SubscriptionTests, decode_invalidLength_throwException) -//{ -// LicenseManager LicenseManager; -// String serial("ABC"); -// -// EXPECT_THROW(LicenseManager.decode(serial), XSubscription); -//} -// -//TEST(SubscriptionTests, decode_unrecognizedDigit_throwException) -//{ -// LicenseManager LicenseManager; -// String serial("MOCK"); -// -// EXPECT_THROW(LicenseManager.decode(serial), XSubscription); -//} -// -//TEST(SubscriptionTests, parsePlainSerial_noParity_throwException) -//{ -// LicenseManager LicenseManager; -// String painText("MOCK"); -// SubscriptionKey key; -// -// EXPECT_THROW(LicenseManager.parsePlainSerial(painText, key), XSubscription); -//} -// -//TEST(SubscriptionTests, parsePlainSerial_invalidSerial_throwException) -//{ -// LicenseManager LicenseManager; -// String painText("{MOCK}"); -// SubscriptionKey key; -// -// EXPECT_THROW(LicenseManager.parsePlainSerial(painText, key), XSubscription); -//} -// -//TEST(SubscriptionTests, parsePlainSerial_validSerial_validSubscriptionKey) -//{ -// // valid until 2 March 2049 -// LicenseManager LicenseManager; -// String painText("{v1;trial;Bob;1;a@a.a;mock company;2147483647;2147483647}"); -// SubscriptionKey key; -// LicenseManager.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("mock company", key.m_company); -// EXPECT_EQ(2147483647, key.m_warnTime); -// EXPECT_EQ(2147483647, key.m_expireTime); -//} -// -//TEST(SubscriptionTests, parsePlainSerial_validSerialWithoutCompany_validSubscriptionKey) -//{ -// // valid until 2 March 2049 -// LicenseManager LicenseManager; -// String painText("{v1;trial;Bob;1;a@a.a;;2147483647;2147483647}"); -// SubscriptionKey key; -// LicenseManager.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) -//{ -// LicenseManager LicenseManager; -// String painText("{v1;trial;Bob;1;1398297600;1398384000}"); -// SubscriptionKey key; -// -// EXPECT_THROW(LicenseManager.parsePlainSerial(painText, key), XSubscription); -//} -// -//TEST(SubscriptionTests, parsePlainSerial_expiredBasicSerial_validSubscriptionKey) -//{ -// LicenseManager LicenseManager; -// String painText("{v1;basic;Bob;1;a@a.a;mock company;1398297600;1398384000}"); -// SubscriptionKey key; -// LicenseManager.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); -//} From 719e64dc8f5ab82bb424333b0a38e14fc585a2a2 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 13 Oct 2016 18:07:21 +0100 Subject: [PATCH 120/241] #5657 Added missing dependencies on Linux and Windows --- src/lib/shared/SerialKey.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index 5f50045ef..54a45f86f 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -17,6 +17,9 @@ #include "SerialKey.h" +#include +#include +#include #include using namespace std; From fd8e778b2a86d692248b9fa03f99d9f846836ff7 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 13 Oct 2016 14:49:53 +0100 Subject: [PATCH 121/241] #5657 Fix serial key file path in GUI --- src/gui/src/CoreInterface.cpp | 14 +++++++++++--- src/gui/src/CoreInterface.h | 2 +- src/gui/src/SubscriptionManager.cpp | 4 ++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index d537a24f7..b06a5d901 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -22,10 +22,18 @@ #include #include +#include +#include #include static const char kCoreBinary[] = "syntool"; +#ifdef Q_WS_WIN +static const char kSerialKeyFilename[] = "Synergy.subkey"; +#else +static const char kSerialKeyFilename[] = ".synergy.subkey"; +#endif + CoreInterface::CoreInterface() { } @@ -54,10 +62,10 @@ QString CoreInterface::getArch() return run(args); } -QString CoreInterface::getSubscriptionFilename() +QString CoreInterface::getSerialKeyFilePath() { - QStringList args("--get-subscription-filename"); - return run(args); + QString filename = getProfileDir() + QDir::separator() + kSerialKeyFilename; + return filename; } QString CoreInterface::activateSerial(const QString& serial) diff --git a/src/gui/src/CoreInterface.h b/src/gui/src/CoreInterface.h index 13e8fd873..cd61ae252 100644 --- a/src/gui/src/CoreInterface.h +++ b/src/gui/src/CoreInterface.h @@ -28,7 +28,7 @@ class CoreInterface QString getProfileDir(); QString getInstalledDir(); QString getArch(); - QString getSubscriptionFilename(); + QString getSerialKeyFilePath(); QString activateSerial(const QString& serial); QString checkSubscription(); QString notifyActivation(const QString& identity); diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index 77e0a91c1..cc842be36 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -85,9 +85,9 @@ bool SubscriptionManager::checkSubscription() bool SubscriptionManager::fileExists() { CoreInterface coreInterface; - QString subscriptionFilename = coreInterface.getSubscriptionFilename(); + QString serialKeyFilePath = coreInterface.getSerialKeyFilePath(); - return QFile::exists(subscriptionFilename); + return QFile::exists(serialKeyFilePath); } void SubscriptionManager::checkError(QString& error) From 743e96f2778f31247771ac07dbf3e4634ff7f40a Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 13 Oct 2016 15:13:12 +0100 Subject: [PATCH 122/241] #5657 Don't store activation email --- src/gui/gui.pro | 6 ++++-- src/gui/src/ActivationDialog.cpp | 2 +- src/gui/src/AppConfig.cpp | 8 -------- src/gui/src/AppConfig.h | 2 -- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/gui/gui.pro b/src/gui/gui.pro index c0bf4fad0..836e5ec50 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -64,7 +64,8 @@ SOURCES += src/main.cpp \ src/ActivationNotifier.cpp \ src/ActivationDialog.cpp \ src/CancelActivationDialog.cpp \ - src/FailedLoginDialog.cpp + src/FailedLoginDialog.cpp \ + ../lib/shared/SerialKey.cpp HEADERS += src/MainWindow.h \ src/AboutDialog.h \ src/ServerConfig.h \ @@ -112,7 +113,8 @@ HEADERS += src/MainWindow.h \ src/ElevateMode.h \ src/ActivationDialog.h \ src/CancelActivationDialog.h \ - src/FailedLoginDialog.h + src/FailedLoginDialog.h \ + ../lib/shared/SerialKey.h RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc macx { diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 9f2e3d51d..fc6715012 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -77,7 +77,7 @@ void ActivationDialog::accept() if (!subscriptionManager.activateSerial(serialKey)) { return; } - m_appConfig->setActivateEmail(""); + notifyActivation("serial:" + m_appConfig->serialKey()); } diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 9848e6b45..059cca59a 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -184,7 +184,6 @@ void AppConfig::saveSettings() settings().setValue("elevateModeEnum", static_cast(m_ElevateMode)); settings().setValue("autoConfigPrompted", m_AutoConfigPrompted); settings().setValue("edition", m_Edition); - settings().setValue("activateEmail", m_ActivateEmail); settings().setValue("cryptoEnabled", m_CryptoEnabled); settings().setValue("autoHide", m_AutoHide); settings().setValue("serialKey", m_Serialkey); @@ -245,13 +244,6 @@ void AppConfig::setEdition(int e) { int AppConfig::edition() const { return m_Edition; } -bool AppConfig::setActivateEmail(QString e) { - m_ActivateEmail = e; - return true; -} - -QString AppConfig::activateEmail() { return m_ActivateEmail; } - bool AppConfig::setSerialKey(QString serial, QString& errorOut) { if (serial.isEmpty()) { errorOut = "Your serial key cannot be blank."; diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 271ef6834..9ebf2c997 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -79,8 +79,6 @@ class AppConfig: public QObject void setAutoConfigPrompted(bool prompted); void setEdition(int e); int edition() const; - bool setActivateEmail(QString e); - QString activateEmail(); bool setSerialKey(QString serial, QString& error); void clearSerialKey(); QString serialKey(); From 540882056f770e3dcc2e9d4c6489afadbdbd86da Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 14 Oct 2016 11:28:37 +0100 Subject: [PATCH 123/241] #5657 Create a global SubscriptionManager instance --- src/gui/gui.pro | 5 +- src/gui/src/ActivationDialog.cpp | 19 ++-- src/gui/src/ActivationDialog.h | 5 +- src/gui/src/AppConfig.cpp | 11 +-- src/gui/src/AppConfig.h | 2 +- src/gui/src/MainWindow.cpp | 58 ++++++------ src/gui/src/MainWindow.h | 10 +- src/gui/src/QUtility.cpp | 3 - src/gui/src/SubscriptionManager.cpp | 150 ++---------------------------- src/gui/src/SubscriptionManager.h | 32 +++---- src/gui/src/main.cpp | 6 +- src/{gui/src => lib/shared}/EditionType.h | 4 +- src/lib/shared/SerialKey.cpp | 25 +++-- src/lib/shared/SerialKey.h | 35 ++++--- 14 files changed, 117 insertions(+), 248 deletions(-) rename src/{gui/src => lib/shared}/EditionType.h (93%) diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 836e5ec50..e56926bcb 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -7,7 +7,8 @@ DEFINES += VERSION_REVISION=\\\"$$QMAKE_VERSION_REVISION\\\" DEPENDPATH += . \ res INCLUDEPATH += . \ - src + src \ + ../lib/shared/ FORMS += res/MainWindowBase.ui \ res/AboutDialogBase.ui \ res/ServerConfigDialogBase.ui \ @@ -102,7 +103,6 @@ HEADERS += src/MainWindow.h \ src/DataDownloader.h \ src/AddClientDialog.h \ src/CommandProcess.h \ - src/EditionType.h \ src/ProcessorArch.h \ src/CoreInterface.h \ src/Fingerprint.h \ @@ -114,6 +114,7 @@ HEADERS += src/MainWindow.h \ src/ActivationDialog.h \ src/CancelActivationDialog.h \ src/FailedLoginDialog.h \ + ../lib/shared/EditionType.h \ ../lib/shared/SerialKey.h RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index fc6715012..424897267 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -14,10 +14,12 @@ #include #include -ActivationDialog::ActivationDialog(QWidget* parent, AppConfig& appConfig) : +ActivationDialog::ActivationDialog(QWidget* parent, AppConfig& appConfig, + SubscriptionManager& subscriptionManager) : QDialog(parent), ui(new Ui::ActivationDialog), - m_appConfig(&appConfig) + m_appConfig(&appConfig), + m_subscriptionManager (&subscriptionManager) { ui->setupUi(this); ui->m_pTextEditSerialKey->setFocus(); @@ -67,17 +69,8 @@ void ActivationDialog::accept() try { QString serialKey = ui->m_pTextEditSerialKey->toPlainText(); - - if (!m_appConfig->setSerialKey(serialKey, error)) { - message.critical(this, "Invalid Serial Key", tr("%1").arg(error)); - return; - } - - SubscriptionManager subscriptionManager(this, *m_appConfig, edition); - if (!subscriptionManager.activateSerial(serialKey)) { - return; - } - + SubscriptionManager subscriptionManager (m_appConfig); + subscriptionManager.setSerialKey (serialKey); notifyActivation("serial:" + m_appConfig->serialKey()); } diff --git a/src/gui/src/ActivationDialog.h b/src/gui/src/ActivationDialog.h index 0f3328ac9..a1ece9a36 100644 --- a/src/gui/src/ActivationDialog.h +++ b/src/gui/src/ActivationDialog.h @@ -2,6 +2,7 @@ #define ACTIVATIONDIALOG_H #include +#include namespace Ui { class ActivationDialog; @@ -14,7 +15,8 @@ class ActivationDialog : public QDialog Q_OBJECT public: - explicit ActivationDialog(QWidget *parent, AppConfig& appConfig); + ActivationDialog(QWidget *parent, AppConfig& appConfig, + SubscriptionManager& subscriptionManager); ~ActivationDialog(); public slots: @@ -27,6 +29,7 @@ public slots: private: Ui::ActivationDialog *ui; AppConfig* m_appConfig; + SubscriptionManager* m_subscriptionManager; }; #endif // ACTIVATIONDIALOG_H diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 059cca59a..22aec72c5 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -244,13 +244,10 @@ void AppConfig::setEdition(int e) { int AppConfig::edition() const { return m_Edition; } -bool AppConfig::setSerialKey(QString serial, QString& errorOut) { - if (serial.isEmpty()) { - errorOut = "Your serial key cannot be blank."; - return false; - } - m_Serialkey = serial; - return true; +QString AppConfig::setSerialKey(QString serial) { + using std::swap; + swap (serial, m_Serialkey); + return serial; } void AppConfig::clearSerialKey() diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 9ebf2c997..f62af1d21 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -79,7 +79,7 @@ class AppConfig: public QObject void setAutoConfigPrompted(bool prompted); void setEdition(int e); int edition() const; - bool setSerialKey(QString serial, QString& error); + QString setSerialKey(QString serial); void clearSerialKey(); QString serialKey(); int lastExpiringWarningTime() const; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index c927839f5..8165a43ef 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -76,12 +76,14 @@ static const char* synergyIconFiles[] = ":/res/icons/16x16/synergy-transfering.png" }; -MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : +MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig, + SubscriptionManager& subscriptionManager) : m_Settings(settings), - m_AppConfig(appConfig), + m_AppConfig(&appConfig), + m_SubscriptionManager(&subscriptionManager), m_pSynergy(NULL), m_SynergyState(synergyDisconnected), - m_ServerConfig(&m_Settings, 5, 3, m_AppConfig.screenName(), this), + m_ServerConfig(&m_Settings, 5, 3, m_AppConfig->screenName(), this), m_pTempConfigFile(NULL), m_pTrayIcon(NULL), m_pTrayIconMenu(NULL), @@ -135,12 +137,12 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : m_pComboServerList->hide(); - setEdition(m_AppConfig.edition()); + setEdition(m_AppConfig->edition()); m_pLabelPadlock->hide(); connect (this, SIGNAL(windowShown()), this, SLOT(on_windowShown()), Qt::QueuedConnection); - connect (&m_AppConfig, SIGNAL(editionSet(int)), this, SLOT(setEdition(int)), Qt::QueuedConnection); - connect (&m_AppConfig, SIGNAL(sslToggled(bool)), this, SLOT(sslToggled(bool)), Qt::QueuedConnection); + connect (m_AppConfig, SIGNAL(editionSet(int)), this, SLOT(setEdition(int)), Qt::QueuedConnection); + connect (m_AppConfig, SIGNAL(sslToggled(bool)), this, SLOT(sslToggled(bool)), Qt::QueuedConnection); } MainWindow::~MainWindow() @@ -498,7 +500,7 @@ void MainWindow::restartSynergy() void MainWindow::proofreadInfo() { - setEdition(m_AppConfig.edition()); // Why is this here? + setEdition(m_AppConfig->edition()); // Why is this here? int oldState = m_SynergyState; m_SynergyState = synergyDisconnected; @@ -566,7 +568,7 @@ void MainWindow::startSynergy() #endif - if (m_AppConfig.getCryptoEnabled()) { + if (m_AppConfig->getCryptoEnabled()) { args << "--enable-crypto"; } @@ -734,18 +736,6 @@ QString MainWindow::appPath(const QString& name) bool MainWindow::serverArgs(QStringList& args, QString& app) { - int edition; - SubscriptionManager subscriptionManager(this, appConfig(), edition); - if (subscriptionManager.fileExists()) - { - if (!subscriptionManager.checkSubscription()) { - return false; - } - else { - setEdition(edition); - } - } - app = appPath(appConfig().synergysName()); if (!QFile::exists(app)) @@ -894,7 +884,7 @@ void MainWindow::setSynergyState(qSynergyState state) switch (state) { case synergyConnected: { - if (m_AppConfig.getCryptoEnabled()) { + if (m_AppConfig->getCryptoEnabled()) { m_pLabelPadlock->show(); } else { @@ -1009,13 +999,13 @@ void MainWindow::updateZeroconfService() QMutexLocker locker(&m_UpdateZeroconfMutex); if (isBonjourRunning()) { - if (!m_AppConfig.wizardShouldRun()) { + if (!m_AppConfig->wizardShouldRun()) { if (m_pZeroconfService) { delete m_pZeroconfService; m_pZeroconfService = NULL; } - if (m_AppConfig.autoConfig() || synergyType() == synergyServer) { + if (m_AppConfig->autoConfig() || synergyType() == synergyServer) { m_pZeroconfService = new ZeroconfService(this); } } @@ -1037,7 +1027,7 @@ void MainWindow::serverDetected(const QString name) void MainWindow::setEdition(int edition) { setWindowTitle(getEditionName(edition)); - if (m_AppConfig.getCryptoEnabled()) { + if (m_AppConfig->getCryptoEnabled()) { m_pSslCertificate = new SslCertificate(this); m_pSslCertificate->generateCertificate(); } @@ -1047,7 +1037,7 @@ void MainWindow::setEdition(int edition) void MainWindow::updateLocalFingerprint() { - if (m_AppConfig.getCryptoEnabled() && Fingerprint::local().fileExists()) { + if (m_AppConfig->getCryptoEnabled() && Fingerprint::local().fileExists()) { m_pLabelFingerprint->setVisible(true); m_pLabelLocalFingerprint->setVisible(true); m_pLabelLocalFingerprint->setText(Fingerprint::local().readFirst()); @@ -1058,6 +1048,12 @@ void MainWindow::updateLocalFingerprint() } } +SubscriptionManager& +MainWindow::subscriptionManager() const +{ + return *m_SubscriptionManager; +} + void MainWindow::on_m_pGroupClient_toggled(bool on) { m_pGroupServer->setChecked(!on); @@ -1159,7 +1155,7 @@ void MainWindow::on_m_pButtonConfigureServer_clicked() void MainWindow::on_m_pActivate_triggered() { - ActivationDialog activationDialog (this, this->appConfig()); + ActivationDialog activationDialog(this, appConfig(), subscriptionManager()); activationDialog.exec(); } @@ -1320,16 +1316,16 @@ void MainWindow::promptAutoConfig() QMessageBox::Yes | QMessageBox::No); if (r == QMessageBox::Yes) { - m_AppConfig.setAutoConfig(true); + m_AppConfig->setAutoConfig(true); downloadBonjour(); } else { - m_AppConfig.setAutoConfig(false); + m_AppConfig->setAutoConfig(false); m_pCheckBoxAutoConfig->setChecked(false); } } - m_AppConfig.setAutoConfigPrompted(true); + m_AppConfig->setAutoConfigPrompted(true); } void MainWindow::on_m_pComboServerList_currentIndexChanged(QString ) @@ -1377,8 +1373,8 @@ void MainWindow::bonjourInstallFinished() void MainWindow::on_windowShown() { - if (!m_AppConfig.activationHasRun() && (m_AppConfig.edition() == Unregistered)) { - ActivationDialog activationDialog (this, m_AppConfig); + if (!m_AppConfig->activationHasRun() && (m_AppConfig->edition() == Unregistered)) { + ActivationDialog activationDialog (this, appConfig(), subscriptionManager()); activationDialog.exec(); } } diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index efd83dcca..1dd678ba7 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -58,6 +58,7 @@ class ZeroconfService; class DataDownloader; class CommandProcess; class SslCertificate; +class SubscriptionManager; class MainWindow : public QMainWindow, public Ui::MainWindowBase { @@ -94,7 +95,8 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase }; public: - MainWindow(QSettings& settings, AppConfig& appConfig); + MainWindow(QSettings& settings, AppConfig& appConfig, + SubscriptionManager& subscriptionManager); ~MainWindow(); public: @@ -116,6 +118,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase void updateZeroconfService(); void serverDetected(const QString name); void updateLocalFingerprint(); + SubscriptionManager& subscriptionManager() const; public slots: void setEdition(int edition); @@ -145,7 +148,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase protected: QSettings& settings() { return m_Settings; } - AppConfig& appConfig() { return m_AppConfig; } + AppConfig& appConfig() { return *m_AppConfig; } QProcess* synergyProcess() { return m_pSynergy; } void setSynergyProcess(QProcess* p) { m_pSynergy = p; } void initConnections(); @@ -188,7 +191,8 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase private: QSettings& m_Settings; - AppConfig& m_AppConfig; + AppConfig* m_AppConfig; + SubscriptionManager* m_SubscriptionManager; QProcess* m_pSynergy; int m_SynergyState; ServerConfig m_ServerConfig; diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index f463f1d2f..322b2fd1d 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -51,9 +51,6 @@ getEditionName (int edition) { else if (edition == Pro) { return "Synergy Pro"; } - else if (edition == Trial) { - return "Synergy Trial"; - } else { return "Synergy (UNREGISTERED)"; } diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index cc842be36..0f3728af5 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -16,152 +16,20 @@ */ #include "SubscriptionManager.h" - - -#include "CoreInterface.h" #include "EditionType.h" #include "AppConfig.h" +#include -#include -#include -#include -#include -#include - -static const char purchaseURL[] = "https://symless.com/account/"; - -SubscriptionManager::SubscriptionManager(QWidget* parent, AppConfig& appConfig, int& edition) : - m_pParent(parent), - m_AppConfig(appConfig), - m_Edition(edition) -{ -} - -bool SubscriptionManager::activateSerial(const QString& serial) -{ - m_Edition = Unregistered; - persistDirectory(); - CoreInterface coreInterface; - QString output; - - try - { - output = coreInterface.activateSerial(serial); - } - catch (std::exception& e) - { - m_ErrorMessage = e.what(); - checkError(m_ErrorMessage); - return false; - } - - checkOutput(output); - - return true; +SubscriptionManager::SubscriptionManager(AppConfig* appConfig) : + m_AppConfig(appConfig) { } -bool SubscriptionManager::checkSubscription() +void +SubscriptionManager::setSerialKey(QString serialKeyString) { - m_Edition = Unregistered; - persistDirectory(); - CoreInterface coreInterface; - QString output; - try - { - output = coreInterface.checkSubscription(); - } - catch (std::exception& e) - { - m_ErrorMessage = e.what(); - checkError(m_ErrorMessage); - return false; - } - - checkOutput(output); - - return true; -} - -bool SubscriptionManager::fileExists() -{ - CoreInterface coreInterface; - QString serialKeyFilePath = coreInterface.getSerialKeyFilePath(); - - return QFile::exists(serialKeyFilePath); -} - -void SubscriptionManager::checkError(QString& error) -{ - 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 Synergy using your 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 result; -} - -void SubscriptionManager::persistDirectory() -{ - CoreInterface coreInterface; - QString profileDir = coreInterface.getProfileDir(); - - QDir dir(profileDir); - if (!dir.exists()) { - dir.mkpath("."); + SerialKey serialKey (serialKeyString.toStdString()); + if (serialKey.isValid (::time(0)) && (serialKey != m_serialKey)) { + m_AppConfig->setSerialKey (serialKeyString); + emit serialKeyChanged (serialKey); } } diff --git a/src/gui/src/SubscriptionManager.h b/src/gui/src/SubscriptionManager.h index 594973521..29b9cd952 100644 --- a/src/gui/src/SubscriptionManager.h +++ b/src/gui/src/SubscriptionManager.h @@ -17,31 +17,23 @@ #pragma once -#include +#include +#include class AppConfig; -class SubscriptionManager : public QWidget +class SubscriptionManager: public QObject { -public: - SubscriptionManager(QWidget* parent, AppConfig& appConfig, int& edition); - - bool activateSerial(const QString& serial); - bool checkSubscription(); - bool fileExists(); - QString getLastError(){ return m_ErrorMessage; } + Q_OBJECT -private: - void checkError(QString& error); - void checkOutput(QString& output); - void getEditionType(QString& output); - void checkExpiring(QString& output); - bool shouldWarnExpiring(); - void persistDirectory(); +public: + SubscriptionManager (AppConfig* appConfig); + void setSerialKey (QString serialKey); private: - QString m_ErrorMessage; - QWidget* m_pParent; - AppConfig& m_AppConfig; - int& m_Edition; + AppConfig* m_AppConfig; + SerialKey m_serialKey; + +signals: + void serialKeyChanged (SerialKey); }; diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index 0ea2cbfee..79131f369 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -20,6 +20,7 @@ #define TRAY_RETRY_WAIT 2000 #include "QSynergyApplication.h" +#include "SubscriptionManager.h" #include "MainWindow.h" #include "AppConfig.h" #include "SetupWizard.h" @@ -82,11 +83,12 @@ int main(int argc, char* argv[]) #endif QSettings settings; - AppConfig appConfig(&settings); + AppConfig appConfig (&settings); + SubscriptionManager subscriptionManager (&appConfig); app.switchTranslator(appConfig.language()); - MainWindow mainWindow(settings, appConfig); + MainWindow mainWindow(settings, appConfig, subscriptionManager); SetupWizard setupWizard(mainWindow, true); if (appConfig.wizardShouldRun()) diff --git a/src/gui/src/EditionType.h b/src/lib/shared/EditionType.h similarity index 93% rename from src/gui/src/EditionType.h rename to src/lib/shared/EditionType.h index 5869a32b4..ddd27ccfd 100644 --- a/src/gui/src/EditionType.h +++ b/src/lib/shared/EditionType.h @@ -20,10 +20,10 @@ /* Do not reorder these! */ -enum EditionType { +enum Edition { Basic, Pro, - Trial, + Trial_DO_NOT_USE_OR_THERE_WILL_BE_PAIN, Unregistered }; diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index 54a45f86f..52dd27435 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -24,11 +24,18 @@ using namespace std; +SerialKey::SerialKey(): + m_warnTime(1), + m_expireTime(1), + m_trial(true) +{ +} + SerialKey::SerialKey(std::string serial) : m_userLimit(1), m_warnTime(0), m_expireTime(0), - m_edition(kBasic), + m_edition(Edition::Basic), m_trial(true), m_valid(false) { @@ -39,7 +46,7 @@ SerialKey::SerialKey(std::string serial) : } bool -SerialKey::isValid(unsigned long long currentTime) const +SerialKey::isValid(time_t currentTime) const { bool result = false; @@ -58,7 +65,7 @@ SerialKey::isValid(unsigned long long currentTime) const } bool -SerialKey::isExpiring(unsigned long long currentTime) const +SerialKey::isExpiring(time_t currentTime) const { bool result = false; @@ -72,7 +79,7 @@ SerialKey::isExpiring(unsigned long long currentTime) const } bool -SerialKey::isExpired(unsigned long long currentTime) const +SerialKey::isExpired(time_t currentTime) const { bool result = false; @@ -91,14 +98,14 @@ SerialKey::isTrial() const return m_trial; } -int +Edition SerialKey::edition() const { return m_edition; } -unsigned long long -SerialKey::dayLeft(unsigned long long currentTime) const +time_t +SerialKey::daysLeft(time_t currentTime) const { unsigned long long timeLeft = 0; if (m_expireTime > currentTime) { @@ -195,9 +202,9 @@ SerialKey::parse(std::string plainSerial) Edition SerialKey::getEdition(std::string editionStr) { - Edition e = kBasic; + Edition e = Edition::Basic; if (editionStr == "pro") { - e = kPro; + e = Edition::Pro; } return e; diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h index d920ba530..df8df4799 100644 --- a/src/lib/shared/SerialKey.h +++ b/src/lib/shared/SerialKey.h @@ -18,31 +18,29 @@ #pragma once #include +#include +#include "EditionType.h" #ifdef TEST_ENV #include "gtest/gtest_prod.h" #endif -enum Edition{ - kBasic, - kPro -}; - class SerialKey { public: + SerialKey(); SerialKey(std::string serial); - bool isValid(unsigned long long currentTime) const; - bool isExpiring(unsigned long long currentTime) const; - bool isExpired(unsigned long long currentTime) const; + bool isValid(time_t currentTime) const; + bool isExpiring(time_t currentTime) const; + bool isExpired(time_t currentTime) const; bool isTrial() const; - int edition() const; - unsigned long long dayLeft(unsigned long long currentTime) const; + time_t daysLeft(time_t currentTime) const; + Edition edition() const; private: std::string decode(const std::string& serial) const; void parse(std::string plainSerial); - Edition getEdition(std::string editionStr); + Edition getEdition(std::string editionStr); #ifdef TEST_ENV private: @@ -59,10 +57,21 @@ class SerialKey { std::string m_name; std::string m_email; std::string m_company; - int m_userLimit; + unsigned m_userLimit; unsigned long long m_warnTime; unsigned long long m_expireTime; - Edition m_edition; + Edition m_edition; bool m_trial; bool m_valid; }; + + +inline bool +operator== (SerialKey const& lhs, SerialKey const& rhs) { + return (lhs.edition() == rhs.edition()); +} + +inline bool +operator!= (SerialKey const& lhs, SerialKey const& rhs) { + return !(lhs == rhs); +} From 727fc5c220415127e5e25b3a20c78093224d6e3e Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 14 Oct 2016 12:16:23 +0100 Subject: [PATCH 124/241] #5659 Move activation notification to SubscriptionManager --- src/gui/src/ActivationDialog.cpp | 23 ++--------------------- src/gui/src/ActivationDialog.h | 3 --- src/gui/src/SubscriptionManager.cpp | 23 +++++++++++++++++++++++ src/gui/src/SubscriptionManager.h | 6 ++++++ src/lib/shared/SerialKey.h | 6 +++--- 5 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 424897267..f070007c3 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -31,27 +31,11 @@ ActivationDialog::~ActivationDialog() delete ui; } -void ActivationDialog::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 ActivationDialog::reject() { CancelActivationDialog cancelActivationDialog(this); if (QDialog::Accepted == cancelActivationDialog.exec()) { - notifyActivation("skip:unknown"); + m_subscriptionManager->notifySkip(); m_appConfig->activationHasRun(true); m_appConfig->saveSettings(); QDialog::reject(); @@ -69,10 +53,7 @@ void ActivationDialog::accept() try { QString serialKey = ui->m_pTextEditSerialKey->toPlainText(); - SubscriptionManager subscriptionManager (m_appConfig); - subscriptionManager.setSerialKey (serialKey); - notifyActivation("serial:" + m_appConfig->serialKey()); - + m_subscriptionManager->setSerialKey(serialKey); } catch (std::exception& e) { message.critical(this, "Unknown Error", diff --git a/src/gui/src/ActivationDialog.h b/src/gui/src/ActivationDialog.h index a1ece9a36..09577ed5b 100644 --- a/src/gui/src/ActivationDialog.h +++ b/src/gui/src/ActivationDialog.h @@ -22,9 +22,6 @@ class ActivationDialog : public QDialog public slots: void reject(); void accept(); - -protected: - void notifyActivation (QString identity); private: Ui::ActivationDialog *ui; diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index 0f3728af5..fd2a77bbc 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -19,6 +19,7 @@ #include "EditionType.h" #include "AppConfig.h" #include +#include SubscriptionManager::SubscriptionManager(AppConfig* appConfig) : m_AppConfig(appConfig) { @@ -30,6 +31,28 @@ SubscriptionManager::setSerialKey(QString serialKeyString) SerialKey serialKey (serialKeyString.toStdString()); if (serialKey.isValid (::time(0)) && (serialKey != m_serialKey)) { m_AppConfig->setSerialKey (serialKeyString); + notifyActivation ("serial:" + serialKeyString); emit serialKeyChanged (serialKey); } } + +void SubscriptionManager::notifySkip() +{ + notifyActivation ("skip:unknown"); +} + +void SubscriptionManager::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); +} diff --git a/src/gui/src/SubscriptionManager.h b/src/gui/src/SubscriptionManager.h index 29b9cd952..2c79f6bdc 100644 --- a/src/gui/src/SubscriptionManager.h +++ b/src/gui/src/SubscriptionManager.h @@ -19,6 +19,7 @@ #include #include +#include class AppConfig; @@ -29,11 +30,16 @@ class SubscriptionManager: public QObject public: SubscriptionManager (AppConfig* appConfig); void setSerialKey (QString serialKey); + void notifySkip (); +private: + void notifyActivation (QString identity); + private: AppConfig* m_AppConfig; SerialKey m_serialKey; signals: void serialKeyChanged (SerialKey); + void editionChanged (Edition); }; diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h index df8df4799..2423cf557 100644 --- a/src/lib/shared/SerialKey.h +++ b/src/lib/shared/SerialKey.h @@ -35,12 +35,12 @@ class SerialKey { bool isExpired(time_t currentTime) const; bool isTrial() const; time_t daysLeft(time_t currentTime) const; - Edition edition() const; + Edition edition() const; private: std::string decode(const std::string& serial) const; void parse(std::string plainSerial); - Edition getEdition(std::string editionStr); + Edition getEdition(std::string editionStr); #ifdef TEST_ENV private: @@ -60,7 +60,7 @@ class SerialKey { unsigned m_userLimit; unsigned long long m_warnTime; unsigned long long m_expireTime; - Edition m_edition; + Edition m_edition; bool m_trial; bool m_valid; }; From 33ebe61ef21d13fea7a6c6c9858ad2303d595540 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 14 Oct 2016 12:43:01 +0100 Subject: [PATCH 125/241] #5657 Counted expiring within 1 day as 1 day left --- src/lib/shared/SerialKey.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index 54a45f86f..38ca87259 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -101,13 +101,16 @@ unsigned long long SerialKey::dayLeft(unsigned long long currentTime) const { unsigned long long timeLeft = 0; + unsigned long long day = 60 * 60 * 24; + if (m_expireTime > currentTime) { timeLeft = m_expireTime - currentTime; } - unsigned long long day = 60 * 60 * 24; + unsigned long long dayLeft = 0; + dayLeft = timeLeft % day != 0 ? 1 : 0; - return timeLeft / day; + return timeLeft / day + dayLeft; } std::string From dce4b382e676ff5df2810d57b79bb968f3f67e32 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 14 Oct 2016 12:43:33 +0100 Subject: [PATCH 126/241] #5657 Fixed passing in the raw serial key --- src/lib/shared/SerialKey.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index 38ca87259..23d92b2e7 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -34,7 +34,7 @@ SerialKey::SerialKey(std::string serial) : { string plainText = decode(serial); if (!plainText.empty()) { - parse(serial); + parse(plainText); } } From 1e5dfd3cb5f267874520289591a36b068846e0e3 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 14 Oct 2016 12:43:58 +0100 Subject: [PATCH 127/241] #5657 Added more unit tests for member functions in SerialKey --- src/test/unittests/shared/SerialKeyTests.cpp | 121 +++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/src/test/unittests/shared/SerialKeyTests.cpp b/src/test/unittests/shared/SerialKeyTests.cpp index 591994353..40fb479a1 100644 --- a/src/test/unittests/shared/SerialKeyTests.cpp +++ b/src/test/unittests/shared/SerialKeyTests.cpp @@ -78,3 +78,124 @@ TEST(SerialKeyTests, parse_validV2Serial_valid) EXPECT_EQ(true, serial.isExpiring(1)); EXPECT_EQ(true, serial.isTrial()); } + +TEST(SerialKeyTests, isValid_validV1BasicSerial_valid) +{ + // {v1;basic;Bob;1;email;company name;0;86400} + SerialKey serial("7B76313B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(true, serial.isValid(0)); + EXPECT_EQ(kBasic, serial.edition()); +} + +TEST(SerialKeyTests, isValid_expiredV1ProSerial_valid) +{ + // {v1;pro;Bob;1;email;company name;0;86400} + SerialKey serial("7B76313B70726F3B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(true, serial.isValid(0)); + EXPECT_EQ(kPro, serial.edition()); +} + +TEST(SerialKeyTests, isValid_validV2LifetimeBasicSerial_valid) +{ + // {v2;lifetime;basic;Bob;1;email;company name;0;86400} + SerialKey serial("7B76323B6C69666574696D653B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(true, serial.isValid(0)); + EXPECT_EQ(kBasic, serial.edition()); +} + +TEST(SerialKeyTests, isValid_validV2LifetimeProSerial_valid) +{ + // {v2;lifetime;pro;Bob;1;email;company name;0;86400} + SerialKey serial("7B76323B6C69666574696D653B70726F3B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(true, serial.isValid(0)); + EXPECT_EQ(kPro, serial.edition()); +} + +TEST(SerialKeyTests, isValid_validV2TrialBasicSerial_valid) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(true, serial.isTrial()); + EXPECT_EQ(true, serial.isValid(0)); + EXPECT_EQ(kBasic, serial.edition()); + +} + +TEST(SerialKeyTests, isValid_expiredV2TrialProSerial_invalid) +{ + // {v2;trial;pro;Bob;1;email;company name;0;86400} + SerialKey serial("7B76323B747269616C3B70726F3B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(true, serial.isTrial()); + EXPECT_FALSE(serial.isValid(86401)); + EXPECT_EQ(kPro, serial.edition()); +} + +TEST(SerialKeyTests, isExpiring_validV2TrialBasicSerial_returnFalse) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(true, serial.isTrial()); + 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("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(true, serial.isTrial()); + EXPECT_EQ(true, serial.isExpiring(1)); +} + +TEST(SerialKeyTests, isExpiring_expiredV2TrialBasicSerial_returnFalse) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(true, serial.isTrial()); + EXPECT_FALSE(serial.isExpiring(86401)); +} + +TEST(SerialKeyTests, isExpired_validV2TrialBasicSerial_returnFalse) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(true, serial.isTrial()); + EXPECT_FALSE(serial.isExpired(0)); +} + +TEST(SerialKeyTests, isExpired_expiringV2TrialBasicSerial_returnFalse) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(true, serial.isTrial()); + EXPECT_FALSE(serial.isExpired(1)); +} + +TEST(SerialKeyTests, isExpired_expiredV2TrialBasicSerial_returnTrue) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(true, serial.isTrial()); + EXPECT_EQ(true, serial.isExpired(86401)); +} + +TEST(SerialKeyTests, dayLeft_validExactlyOneDayV2TrialBasicSerial_returnOne) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(1, serial.dayLeft(0)); +} + +TEST(SerialKeyTests, dayLeft_validWithinOneDayV2TrialBasicSerial_returnOne) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(1, serial.dayLeft(1)); +} + +TEST(SerialKeyTests, dayLeft_expiredV2TrialBasicSerial_returnZero) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + EXPECT_EQ(0, serial.dayLeft(86401)); +} From 92b29276d0ab586c03c94b57733419028c035098 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 14 Oct 2016 13:51:27 +0100 Subject: [PATCH 128/241] #5657 Added serial argument in core --- src/lib/synergy/ArgParser.cpp | 3 +++ src/lib/synergy/ServerArgs.cpp | 1 + src/lib/synergy/ServerArgs.h | 1 + 3 files changed, 5 insertions(+) diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 11adc2d5d..d16a6fa94 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -70,6 +70,9 @@ ArgParser::parseServerArgs(ServerArgs& args, int argc, const char* const* argv) else if (isArg(i, argc, argv, "", "--prm-hc", 1)) { DpiHelper::s_primaryHeightCenter = synergy::string::stringToSizeType(argv[++i]); } + else if (isArg(i, argc, argv, "", "--serial-key", 1)) { + args.m_serial = argv[++i]; + } else { LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, args.m_pname, argv[i], args.m_pname)); return false; diff --git a/src/lib/synergy/ServerArgs.cpp b/src/lib/synergy/ServerArgs.cpp index f56f6e8d5..52166f2aa 100644 --- a/src/lib/synergy/ServerArgs.cpp +++ b/src/lib/synergy/ServerArgs.cpp @@ -19,6 +19,7 @@ ServerArgs::ServerArgs() : m_configFile(), + m_serial(), m_config(NULL) { } diff --git a/src/lib/synergy/ServerArgs.h b/src/lib/synergy/ServerArgs.h index 54310f8e7..7c69fae64 100644 --- a/src/lib/synergy/ServerArgs.h +++ b/src/lib/synergy/ServerArgs.h @@ -28,5 +28,6 @@ class ServerArgs : public ArgsBase { public: String m_configFile; + String m_serial; Config* m_config; }; From 2b1b0640eac3aa5f9a20b4aa1a98be72cb759937 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 14 Oct 2016 13:51:46 +0100 Subject: [PATCH 129/241] #5657 Added serial argument parsing unit test --- src/test/unittests/synergy/ServerArgsParsingTests.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/unittests/synergy/ServerArgsParsingTests.cpp b/src/test/unittests/synergy/ServerArgsParsingTests.cpp index 5ae18b94b..92db8c0d5 100644 --- a/src/test/unittests/synergy/ServerArgsParsingTests.cpp +++ b/src/test/unittests/synergy/ServerArgsParsingTests.cpp @@ -64,3 +64,17 @@ TEST(ServerArgsParsingTests, parseServerArgs_configArg_setConfigFile) EXPECT_EQ("mock_configFile", serverArgs.m_configFile); } + +TEST(ServerArgsParsingTests, parseServerArgs_serialArg_setSerial) +{ + NiceMock argParser; + ON_CALL(argParser, parseGenericArgs(_, _, _)).WillByDefault(Invoke(server_stubParseGenericArgs)); + ON_CALL(argParser, checkUnexpectedArgs()).WillByDefault(Invoke(server_stubCheckUnexpectedArgs)); + ServerArgs serverArgs; + const int argc = 3; + const char* kSerialCmd[argc] = { "stub", "--serial-key", "mock_serial" }; + + argParser.parseServerArgs(serverArgs, argc, kSerialCmd); + + EXPECT_EQ("mock_serial", serverArgs.m_serial); +} From 89851fddc32b181129e0db229c117302890ba9c8 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 14 Oct 2016 17:14:21 +0100 Subject: [PATCH 130/241] #5657 Exited server if trial is expired --- src/lib/server/CMakeLists.txt | 2 ++ src/lib/server/Server.cpp | 27 ++++++++++++++++++++------- src/lib/server/Server.h | 5 +++-- src/lib/synergy/ServerApp.cpp | 2 +- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/lib/server/CMakeLists.txt b/src/lib/server/CMakeLists.txt index 2c34af078..3cb582ec0 100644 --- a/src/lib/server/CMakeLists.txt +++ b/src/lib/server/CMakeLists.txt @@ -35,6 +35,8 @@ endif() add_library(server STATIC ${sources}) +target_link_libraries(server shared) + if (UNIX) target_link_libraries(server synergy) endif() diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index d9394ed57..a202648e7 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -45,11 +45,13 @@ #include "base/Log.h" #include "base/TMethodEventJob.h" #include "common/stdexcept.h" +#include "shared/SerialKey.h" #include #include #include #include +#include // // Server @@ -60,7 +62,7 @@ Server::Server( PrimaryClient* primaryClient, synergy::Screen* screen, IEventQueue* events, - bool enableDragDrop) : + ServerArgs const& args) : m_mock(false), m_primaryClient(primaryClient), m_active(primaryClient), @@ -91,10 +93,10 @@ Server::Server( m_sendFileThread(NULL), m_writeToDropDirThread(NULL), m_ignoreFileTransfer(false), - m_enableDragDrop(enableDragDrop), m_enableClipboard(true), m_sendDragInfoThread(NULL), - m_waitDragInfoThread(true) + m_waitDragInfoThread(true), + m_args(args) { // must have a primary client and it must have a canonical name assert(m_primaryClient != NULL); @@ -184,7 +186,7 @@ Server::Server( new TMethodEventJob(this, &Server::handleFakeInputEndEvent)); - if (m_enableDragDrop) { + if (m_args.m_enableDragDrop) { m_events->adoptHandler(m_events->forFile().fileChunkSending(), this, new TMethodEventJob(this, @@ -451,6 +453,17 @@ Server::switchScreen(BaseClientProxy* dst, SInt32 x, SInt32 y, bool forScreensaver) { assert(dst != NULL); + + // if trial is expired, exit the process + if (!m_args.m_serial.empty()) { + SerialKey serial(m_args.m_serial); + if (!serial.isValid(std::time(0))) { + LOG((CLOG_ERR "trial is expired, aborting server")); + exit(kExitSuccess); + return; + } + } + #ifndef NDEBUG { SInt32 dx, dy, dw, dh; @@ -1706,7 +1719,7 @@ Server::onMouseUp(ButtonID id) return; } - if (m_enableDragDrop) { + if (m_args.m_enableDragDrop) { if (!m_screen->isOnScreen()) { String& file = m_screen->getDraggingFilename(); if (!file.empty()) { @@ -1791,7 +1804,7 @@ Server::onMouseMovePrimary(SInt32 x, SInt32 y) // should we switch or not? if (isSwitchOkay(newScreen, dir, x, y, xc, yc)) { - if (m_enableDragDrop + if (m_args.m_enableDragDrop && m_screen->isDraggingStarted() && m_active != newScreen && m_waitDragInfoThread) { @@ -2393,7 +2406,7 @@ Server::sendFileThread(void* data) void Server::dragInfoReceived(UInt32 fileNum, String content) { - if (!m_enableDragDrop) { + if (!m_args.m_enableDragDrop) { LOG((CLOG_DEBUG "drag drop not enabled, ignoring drag info.")); return; } diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index 7681487a5..d1e48bd59 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -25,6 +25,7 @@ #include "synergy/mouse_types.h" #include "synergy/INode.h" #include "synergy/DragInformation.h" +#include "synergy/ServerArgs.h" #include "base/Event.h" #include "base/Stopwatch.h" #include "base/EventTypes.h" @@ -106,7 +107,7 @@ class Server : public INode { ownership of \p primaryClient. */ Server(Config& config, PrimaryClient* primaryClient, - synergy::Screen* screen, IEventQueue* events, bool enableDragDrop); + synergy::Screen* screen, IEventQueue* events, ServerArgs const& args); ~Server(); #ifdef TEST_ENV @@ -472,11 +473,11 @@ class Server : public INode { Thread* m_writeToDropDirThread; String m_dragFileExt; bool m_ignoreFileTransfer; - bool m_enableDragDrop; bool m_enableClipboard; Thread* m_sendDragInfoThread; bool m_waitDragInfoThread; ClientListener* m_clientListener; + ServerArgs m_args; }; diff --git a/src/lib/synergy/ServerApp.cpp b/src/lib/synergy/ServerApp.cpp index 23884aeca..ae1a20772 100644 --- a/src/lib/synergy/ServerApp.cpp +++ b/src/lib/synergy/ServerApp.cpp @@ -647,7 +647,7 @@ ServerApp::openClientListener(const NetworkAddress& address) Server* ServerApp::openServer(Config& config, PrimaryClient* primaryClient) { - Server* server = new Server(config, primaryClient, m_serverScreen, m_events, args().m_enableDragDrop); + Server* server = new Server(config, primaryClient, m_serverScreen, m_events, args()); try { m_events->adoptHandler( m_events->forServer().disconnected(), server, From 55414e458100881ff9ada66afaa2b998cb4aeff0 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 14 Oct 2016 17:38:31 +0100 Subject: [PATCH 131/241] #5657 Make SubscriptionManager backward compatible --- src/gui/res/ActivationDialog.ui | 6 ++-- src/gui/res/MainWindowBase.ui | 36 ++++++++++++++++++++++++ src/gui/res/ServerConfigDialogBase.ui | 2 +- src/gui/src/ActivationDialog.cpp | 36 ++++++++++++++---------- src/gui/src/ActivationDialog.h | 3 ++ src/gui/src/AppConfig.cpp | 9 +++--- src/gui/src/AppConfig.h | 8 +++--- src/gui/src/MainWindow.cpp | 41 +++++++++++++++++++++------ src/gui/src/MainWindow.h | 4 ++- src/gui/src/QUtility.cpp | 4 +-- src/gui/src/SettingsDialog.cpp | 2 +- src/gui/src/SubscriptionManager.cpp | 52 ++++++++++++++++++++++++++++++----- src/gui/src/SubscriptionManager.h | 12 +++++--- src/gui/src/main.cpp | 1 + src/lib/shared/EditionType.h | 6 ++-- src/lib/shared/SerialKey.cpp | 29 +++++++++++-------- src/lib/shared/SerialKey.h | 15 ++++++++-- 17 files changed, 198 insertions(+), 68 deletions(-) diff --git a/src/gui/res/ActivationDialog.ui b/src/gui/res/ActivationDialog.ui index d11a1a9f8..0a980eb2c 100644 --- a/src/gui/res/ActivationDialog.ui +++ b/src/gui/res/ActivationDialog.ui @@ -30,7 +30,7 @@ - Found on your <a href="https://symless.com/account/?source=gui">account</a> page. + <html><head/><body><p>This can be found on your <a href="https://symless.com/account/?source=gui"><span style=" text-decoration: underline; color:#0000ff;">account</span></a> page.</p></body></html> true @@ -46,8 +46,8 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'.SF NS Text'; font-size:13pt; 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> +</style></head><body style=" font-family:'Sans'; font-size:10pt; 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;"><br /></p></body></html> false diff --git a/src/gui/res/MainWindowBase.ui b/src/gui/res/MainWindowBase.ui index 87144cd13..644107bf6 100644 --- a/src/gui/res/MainWindowBase.ui +++ b/src/gui/res/MainWindowBase.ui @@ -27,6 +27,42 @@ + + + + + + + + + + :/res/icons/16x16/warning.png + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + diff --git a/src/gui/res/ServerConfigDialogBase.ui b/src/gui/res/ServerConfigDialogBase.ui index 1cc4d2b53..5478f3601 100644 --- a/src/gui/res/ServerConfigDialogBase.ui +++ b/src/gui/res/ServerConfigDialogBase.ui @@ -17,7 +17,7 @@ - 2 + 0 diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index f070007c3..5162dd116 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -22,8 +22,14 @@ ActivationDialog::ActivationDialog(QWidget* parent, AppConfig& appConfig, m_subscriptionManager (&subscriptionManager) { ui->setupUi(this); + refreshSerialKey(); +} + +void ActivationDialog::refreshSerialKey() +{ + ui->m_pTextEditSerialKey->setText(m_appConfig->serialKey()); ui->m_pTextEditSerialKey->setFocus(); - ui->m_pTextEditSerialKey->moveCursor(QTextCursor::End); + ui->m_pTextEditSerialKey->moveCursor(QTextCursor::End); } ActivationDialog::~ActivationDialog() @@ -33,21 +39,22 @@ ActivationDialog::~ActivationDialog() void ActivationDialog::reject() { - CancelActivationDialog cancelActivationDialog(this); - if (QDialog::Accepted == cancelActivationDialog.exec()) { - m_subscriptionManager->notifySkip(); - m_appConfig->activationHasRun(true); - m_appConfig->saveSettings(); - QDialog::reject(); + if (m_subscriptionManager->edition() == Edition::kUnregistered) { + CancelActivationDialog cancelActivationDialog(this); + if (QDialog::Accepted == cancelActivationDialog.exec()) { + m_subscriptionManager->skipActivation(); + m_appConfig->activationHasRun(true); + m_appConfig->saveSettings(); + } } + QDialog::reject(); } void ActivationDialog::accept() { QMessageBox message; QString error; - int edition = Unregistered; - + m_appConfig->activationHasRun(true); m_appConfig->saveSettings(); @@ -60,13 +67,14 @@ void ActivationDialog::accept() tr("An error occurred while trying to activate Synergy. " "Please contact the helpdesk, and provide the " "following details.\n\n%1").arg(e.what())); + refreshSerialKey(); return; } - m_appConfig->setEdition(edition); - m_appConfig->saveSettings(); - - message.information(this, "Activated!", - tr("Thanks for activating %1!").arg(getEditionName(edition))); + if (m_subscriptionManager->edition() != Edition::kUnregistered) { + message.information(this, "Activated!", + tr("Thanks for activating %1!").arg + (getEditionName(m_subscriptionManager->edition()))); + } QDialog::accept(); } diff --git a/src/gui/src/ActivationDialog.h b/src/gui/src/ActivationDialog.h index 09577ed5b..b0e9aa942 100644 --- a/src/gui/src/ActivationDialog.h +++ b/src/gui/src/ActivationDialog.h @@ -23,6 +23,9 @@ public slots: void reject(); void accept(); +protected: + void refreshSerialKey(); + private: Ui::ActivationDialog *ui; AppConfig* m_appConfig; diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 22aec72c5..cdb26e14b 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -157,7 +157,7 @@ void AppConfig::loadSettings() } m_ElevateMode = static_cast(elevateMode.toInt()); m_AutoConfigPrompted = settings().value("autoConfigPrompted", false).toBool(); - m_Edition = settings().value("edition", Unregistered).toInt(); + m_Edition = static_cast(settings().value("edition", kUnregistered).toInt()); m_ActivateEmail = settings().value("activateEmail", "").toString(); m_CryptoEnabled = settings().value("cryptoEnabled", true).toBool(); m_AutoHide = settings().value("autoHide", false).toBool(); @@ -237,12 +237,11 @@ void AppConfig::setAutoConfigPrompted(bool prompted) m_AutoConfigPrompted = prompted; } -void AppConfig::setEdition(int e) { +void AppConfig::setEdition(Edition e) { m_Edition = e; - emit editionSet (e); } -int AppConfig::edition() const { return m_Edition; } +Edition AppConfig::edition() const { return m_Edition; } QString AppConfig::setSerialKey(QString serial) { using std::swap; @@ -276,7 +275,7 @@ void AppConfig::setCryptoEnabled(bool e) { } bool AppConfig::getCryptoEnabled() const { - return (edition() == Pro) && m_CryptoEnabled; + return (edition() == kPro) && m_CryptoEnabled; } void AppConfig::setAutoHide(bool b) { m_AutoHide = b; } diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index f62af1d21..b7eacf61d 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -23,6 +23,7 @@ #include #include #include "ElevateMode.h" +#include // this should be incremented each time a new page is added. this is // saved to settings when the user finishes running the wizard. if @@ -77,8 +78,8 @@ class AppConfig: public QObject void setAutoConfig(bool autoConfig); bool autoConfigPrompted(); void setAutoConfigPrompted(bool prompted); - void setEdition(int e); - int edition() const; + void setEdition(Edition); + Edition edition() const; QString setSerialKey(QString serial); void clearSerialKey(); QString serialKey(); @@ -134,7 +135,7 @@ class AppConfig: public QObject bool m_AutoConfig; ElevateMode m_ElevateMode; bool m_AutoConfigPrompted; - int m_Edition; + Edition m_Edition; QString m_ActivateEmail; bool m_CryptoEnabled; bool m_AutoHide; @@ -147,7 +148,6 @@ class AppConfig: public QObject static const char m_SynergyLogDir[]; signals: - void editionSet(int); void sslToggled(bool enabled); }; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 8165a43ef..5adeab504 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -136,13 +136,28 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig, m_SuppressAutoConfigWarning = false; m_pComboServerList->hide(); - - setEdition(m_AppConfig->edition()); - m_pLabelPadlock->hide(); - connect (this, SIGNAL(windowShown()), this, SLOT(on_windowShown()), Qt::QueuedConnection); - connect (m_AppConfig, SIGNAL(editionSet(int)), this, SLOT(setEdition(int)), Qt::QueuedConnection); - connect (m_AppConfig, SIGNAL(sslToggled(bool)), this, SLOT(sslToggled(bool)), Qt::QueuedConnection); + setEdition (m_SubscriptionManager->edition()); + + this->m_trialWidget->hide(); + if (m_SubscriptionManager->isTrial()) { + beginTrial(); + } + + connect (this, SIGNAL(windowShown()), + this, SLOT(on_windowShown()), Qt::QueuedConnection); + + connect (m_SubscriptionManager, SIGNAL(editionChanged(Edition)), + this, SLOT(setEdition(Edition)), Qt::QueuedConnection); + + connect (m_SubscriptionManager, SIGNAL(beginTrial()), + this, SLOT(beginTrial()), Qt::QueuedConnection); + + connect (m_SubscriptionManager, SIGNAL(endTrial()), + this, SLOT(endTrial()), Qt::QueuedConnection); + + connect (m_AppConfig, SIGNAL(sslToggled(bool)), + this, SLOT(sslToggled(bool)), Qt::QueuedConnection); } MainWindow::~MainWindow() @@ -1024,7 +1039,7 @@ void MainWindow::serverDetected(const QString name) } } -void MainWindow::setEdition(int edition) +void MainWindow::setEdition(Edition edition) { setWindowTitle(getEditionName(edition)); if (m_AppConfig->getCryptoEnabled()) { @@ -1035,6 +1050,16 @@ void MainWindow::setEdition(int edition) saveSettings(); } +void MainWindow::beginTrial() +{ + this->m_trialWidget->show(); +} + +void MainWindow::endTrial() +{ + this->m_trialWidget->hide(); +} + void MainWindow::updateLocalFingerprint() { if (m_AppConfig->getCryptoEnabled() && Fingerprint::local().fileExists()) { @@ -1373,7 +1398,7 @@ void MainWindow::bonjourInstallFinished() void MainWindow::on_windowShown() { - if (!m_AppConfig->activationHasRun() && (m_AppConfig->edition() == Unregistered)) { + if (!m_AppConfig->activationHasRun() && (m_AppConfig->edition() == kUnregistered)) { ActivationDialog activationDialog (this, appConfig(), subscriptionManager()); activationDialog.exec(); } diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 1dd678ba7..22cddabe6 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -121,7 +121,9 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase SubscriptionManager& subscriptionManager() const; public slots: - void setEdition(int edition); + void setEdition(Edition edition); + void beginTrial(); + void endTrial(); void appendLogRaw(const QString& text); void appendLogInfo(const QString& text); void appendLogDebug(const QString& text); diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index 322b2fd1d..c35f96388 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -45,10 +45,10 @@ void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData) QString getEditionName (int edition) { - if (edition == Basic) { + if (edition == kBasic) { return "Synergy Basic"; } - else if (edition == Pro) { + else if (edition == kPro) { return "Synergy Pro"; } else { diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index 9b1d1b0e4..ddbeae4d4 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -64,7 +64,7 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) : #endif m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled()); - m_pCheckBoxEnableCrypto->setEnabled(m_appConfig.edition() == Pro); + m_pCheckBoxEnableCrypto->setEnabled(m_appConfig.edition() == kPro); } void SettingsDialog::accept() diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index fd2a77bbc..86d425364 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -22,21 +22,59 @@ #include SubscriptionManager::SubscriptionManager(AppConfig* appConfig) : - m_AppConfig(appConfig) { + m_AppConfig(appConfig), + m_serialKey(appConfig->edition()) { + try { + setSerialKey(m_AppConfig->serialKey()); + } catch (...) { + m_AppConfig->setSerialKey(""); + } } -void +SerialKey SubscriptionManager::setSerialKey(QString serialKeyString) { SerialKey serialKey (serialKeyString.toStdString()); - if (serialKey.isValid (::time(0)) && (serialKey != m_serialKey)) { + if (!serialKey.isValid (::time(0))) { + throw std::runtime_error ("Invalid serial key"); + } + + if (serialKey != m_serialKey) { + using std::swap; + swap (serialKey, m_serialKey); + m_AppConfig->setSerialKey (serialKeyString); notifyActivation ("serial:" + serialKeyString); - emit serialKeyChanged (serialKey); + emit serialKeyChanged (m_serialKey); + + if (m_serialKey.edition() != serialKey.edition()) { + m_AppConfig->setEdition (m_serialKey.edition()); + emit editionChanged (m_serialKey.edition()); + } + + if (m_serialKey.isTrial() != serialKey.isTrial()) { + if (m_serialKey.isTrial()) { + emit beginTrial(); + } else { + emit endTrial(); + } + } } + + return serialKey; } -void SubscriptionManager::notifySkip() +Edition SubscriptionManager::edition() const +{ + return m_serialKey.edition(); +} + +bool SubscriptionManager::isTrial() const +{ + return m_serialKey.isTrial(); +} + +void SubscriptionManager::skipActivation() { notifyActivation ("skip:unknown"); } @@ -45,7 +83,7 @@ void SubscriptionManager::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())); @@ -54,5 +92,5 @@ void SubscriptionManager::notifyActivation(QString identity) notifier->moveToThread(thread); thread->start(); - QMetaObject::invokeMethod(notifier, "notify", Qt::QueuedConnection); + QMetaObject::invokeMethod(notifier, "notify", Qt::QueuedConnection); } diff --git a/src/gui/src/SubscriptionManager.h b/src/gui/src/SubscriptionManager.h index 2c79f6bdc..eff9112d2 100644 --- a/src/gui/src/SubscriptionManager.h +++ b/src/gui/src/SubscriptionManager.h @@ -28,12 +28,14 @@ class SubscriptionManager: public QObject Q_OBJECT public: - SubscriptionManager (AppConfig* appConfig); - void setSerialKey (QString serialKey); - void notifySkip (); + SubscriptionManager(AppConfig* appConfig); + SerialKey setSerialKey(QString serialKey); + Edition edition() const; + bool isTrial() const; + void skipActivation(); private: - void notifyActivation (QString identity); + void notifyActivation(QString identity); private: AppConfig* m_AppConfig; @@ -42,4 +44,6 @@ class SubscriptionManager: public QObject signals: void serialKeyChanged (SerialKey); void editionChanged (Edition); + void beginTrial (); + void endTrial (); }; diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index 79131f369..18febc8e8 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -84,6 +84,7 @@ int main(int argc, char* argv[]) QSettings settings; AppConfig appConfig (&settings); + qRegisterMetaType("Edition"); SubscriptionManager subscriptionManager (&appConfig); app.switchTranslator(appConfig.language()); diff --git a/src/lib/shared/EditionType.h b/src/lib/shared/EditionType.h index ddd27ccfd..66f30aa95 100644 --- a/src/lib/shared/EditionType.h +++ b/src/lib/shared/EditionType.h @@ -21,10 +21,10 @@ /* Do not reorder these! */ enum Edition { - Basic, - Pro, + kBasic, + kPro, Trial_DO_NOT_USE_OR_THERE_WILL_BE_PAIN, - Unregistered + kUnregistered }; #endif // EDITIONTYPE_H diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index 52dd27435..be8cbc357 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -21,13 +21,17 @@ #include #include #include +#include using namespace std; -SerialKey::SerialKey(): - m_warnTime(1), - m_expireTime(1), - m_trial(true) +SerialKey::SerialKey(Edition edition): + m_userLimit(1), + m_warnTime(ULLONG_MAX), + m_expireTime(ULLONG_MAX), + m_edition(edition), + m_trial(false), + m_valid(true) { } @@ -35,13 +39,13 @@ SerialKey::SerialKey(std::string serial) : m_userLimit(1), m_warnTime(0), m_expireTime(0), - m_edition(Edition::Basic), + m_edition(Edition::kBasic), m_trial(true), m_valid(false) { string plainText = decode(serial); if (!plainText.empty()) { - parse(serial); + parse(plainText); } } @@ -70,7 +74,7 @@ SerialKey::isExpiring(time_t currentTime) const bool result = false; if (m_valid) { - if (m_warnTime < currentTime && currentTime < m_expireTime) { + if (m_warnTime <= currentTime && currentTime < m_expireTime) { result = true; } } @@ -84,7 +88,7 @@ SerialKey::isExpired(time_t currentTime) const bool result = false; if (m_valid) { - if (currentTime > m_expireTime) { + if (m_expireTime <= currentTime) { result = true; } } @@ -108,7 +112,7 @@ time_t SerialKey::daysLeft(time_t currentTime) const { unsigned long long timeLeft = 0; - if (m_expireTime > currentTime) { + if (currentTime < m_expireTime) { timeLeft = m_expireTime - currentTime; } @@ -170,12 +174,13 @@ SerialKey::parse(std::string plainSerial) parts.push_back(plainSerial.substr(start, pos - start)); pos += 1; } - + if ((parts.size() == 8) && (parts.at(0).find("v1") != string::npos)) { // e.g.: {v1;basic;Bob;1;email;company name;1398297600;1398384000} m_edition = getEdition(parts.at(1)); m_name = parts.at(2); + m_trial = false; sscanf(parts.at(3).c_str(), "%d", &m_userLimit); m_email = parts.at(4); m_company = parts.at(5); @@ -202,9 +207,9 @@ SerialKey::parse(std::string plainSerial) Edition SerialKey::getEdition(std::string editionStr) { - Edition e = Edition::Basic; + Edition e = Edition::kBasic; if (editionStr == "pro") { - e = Edition::Pro; + e = Edition::kPro; } return e; diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h index 2423cf557..03d0ab98d 100644 --- a/src/lib/shared/SerialKey.h +++ b/src/lib/shared/SerialKey.h @@ -26,9 +26,10 @@ #endif class SerialKey { + friend bool operator== (SerialKey const&, SerialKey const&); public: - SerialKey(); - SerialKey(std::string serial); + explicit SerialKey(Edition edition = Edition::kUnregistered); + explicit SerialKey(std::string serial); bool isValid(time_t currentTime) const; bool isExpiring(time_t currentTime) const; @@ -68,7 +69,15 @@ class SerialKey { inline bool operator== (SerialKey const& lhs, SerialKey const& rhs) { - return (lhs.edition() == rhs.edition()); + return (lhs.m_name == rhs.m_name) && + (lhs.m_email == rhs.m_email) && + (lhs.m_company == rhs.m_company) && + (lhs.m_userLimit == rhs.m_userLimit) && + (lhs.m_warnTime == rhs.m_warnTime) && + (lhs.m_expireTime == rhs.m_expireTime) && + (lhs.m_edition == rhs.m_edition) && + (lhs.m_trial == rhs.m_trial) && + (lhs.m_valid == rhs.m_valid); } inline bool From dc31f395cc87c06eef3e6313a9c449e7a9b566ad Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Fri, 14 Oct 2016 09:44:02 -0700 Subject: [PATCH 132/241] #5657 Fixed integtests using the old server constructor --- src/test/integtests/net/NetworkTests.cpp | 48 +++++++++++++++++++------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/test/integtests/net/NetworkTests.cpp b/src/test/integtests/net/NetworkTests.cpp index 79ef7c99c..dbb3dd1e4 100644 --- a/src/test/integtests/net/NetworkTests.cpp +++ b/src/test/integtests/net/NetworkTests.cpp @@ -129,7 +129,9 @@ TEST_F(NetworkTests, sendToClient_mockData) ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true)); ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter)); - Server server(serverConfig, &primaryClient, &serverScreen, &m_events, true); + ServerArgs serverArgs; + serverArgs.m_enableDragDrop = true; + Server server(serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs); server.m_mock = true; listener.setServer(&server); @@ -142,10 +144,10 @@ TEST_F(NetworkTests, sendToClient_mockData) ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); - ClientArgs args; - args.m_enableDragDrop = true; - args.m_enableCrypto = false; - Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, args); + ClientArgs clientArgs; + clientArgs.m_enableDragDrop = true; + clientArgs.m_enableCrypto = false; + Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, clientArgs); m_events.adoptHandler( m_events.forFile().fileRecieveCompleted(), &client, @@ -185,7 +187,9 @@ TEST_F(NetworkTests, sendToClient_mockFile) ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true)); ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter)); - Server server(serverConfig, &primaryClient, &serverScreen, &m_events, true); + ServerArgs serverArgs; + serverArgs.m_enableDragDrop = true; + Server server(serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs); server.m_mock = true; listener.setServer(&server); @@ -198,10 +202,10 @@ TEST_F(NetworkTests, sendToClient_mockFile) ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); - ClientArgs args; - args.m_enableDragDrop = true; - args.m_enableCrypto = false; - Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, args); + ClientArgs clientArgs; + clientArgs.m_enableDragDrop = true; + clientArgs.m_enableCrypto = false; + Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, clientArgs); m_events.adoptHandler( m_events.forFile().fileRecieveCompleted(), &client, @@ -235,7 +239,9 @@ TEST_F(NetworkTests, sendToServer_mockData) ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true)); ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter)); - Server server(serverConfig, &primaryClient, &serverScreen, &m_events, true); + ServerArgs serverArgs; + serverArgs.m_enableDragDrop = true; + Server server(serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs); server.m_mock = true; listener.setServer(&server); @@ -247,10 +253,10 @@ TEST_F(NetworkTests, sendToServer_mockData) ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape)); ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); - ClientArgs args; - args.m_enableDragDrop = true; - args.m_enableCrypto = false; - Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, args); + ClientArgs clientArgs; + clientArgs.m_enableDragDrop = true; + clientArgs.m_enableCrypto = false; + Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, clientArgs); m_events.adoptHandler( m_events.forClientListener().connected(), &listener, @@ -290,7 +296,9 @@ TEST_F(NetworkTests, sendToServer_mockFile) ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true)); ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter)); - Server server(serverConfig, &primaryClient, &serverScreen, &m_events, true); + ServerArgs serverArgs; + serverArgs.m_enableDragDrop = true; + Server server(serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs); server.m_mock = true; listener.setServer(&server); @@ -302,10 +310,10 @@ TEST_F(NetworkTests, sendToServer_mockFile) ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape)); ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos)); - ClientArgs args; - args.m_enableDragDrop = true; - args.m_enableCrypto = false; - Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, args); + ClientArgs clientArgs; + clientArgs.m_enableDragDrop = true; + clientArgs.m_enableCrypto = false; + Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, clientArgs); m_events.adoptHandler( m_events.forClientListener().connected(), &listener, From 1f93b4a918a4b4c4b21a219b38f5c3996fba12cb Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Sat, 15 Oct 2016 12:37:00 +0100 Subject: [PATCH 133/241] #5657 Rename dayLeft to daysLeft in unit tests --- src/test/unittests/shared/SerialKeyTests.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/unittests/shared/SerialKeyTests.cpp b/src/test/unittests/shared/SerialKeyTests.cpp index 40fb479a1..9535e15df 100644 --- a/src/test/unittests/shared/SerialKeyTests.cpp +++ b/src/test/unittests/shared/SerialKeyTests.cpp @@ -63,7 +63,7 @@ TEST(SerialKeyTests, parse_validV1Serial_valid) EXPECT_EQ(true, serial.isValid(0)); EXPECT_EQ(kBasic, serial.edition()); EXPECT_FALSE(serial.isExpired(0)); - EXPECT_EQ(true, serial.dayLeft(0)); + EXPECT_EQ(true, serial.daysLeft(0)); EXPECT_EQ(true, serial.isExpiring(1)); } @@ -74,7 +74,7 @@ TEST(SerialKeyTests, parse_validV2Serial_valid) EXPECT_EQ(true, serial.isValid(0)); EXPECT_EQ(kPro, serial.edition()); EXPECT_FALSE(serial.isExpired(0)); - EXPECT_EQ(true, serial.dayLeft(0)); + EXPECT_EQ(true, serial.daysLeft(0)); EXPECT_EQ(true, serial.isExpiring(1)); EXPECT_EQ(true, serial.isTrial()); } @@ -179,23 +179,23 @@ TEST(SerialKeyTests, isExpired_expiredV2TrialBasicSerial_returnTrue) EXPECT_EQ(true, serial.isExpired(86401)); } -TEST(SerialKeyTests, dayLeft_validExactlyOneDayV2TrialBasicSerial_returnOne) +TEST(SerialKeyTests, daysLeft_validExactlyOneDayV2TrialBasicSerial_returnOne) { // {v2;trial;basic;Bob;1;email;company name;0;86400} SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(1, serial.dayLeft(0)); + EXPECT_EQ(1, serial.daysLeft(0)); } -TEST(SerialKeyTests, dayLeft_validWithinOneDayV2TrialBasicSerial_returnOne) +TEST(SerialKeyTests, daysLeft_validWithinOneDayV2TrialBasicSerial_returnOne) { // {v2;trial;basic;Bob;1;email;company name;0;86400} SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(1, serial.dayLeft(1)); + EXPECT_EQ(1, serial.daysLeft(1)); } -TEST(SerialKeyTests, dayLeft_expiredV2TrialBasicSerial_returnZero) +TEST(SerialKeyTests, daysLeft_expiredV2TrialBasicSerial_returnZero) { // {v2;trial;basic;Bob;1;email;company name;0;86400} SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(0, serial.dayLeft(86401)); + EXPECT_EQ(0, serial.daysLeft(86401)); } From 3b98a7b785be7616709ae80420e2c5bc57d2016b Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Sat, 15 Oct 2016 14:58:03 +0100 Subject: [PATCH 134/241] #5657 Add trial label to main window --- src/gui/res/MainWindowBase.ui | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/gui/res/MainWindowBase.ui b/src/gui/res/MainWindowBase.ui index 644107bf6..ba00f2d25 100644 --- a/src/gui/res/MainWindowBase.ui +++ b/src/gui/res/MainWindowBase.ui @@ -30,6 +30,18 @@ + + 2 + + + 0 + + + 0 + + + 8 + @@ -41,9 +53,9 @@ - + - + <html><head/><body><p><span style=" font-weight:600;">6</span> days of your Synergy Pro trial remain. <a href="http://symless.com/pricing?src=gui"><span style=" text-decoration: underline; color:#0000ff;">Buy now!</span></a></p></body></html> @@ -76,7 +88,7 @@ 0 - 7 + 8 From 4ad7c7fe39eeb96a051691d7a08d11389625448f Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Sat, 15 Oct 2016 15:30:28 +0100 Subject: [PATCH 135/241] #5657 Rename edition to activeLicense. WSFs --- src/gui/src/ActivationDialog.cpp | 88 +- src/gui/src/MainWindow.cpp | 1849 ++++++++++++++++++----------------- src/gui/src/MainWindow.h | 322 +++--- src/gui/src/SubscriptionManager.cpp | 105 +- src/gui/src/SubscriptionManager.h | 30 +- src/lib/shared/SerialKey.cpp | 288 +++--- src/lib/shared/SerialKey.h | 84 +- 7 files changed, 1387 insertions(+), 1379 deletions(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 5162dd116..9d3b43f33 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -14,67 +14,67 @@ #include #include -ActivationDialog::ActivationDialog(QWidget* parent, AppConfig& appConfig, - SubscriptionManager& subscriptionManager) : - QDialog(parent), - ui(new Ui::ActivationDialog), - m_appConfig(&appConfig), - m_subscriptionManager (&subscriptionManager) +ActivationDialog::ActivationDialog(QWidget* parent, AppConfig& appConfig, + SubscriptionManager& subscriptionManager) : + QDialog(parent), + ui(new Ui::ActivationDialog), + m_appConfig(&appConfig), + m_subscriptionManager (&subscriptionManager) { - ui->setupUi(this); - refreshSerialKey(); + ui->setupUi(this); + refreshSerialKey(); } void ActivationDialog::refreshSerialKey() { - ui->m_pTextEditSerialKey->setText(m_appConfig->serialKey()); - ui->m_pTextEditSerialKey->setFocus(); - ui->m_pTextEditSerialKey->moveCursor(QTextCursor::End); + ui->m_pTextEditSerialKey->setText(m_appConfig->serialKey()); + ui->m_pTextEditSerialKey->setFocus(); + ui->m_pTextEditSerialKey->moveCursor(QTextCursor::End); } ActivationDialog::~ActivationDialog() { - delete ui; + delete ui; } void ActivationDialog::reject() { - if (m_subscriptionManager->edition() == Edition::kUnregistered) { - CancelActivationDialog cancelActivationDialog(this); - if (QDialog::Accepted == cancelActivationDialog.exec()) { - m_subscriptionManager->skipActivation(); - m_appConfig->activationHasRun(true); - m_appConfig->saveSettings(); - } - } - QDialog::reject(); + if (m_subscriptionManager->activeLicense() == Edition::kUnregistered) { + CancelActivationDialog cancelActivationDialog(this); + if (QDialog::Accepted == cancelActivationDialog.exec()) { + m_subscriptionManager->skipActivation(); + m_appConfig->activationHasRun(true); + m_appConfig->saveSettings(); + } + } + QDialog::reject(); } void ActivationDialog::accept() { - QMessageBox message; - QString error; - - m_appConfig->activationHasRun(true); - m_appConfig->saveSettings(); + QMessageBox message; + QString error; - try { - QString serialKey = ui->m_pTextEditSerialKey->toPlainText(); - m_subscriptionManager->setSerialKey(serialKey); - } - catch (std::exception& e) { - message.critical(this, "Unknown Error", - tr("An error occurred while trying to activate Synergy. " - "Please contact the helpdesk, and provide the " - "following details.\n\n%1").arg(e.what())); - refreshSerialKey(); - return; - } + m_appConfig->activationHasRun(true); + m_appConfig->saveSettings(); - if (m_subscriptionManager->edition() != Edition::kUnregistered) { - message.information(this, "Activated!", - tr("Thanks for activating %1!").arg - (getEditionName(m_subscriptionManager->edition()))); - } - QDialog::accept(); + try { + QString serialKey = ui->m_pTextEditSerialKey->toPlainText(); + m_subscriptionManager->setSerialKey(serialKey); + } + catch (std::exception& e) { + message.critical(this, "Unknown Error", + tr("An error occurred while trying to activate Synergy. " + "Please contact the helpdesk, and provide the " + "following details.\n\n%1").arg(e.what())); + refreshSerialKey(); + return; + } + + if (m_subscriptionManager->activeLicense() != Edition::kUnregistered) { + message.information(this, "Activated!", + tr("Thanks for activating %1!").arg + (getEditionName(m_subscriptionManager->activeLicense()))); + } + QDialog::accept(); } diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 5adeab504..ad9d37d04 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -70,1351 +70,1352 @@ static const QString synergyConfigFilter(QObject::tr("Synergy Configurations (*. static const char* synergyIconFiles[] = { - ":/res/icons/16x16/synergy-disconnected.png", - ":/res/icons/16x16/synergy-disconnected.png", - ":/res/icons/16x16/synergy-connected.png", - ":/res/icons/16x16/synergy-transfering.png" + ":/res/icons/16x16/synergy-disconnected.png", + ":/res/icons/16x16/synergy-disconnected.png", + ":/res/icons/16x16/synergy-connected.png", + ":/res/icons/16x16/synergy-transfering.png" }; MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig, - SubscriptionManager& subscriptionManager) : - m_Settings(settings), - m_AppConfig(&appConfig), - m_SubscriptionManager(&subscriptionManager), - m_pSynergy(NULL), - m_SynergyState(synergyDisconnected), - m_ServerConfig(&m_Settings, 5, 3, m_AppConfig->screenName(), this), - m_pTempConfigFile(NULL), - m_pTrayIcon(NULL), - m_pTrayIconMenu(NULL), - m_AlreadyHidden(false), - m_pMenuBar(NULL), - m_pMenuFile(NULL), - m_pMenuEdit(NULL), - m_pMenuWindow(NULL), - m_pMenuHelp(NULL), - m_pZeroconfService(NULL), - m_pDataDownloader(NULL), - m_DownloadMessageBox(NULL), - m_pCancelButton(NULL), - m_SuppressAutoConfigWarning(false), - m_BonjourInstall(NULL), - m_SuppressEmptyServerWarning(false), - m_ExpectedRunningState(kStopped), - m_pSslCertificate(NULL) -{ - setupUi(this); - - createMenuBar(); - loadSettings(); - initConnections(); - - m_pWidgetUpdate->hide(); - m_VersionChecker.setApp(appPath(appConfig.synergycName())); - m_pLabelScreenName->setText(getScreenName()); - m_pLabelIpAddresses->setText(getIPAddresses()); + SubscriptionManager& subscriptionManager) : + m_Settings(settings), + m_AppConfig(&appConfig), + m_SubscriptionManager(&subscriptionManager), + m_pSynergy(NULL), + m_SynergyState(synergyDisconnected), + m_ServerConfig(&m_Settings, 5, 3, m_AppConfig->screenName(), this), + m_pTempConfigFile(NULL), + m_pTrayIcon(NULL), + m_pTrayIconMenu(NULL), + m_AlreadyHidden(false), + m_pMenuBar(NULL), + m_pMenuFile(NULL), + m_pMenuEdit(NULL), + m_pMenuWindow(NULL), + m_pMenuHelp(NULL), + m_pZeroconfService(NULL), + m_pDataDownloader(NULL), + m_DownloadMessageBox(NULL), + m_pCancelButton(NULL), + m_SuppressAutoConfigWarning(false), + m_BonjourInstall(NULL), + m_SuppressEmptyServerWarning(false), + m_ExpectedRunningState(kStopped), + m_pSslCertificate(NULL) +{ + setupUi(this); + + createMenuBar(); + loadSettings(); + initConnections(); + + m_pWidgetUpdate->hide(); + m_VersionChecker.setApp(appPath(appConfig.synergycName())); + m_pLabelScreenName->setText(getScreenName()); + m_pLabelIpAddresses->setText(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(appendLogNote(const QString&))); - m_IpcClient.connectToHost(); + // 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(appendLogNote(const QString&))); + m_IpcClient.connectToHost(); #endif - // change default size based on os + // change default size based on os #if defined(Q_OS_MAC) - resize(720, 550); - setMinimumSize(size()); + resize(720, 550); + setMinimumSize(size()); #elif defined(Q_OS_LINUX) - resize(700, 530); - setMinimumSize(size()); + resize(700, 530); + setMinimumSize(size()); #endif - m_SuppressAutoConfigWarning = true; - m_pCheckBoxAutoConfig->setChecked(appConfig.autoConfig()); - m_SuppressAutoConfigWarning = false; - - m_pComboServerList->hide(); - m_pLabelPadlock->hide(); - setEdition (m_SubscriptionManager->edition()); - - this->m_trialWidget->hide(); - if (m_SubscriptionManager->isTrial()) { - beginTrial(); - } - - connect (this, SIGNAL(windowShown()), - this, SLOT(on_windowShown()), Qt::QueuedConnection); - - connect (m_SubscriptionManager, SIGNAL(editionChanged(Edition)), - this, SLOT(setEdition(Edition)), Qt::QueuedConnection); - - connect (m_SubscriptionManager, SIGNAL(beginTrial()), - this, SLOT(beginTrial()), Qt::QueuedConnection); - - connect (m_SubscriptionManager, SIGNAL(endTrial()), - this, SLOT(endTrial()), Qt::QueuedConnection); - - connect (m_AppConfig, SIGNAL(sslToggled(bool)), - this, SLOT(sslToggled(bool)), Qt::QueuedConnection); + m_SuppressAutoConfigWarning = true; + m_pCheckBoxAutoConfig->setChecked(appConfig.autoConfig()); + m_SuppressAutoConfigWarning = false; + + m_pComboServerList->hide(); + m_pLabelPadlock->hide(); + m_trialWidget->hide(); + + connect (this, SIGNAL(windowShown()), + this, SLOT(on_windowShown()), Qt::QueuedConnection); + + connect (m_SubscriptionManager, SIGNAL(editionChanged(Edition)), + this, SLOT(setEdition(Edition)), Qt::QueuedConnection); + + connect (m_SubscriptionManager, SIGNAL(beginTrial(bool)), + this, SLOT(beginTrial(bool)), Qt::QueuedConnection); + + connect (m_SubscriptionManager, SIGNAL(endTrial(bool)), + this, SLOT(endTrial(bool)), Qt::QueuedConnection); + + connect (m_AppConfig, SIGNAL(sslToggled(bool)), + this, SLOT(sslToggled(bool)), Qt::QueuedConnection); + + m_SubscriptionManager->update(); } MainWindow::~MainWindow() { - if (appConfig().processMode() == Desktop) { - m_ExpectedRunningState = kStopped; - stopDesktop(); - } + if (appConfig().processMode() == Desktop) { + m_ExpectedRunningState = kStopped; + stopDesktop(); + } - saveSettings(); + saveSettings(); - delete m_pZeroconfService; + delete m_pZeroconfService; - if (m_DownloadMessageBox != NULL) { - delete m_DownloadMessageBox; - } + if (m_DownloadMessageBox != NULL) { + delete m_DownloadMessageBox; + } - if (m_BonjourInstall != NULL) { - delete m_BonjourInstall; - } + if (m_BonjourInstall != NULL) { + delete m_BonjourInstall; + } - delete m_pSslCertificate; + delete m_pSslCertificate; } void MainWindow::open() { - createTrayIcon(); + createTrayIcon(); - if (!autoHide()) { - showNormal(); - } + if (!autoHide()) { + showNormal(); + } - m_VersionChecker.checkLatest(); + m_VersionChecker.checkLatest(); - if (!appConfig().autoConfigPrompted()) { - promptAutoConfig(); - } + if (!appConfig().autoConfigPrompted()) { + promptAutoConfig(); + } - // only start if user has previously started. this stops the gui from - // auto hiding before the user has configured synergy (which of course - // confuses first time users, who think synergy has crashed). - if (appConfig().startedBefore() && appConfig().processMode() == Desktop) { - m_SuppressEmptyServerWarning = true; - startSynergy(); - m_SuppressEmptyServerWarning = false; - } + // only start if user has previously started. this stops the gui from + // auto hiding before the user has configured synergy (which of course + // confuses first time users, who think synergy has crashed). + if (appConfig().startedBefore() && appConfig().processMode() == Desktop) { + m_SuppressEmptyServerWarning = true; + startSynergy(); + m_SuppressEmptyServerWarning = false; + } } void MainWindow::onModeChanged(bool startDesktop, bool applyService) { - if (appConfig().processMode() == Service) - { - // ensure that the apply button actually does something, since desktop - // mode screws around with connecting/disconnecting the action. - disconnect(m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); - connect(m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); - - if (applyService) - { - stopDesktop(); - startSynergy(); - } - } - else if ((appConfig().processMode() == Desktop) && startDesktop) - { - stopService(); - startSynergy(); - } + if (appConfig().processMode() == Service) + { + // ensure that the apply button actually does something, since desktop + // mode screws around with connecting/disconnecting the action. + disconnect(m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); + connect(m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); + + if (applyService) + { + stopDesktop(); + startSynergy(); + } + } + else if ((appConfig().processMode() == Desktop) && startDesktop) + { + stopService(); + startSynergy(); + } } void MainWindow::setStatus(const QString &status) { - m_pStatusLabel->setText(status); + m_pStatusLabel->setText(status); } void MainWindow::createTrayIcon() { - m_pTrayIconMenu = new QMenu(this); + m_pTrayIconMenu = new QMenu(this); - m_pTrayIconMenu->addAction(m_pActionStartSynergy); - m_pTrayIconMenu->addAction(m_pActionStopSynergy); - m_pTrayIconMenu->addSeparator(); + m_pTrayIconMenu->addAction(m_pActionStartSynergy); + m_pTrayIconMenu->addAction(m_pActionStopSynergy); + m_pTrayIconMenu->addSeparator(); - m_pTrayIconMenu->addAction(m_pActionMinimize); - m_pTrayIconMenu->addAction(m_pActionRestore); - m_pTrayIconMenu->addSeparator(); - m_pTrayIconMenu->addAction(m_pActionQuit); + m_pTrayIconMenu->addAction(m_pActionMinimize); + m_pTrayIconMenu->addAction(m_pActionRestore); + m_pTrayIconMenu->addSeparator(); + m_pTrayIconMenu->addAction(m_pActionQuit); - m_pTrayIcon = new QSystemTrayIcon(this); - m_pTrayIcon->setContextMenu(m_pTrayIconMenu); + m_pTrayIcon = new QSystemTrayIcon(this); + m_pTrayIcon->setContextMenu(m_pTrayIconMenu); - connect(m_pTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), - this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))); + connect(m_pTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))); - setIcon(synergyDisconnected); + setIcon(synergyDisconnected); - m_pTrayIcon->show(); + m_pTrayIcon->show(); } void MainWindow::retranslateMenuBar() { - m_pMenuFile->setTitle(tr("&File")); - m_pMenuEdit->setTitle(tr("&Edit")); - m_pMenuWindow->setTitle(tr("&Window")); - m_pMenuHelp->setTitle(tr("&Help")); + m_pMenuFile->setTitle(tr("&File")); + m_pMenuEdit->setTitle(tr("&Edit")); + m_pMenuWindow->setTitle(tr("&Window")); + m_pMenuHelp->setTitle(tr("&Help")); } void MainWindow::createMenuBar() { - m_pMenuBar = new QMenuBar(this); - m_pMenuFile = new QMenu("", m_pMenuBar); - m_pMenuEdit = new QMenu("", m_pMenuBar); - m_pMenuWindow = new QMenu("", m_pMenuBar); - m_pMenuHelp = new QMenu("", m_pMenuBar); - retranslateMenuBar(); + m_pMenuBar = new QMenuBar(this); + m_pMenuFile = new QMenu("", m_pMenuBar); + m_pMenuEdit = new QMenu("", m_pMenuBar); + m_pMenuWindow = new QMenu("", m_pMenuBar); + m_pMenuHelp = new QMenu("", m_pMenuBar); + retranslateMenuBar(); - m_pMenuBar->addAction(m_pMenuFile->menuAction()); - m_pMenuBar->addAction(m_pMenuEdit->menuAction()); + m_pMenuBar->addAction(m_pMenuFile->menuAction()); + m_pMenuBar->addAction(m_pMenuEdit->menuAction()); #if !defined(Q_OS_MAC) - m_pMenuBar->addAction(m_pMenuWindow->menuAction()); + m_pMenuBar->addAction(m_pMenuWindow->menuAction()); #endif - m_pMenuBar->addAction(m_pMenuHelp->menuAction()); + m_pMenuBar->addAction(m_pMenuHelp->menuAction()); - m_pMenuFile->addAction(m_pActionStartSynergy); - m_pMenuFile->addAction(m_pActionStopSynergy); - m_pMenuFile->addSeparator(); - m_pMenuFile->addAction(m_pActivate); - m_pMenuFile->addSeparator(); - m_pMenuFile->addAction(m_pActionSave); - m_pMenuFile->addSeparator(); - m_pMenuFile->addAction(m_pActionQuit); - m_pMenuEdit->addAction(m_pActionSettings); - m_pMenuWindow->addAction(m_pActionMinimize); - m_pMenuWindow->addAction(m_pActionRestore); - m_pMenuHelp->addAction(m_pActionAbout); + m_pMenuFile->addAction(m_pActionStartSynergy); + m_pMenuFile->addAction(m_pActionStopSynergy); + m_pMenuFile->addSeparator(); + m_pMenuFile->addAction(m_pActivate); + m_pMenuFile->addSeparator(); + m_pMenuFile->addAction(m_pActionSave); + m_pMenuFile->addSeparator(); + m_pMenuFile->addAction(m_pActionQuit); + m_pMenuEdit->addAction(m_pActionSettings); + m_pMenuWindow->addAction(m_pActionMinimize); + m_pMenuWindow->addAction(m_pActionRestore); + m_pMenuHelp->addAction(m_pActionAbout); - setMenuBar(m_pMenuBar); + setMenuBar(m_pMenuBar); } void MainWindow::loadSettings() { - // the next two must come BEFORE loading groupServerChecked and groupClientChecked or - // disabling and/or enabling the right widgets won't automatically work - m_pRadioExternalConfig->setChecked(settings().value("useExternalConfig", false).toBool()); - m_pRadioInternalConfig->setChecked(settings().value("useInternalConfig", true).toBool()); + // the next two must come BEFORE loading groupServerChecked and groupClientChecked or + // disabling and/or enabling the right widgets won't automatically work + m_pRadioExternalConfig->setChecked(settings().value("useExternalConfig", false).toBool()); + m_pRadioInternalConfig->setChecked(settings().value("useInternalConfig", true).toBool()); - m_pGroupServer->setChecked(settings().value("groupServerChecked", false).toBool()); - m_pLineEditConfigFile->setText(settings().value("configFile", QDir::homePath() + "/" + synergyConfigName).toString()); - m_pGroupClient->setChecked(settings().value("groupClientChecked", true).toBool()); - m_pLineEditHostname->setText(settings().value("serverHostname").toString()); + m_pGroupServer->setChecked(settings().value("groupServerChecked", false).toBool()); + m_pLineEditConfigFile->setText(settings().value("configFile", QDir::homePath() + "/" + synergyConfigName).toString()); + m_pGroupClient->setChecked(settings().value("groupClientChecked", true).toBool()); + m_pLineEditHostname->setText(settings().value("serverHostname").toString()); } void MainWindow::initConnections() { - connect(m_pActionMinimize, SIGNAL(triggered()), this, SLOT(hide())); - connect(m_pActionRestore, SIGNAL(triggered()), this, SLOT(showNormal())); - connect(m_pActionStartSynergy, SIGNAL(triggered()), this, SLOT(startSynergy())); - connect(m_pActionStopSynergy, SIGNAL(triggered()), this, SLOT(stopSynergy())); - connect(m_pActionQuit, SIGNAL(triggered()), qApp, SLOT(quit())); - connect(&m_VersionChecker, SIGNAL(updateFound(const QString&)), this, SLOT(updateFound(const QString&))); + connect(m_pActionMinimize, SIGNAL(triggered()), this, SLOT(hide())); + connect(m_pActionRestore, SIGNAL(triggered()), this, SLOT(showNormal())); + connect(m_pActionStartSynergy, SIGNAL(triggered()), this, SLOT(startSynergy())); + connect(m_pActionStopSynergy, SIGNAL(triggered()), this, SLOT(stopSynergy())); + connect(m_pActionQuit, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(&m_VersionChecker, SIGNAL(updateFound(const QString&)), this, SLOT(updateFound(const QString&))); } void MainWindow::saveSettings() { - // program settings - settings().setValue("groupServerChecked", m_pGroupServer->isChecked()); - settings().setValue("useExternalConfig", m_pRadioExternalConfig->isChecked()); - settings().setValue("configFile", m_pLineEditConfigFile->text()); - settings().setValue("useInternalConfig", m_pRadioInternalConfig->isChecked()); - settings().setValue("groupClientChecked", m_pGroupClient->isChecked()); - settings().setValue("serverHostname", m_pLineEditHostname->text()); + // program settings + settings().setValue("groupServerChecked", m_pGroupServer->isChecked()); + settings().setValue("useExternalConfig", m_pRadioExternalConfig->isChecked()); + settings().setValue("configFile", m_pLineEditConfigFile->text()); + settings().setValue("useInternalConfig", m_pRadioInternalConfig->isChecked()); + settings().setValue("groupClientChecked", m_pGroupClient->isChecked()); + settings().setValue("serverHostname", m_pLineEditHostname->text()); - settings().sync(); + settings().sync(); } void MainWindow::setIcon(qSynergyState state) { - QIcon icon; - icon.addFile(synergyIconFiles[state]); + QIcon icon; + icon.addFile(synergyIconFiles[state]); - if (m_pTrayIcon) - m_pTrayIcon->setIcon(icon); + if (m_pTrayIcon) + m_pTrayIcon->setIcon(icon); } void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason) { #ifndef Q_OS_WIN - if (reason == QSystemTrayIcon::DoubleClick) - { - if (isVisible()) - { - hide(); - } - else - { - showNormal(); - activateWindow(); - } - } + if (reason == QSystemTrayIcon::DoubleClick) + { + if (isVisible()) + { + hide(); + } + else + { + showNormal(); + activateWindow(); + } + } #endif } void MainWindow::logOutput() { - if (m_pSynergy) - { - QString text(m_pSynergy->readAllStandardOutput()); - foreach(QString line, text.split(QRegExp("\r|\n|\r\n"))) - { - if (!line.isEmpty()) - { - appendLogRaw(line); - } - } - } + if (m_pSynergy) + { + QString text(m_pSynergy->readAllStandardOutput()); + foreach(QString line, text.split(QRegExp("\r|\n|\r\n"))) + { + if (!line.isEmpty()) + { + appendLogRaw(line); + } + } + } } void MainWindow::logError() { - if (m_pSynergy) - { - appendLogRaw(m_pSynergy->readAllStandardError()); - } + if (m_pSynergy) + { + appendLogRaw(m_pSynergy->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(DOWNLOAD_URL)); + 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(DOWNLOAD_URL)); } void MainWindow::appendLogInfo(const QString& text) { - appendLogRaw(getTimeStamp() + " INFO: " + text); + appendLogRaw(getTimeStamp() + " INFO: " + text); } void MainWindow::appendLogDebug(const QString& text) { - if (appConfig().logLevel() >= 4) { - appendLogRaw(getTimeStamp() + " DEBUG: " + text); - } + if (appConfig().logLevel() >= 4) { + appendLogRaw(getTimeStamp() + " DEBUG: " + text); + } } void MainWindow::appendLogError(const QString& text) { - appendLogRaw(getTimeStamp() + " ERROR: " + text); + appendLogRaw(getTimeStamp() + " ERROR: " + text); } void MainWindow::appendLogRaw(const QString& text) { - foreach(QString line, text.split(QRegExp("\r|\n|\r\n"))) { - if (!line.isEmpty()) { - m_pLogOutput->append(line); - updateStateFromLogLine(line); - } - } + foreach(QString line, text.split(QRegExp("\r|\n|\r\n"))) { + if (!line.isEmpty()) { + m_pLogOutput->append(line); + updateStateFromLogLine(line); + } + } } void MainWindow::updateStateFromLogLine(const QString &line) { - checkConnected(line); - checkFingerprint(line); + checkConnected(line); + checkFingerprint(line); } void MainWindow::checkConnected(const QString& line) { - // TODO: implement ipc connection state messages to replace this hack. - if (line.contains("started server") || - line.contains("connected to server") || - line.contains("watchdog status: ok")) - { - setSynergyState(synergyConnected); + // TODO: implement ipc connection state messages to replace this hack. + if (line.contains("started server") || + line.contains("connected to server") || + line.contains("watchdog status: ok")) + { + setSynergyState(synergyConnected); - if (!appConfig().startedBefore() && isVisible()) { - QMessageBox::information( - this, "Synergy", - tr("Synergy is now connected. You can close the " - "config window and Synergy will remain connected in " - "the background.")); + if (!appConfig().startedBefore() && isVisible()) { + QMessageBox::information( + this, "Synergy", + tr("Synergy is now connected. You can close the " + "config window and Synergy will remain connected in " + "the background.")); - appConfig().setStartedBefore(true); - appConfig().saveSettings(); - } - } + appConfig().setStartedBefore(true); + appConfig().saveSettings(); + } + } } void MainWindow::checkFingerprint(const QString& line) { - QRegExp fingerprintRegex(".*server fingerprint: ([A-F0-9:]+)"); - if (!fingerprintRegex.exactMatch(line)) { - return; - } - - QString fingerprint = fingerprintRegex.cap(1); - if (Fingerprint::trustedServers().isTrusted(fingerprint)) { - return; - } - - static bool messageBoxAlreadyShown = false; - - if (!messageBoxAlreadyShown) { - stopSynergy(); - - messageBoxAlreadyShown = true; - QMessageBox::StandardButton fingerprintReply = - QMessageBox::information( - this, tr("Security question"), - tr("Do you trust this fingerprint?\n\n" - "%1\n\n" - "This is a server fingerprint. You should compare this " - "fingerprint to the one on your server's screen. If the " - "two don't match exactly, then it's probably not the server " - "you're expecting (it could be a malicious user).\n\n" - "To automatically trust this fingerprint for future " - "connections, click Yes. To reject this fingerprint and " - "disconnect from the server, click No.") - .arg(fingerprint), - QMessageBox::Yes | QMessageBox::No); - - if (fingerprintReply == QMessageBox::Yes) { - // restart core process after trusting fingerprint. - Fingerprint::trustedServers().trust(fingerprint); - startSynergy(); - } - - messageBoxAlreadyShown = false; - } + QRegExp fingerprintRegex(".*server fingerprint: ([A-F0-9:]+)"); + if (!fingerprintRegex.exactMatch(line)) { + return; + } + + QString fingerprint = fingerprintRegex.cap(1); + if (Fingerprint::trustedServers().isTrusted(fingerprint)) { + return; + } + + static bool messageBoxAlreadyShown = false; + + if (!messageBoxAlreadyShown) { + stopSynergy(); + + messageBoxAlreadyShown = true; + QMessageBox::StandardButton fingerprintReply = + QMessageBox::information( + this, tr("Security question"), + tr("Do you trust this fingerprint?\n\n" + "%1\n\n" + "This is a server fingerprint. You should compare this " + "fingerprint to the one on your server's screen. If the " + "two don't match exactly, then it's probably not the server " + "you're expecting (it could be a malicious user).\n\n" + "To automatically trust this fingerprint for future " + "connections, click Yes. To reject this fingerprint and " + "disconnect from the server, click No.") + .arg(fingerprint), + QMessageBox::Yes | QMessageBox::No); + + if (fingerprintReply == QMessageBox::Yes) { + // restart core process after trusting fingerprint. + Fingerprint::trustedServers().trust(fingerprint); + startSynergy(); + } + + messageBoxAlreadyShown = false; + } } bool MainWindow::autoHide() { - if ((appConfig().processMode() == Desktop) && - appConfig().getAutoHide()) { - hide(); - return true; - } + if ((appConfig().processMode() == Desktop) && + appConfig().getAutoHide()) { + hide(); + return true; + } - return false; + return false; } QString MainWindow::getTimeStamp() { - QDateTime current = QDateTime::currentDateTime(); - return '[' + current.toString(Qt::ISODate) + ']'; + QDateTime current = QDateTime::currentDateTime(); + return '[' + current.toString(Qt::ISODate) + ']'; } void MainWindow::restartSynergy() { - stopSynergy(); - startSynergy(); + stopSynergy(); + startSynergy(); } void MainWindow::proofreadInfo() { - setEdition(m_AppConfig->edition()); // Why is this here? + setEdition(m_AppConfig->edition()); // Why is this here? - int oldState = m_SynergyState; - m_SynergyState = synergyDisconnected; - setSynergyState((qSynergyState)oldState); + int oldState = m_SynergyState; + m_SynergyState = synergyDisconnected; + setSynergyState((qSynergyState)oldState); } void MainWindow::showEvent(QShowEvent* event) { - QMainWindow::showEvent(event); - emit windowShown(); + QMainWindow::showEvent(event); + emit windowShown(); } void MainWindow::clearLog() { - m_pLogOutput->clear(); + m_pLogOutput->clear(); } void MainWindow::startSynergy() { - bool desktopMode = appConfig().processMode() == Desktop; - bool serviceMode = appConfig().processMode() == Service; + bool desktopMode = appConfig().processMode() == Desktop; + bool serviceMode = appConfig().processMode() == Service; - appendLogDebug("starting process"); - m_ExpectedRunningState = kStarted; - setSynergyState(synergyConnecting); + appendLogDebug("starting process"); + m_ExpectedRunningState = kStarted; + setSynergyState(synergyConnecting); - QString app; - QStringList args; + QString app; + QStringList args; - args << "-f" << "--no-tray" << "--debug" << appConfig().logLevelText(); + args << "-f" << "--no-tray" << "--debug" << appConfig().logLevelText(); - args << "--name" << getScreenName(); + args << "--name" << getScreenName(); - if (desktopMode) - { - setSynergyProcess(new QProcess(this)); - } - else - { - // tell client/server to talk to daemon through ipc. - args << "--ipc"; + if (desktopMode) + { + setSynergyProcess(new QProcess(this)); + } + else + { + // tell client/server to talk to daemon through ipc. + args << "--ipc"; #if defined(Q_OS_WIN) - // tell the client/server to shut down when a ms windows desk - // is switched; this is because we may need to elevate or not - // based on which desk the user is in (login always needs - // elevation, where as default desk does not). - // Note that this is only enabled when synergy is set to elevate - // 'as needed' (e.g. on a UAC dialog popup) in order to prevent - // unnecessary restarts when synergy was started elevated or - // when it is not allowed to elevate. In these cases restarting - // the server is fruitless. - if (appConfig().elevateMode() == ElevateAsNeeded) { - args << "--stop-on-desk-switch"; - } + // tell the client/server to shut down when a ms windows desk + // is switched; this is because we may need to elevate or not + // based on which desk the user is in (login always needs + // elevation, where as default desk does not). + // Note that this is only enabled when synergy is set to elevate + // 'as needed' (e.g. on a UAC dialog popup) in order to prevent + // unnecessary restarts when synergy was started elevated or + // when it is not allowed to elevate. In these cases restarting + // the server is fruitless. + if (appConfig().elevateMode() == ElevateAsNeeded) { + args << "--stop-on-desk-switch"; + } #endif - } + } #ifndef Q_OS_LINUX - if (m_ServerConfig.enableDragAndDrop()) { - args << "--enable-drag-drop"; - } + if (m_ServerConfig.enableDragAndDrop()) { + args << "--enable-drag-drop"; + } #endif - if (m_AppConfig->getCryptoEnabled()) { - args << "--enable-crypto"; - } + if (m_AppConfig->getCryptoEnabled()) { + args << "--enable-crypto"; + } #if defined(Q_OS_WIN) - // on windows, the profile directory changes depending on the user that - // launched the process (e.g. when launched with elevation). setting the - // profile dir on launch ensures it uses the same profile dir is used - // no matter how its relaunched. - args << "--profile-dir" << getProfileRootForArg(); + // on windows, the profile directory changes depending on the user that + // launched the process (e.g. when launched with elevation). setting the + // profile dir on launch ensures it uses the same profile dir is used + // no matter how its relaunched. + args << "--profile-dir" << getProfileRootForArg(); #endif - if ((synergyType() == synergyClient && !clientArgs(args, app)) - || (synergyType() == synergyServer && !serverArgs(args, app))) - { - stopSynergy(); - return; - } - - if (desktopMode) - { - connect(synergyProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(synergyFinished(int, QProcess::ExitStatus))); - connect(synergyProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(logOutput())); - connect(synergyProcess(), SIGNAL(readyReadStandardError()), this, SLOT(logError())); - } - - // put a space between last log output and new instance. - if (!m_pLogOutput->toPlainText().isEmpty()) - appendLogRaw(""); - - appendLogInfo("starting " + QString(synergyType() == synergyServer ? "server" : "client")); - - qDebug() << args; - - // show command if debug log level... - if (appConfig().logLevel() >= 4) { - appendLogInfo(QString("command: %1 %2").arg(app, args.join(" "))); - } - - appendLogInfo("config file: " + configFilename()); - appendLogInfo("log level: " + appConfig().logLevelText()); - - if (appConfig().logToFile()) - appendLogInfo("log file: " + appConfig().logFilename()); - - if (desktopMode) - { - synergyProcess()->start(app, args); - if (!synergyProcess()->waitForStarted()) - { - show(); - QMessageBox::warning(this, tr("Program can not be started"), QString(tr("The executable

%1

could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.").arg(app))); - return; - } - } - - if (serviceMode) - { - QString command(app + " " + args.join(" ")); - m_IpcClient.sendCommand(command, appConfig().elevateMode()); - } + if ((synergyType() == synergyClient && !clientArgs(args, app)) + || (synergyType() == synergyServer && !serverArgs(args, app))) + { + stopSynergy(); + return; + } + + if (desktopMode) + { + connect(synergyProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(synergyFinished(int, QProcess::ExitStatus))); + connect(synergyProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(logOutput())); + connect(synergyProcess(), SIGNAL(readyReadStandardError()), this, SLOT(logError())); + } + + // put a space between last log output and new instance. + if (!m_pLogOutput->toPlainText().isEmpty()) + appendLogRaw(""); + + appendLogInfo("starting " + QString(synergyType() == synergyServer ? "server" : "client")); + + qDebug() << args; + + // show command if debug log level... + if (appConfig().logLevel() >= 4) { + appendLogInfo(QString("command: %1 %2").arg(app, args.join(" "))); + } + + appendLogInfo("config file: " + configFilename()); + appendLogInfo("log level: " + appConfig().logLevelText()); + + if (appConfig().logToFile()) + appendLogInfo("log file: " + appConfig().logFilename()); + + if (desktopMode) + { + synergyProcess()->start(app, args); + if (!synergyProcess()->waitForStarted()) + { + show(); + QMessageBox::warning(this, tr("Program can not be started"), QString(tr("The executable

%1

could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.").arg(app))); + return; + } + } + + if (serviceMode) + { + QString command(app + " " + args.join(" ")); + m_IpcClient.sendCommand(command, appConfig().elevateMode()); + } } void MainWindow::sslToggled (bool enabled) { - if (enabled) { - m_pSslCertificate = new SslCertificate(this); - m_pSslCertificate->generateCertificate(); - } - updateLocalFingerprint(); + if (enabled) { + m_pSslCertificate = new SslCertificate(this); + m_pSslCertificate->generateCertificate(); + } + updateLocalFingerprint(); } bool MainWindow::clientArgs(QStringList& args, QString& app) { - app = appPath(appConfig().synergycName()); + app = appPath(appConfig().synergycName()); - if (!QFile::exists(app)) - { - show(); - QMessageBox::warning(this, tr("Synergy client not found"), - tr("The executable for the synergy client does not exist.")); - return false; - } + if (!QFile::exists(app)) + { + show(); + QMessageBox::warning(this, tr("Synergy client not found"), + tr("The executable for the synergy client does not exist.")); + return false; + } #if defined(Q_OS_WIN) - // wrap in quotes so a malicious user can't start \Program.exe as admin. - app = QString("\"%1\"").arg(app); + // wrap in quotes so a malicious user can't start \Program.exe as admin. + app = QString("\"%1\"").arg(app); #endif - if (appConfig().logToFile()) - { - appConfig().persistLogDir(); - args << "--log" << appConfig().logFilenameCmd(); - } + if (appConfig().logToFile()) + { + appConfig().persistLogDir(); + args << "--log" << appConfig().logFilenameCmd(); + } - // check auto config first, if it is disabled or no server detected, - // use line edit host name if it is not empty - if (m_pCheckBoxAutoConfig->isChecked()) { - if (m_pComboServerList->count() != 0) { - QString serverIp = m_pComboServerList->currentText(); - args << serverIp + ":" + QString::number(appConfig().port()); - return true; - } - } + // check auto config first, if it is disabled or no server detected, + // use line edit host name if it is not empty + if (m_pCheckBoxAutoConfig->isChecked()) { + if (m_pComboServerList->count() != 0) { + QString serverIp = m_pComboServerList->currentText(); + args << serverIp + ":" + QString::number(appConfig().port()); + return true; + } + } - if (m_pLineEditHostname->text().isEmpty()) { - show(); - if (!m_SuppressEmptyServerWarning) { - QMessageBox::warning(this, tr("Hostname is empty"), - tr("Please fill in a hostname for the synergy client to connect to.")); - } - return false; - } + if (m_pLineEditHostname->text().isEmpty()) { + show(); + if (!m_SuppressEmptyServerWarning) { + QMessageBox::warning(this, tr("Hostname is empty"), + tr("Please fill in a hostname for the synergy client to connect to.")); + } + return false; + } - args << m_pLineEditHostname->text() + ":" + QString::number(appConfig().port()); + args << m_pLineEditHostname->text() + ":" + QString::number(appConfig().port()); - return true; + return true; } QString MainWindow::configFilename() { - QString filename; - if (m_pRadioInternalConfig->isChecked()) - { - // TODO: no need to use a temporary file, since we need it to - // be permenant (since it'll be used for Windows services, etc). - m_pTempConfigFile = new QTemporaryFile(); - if (!m_pTempConfigFile->open()) - { - QMessageBox::critical(this, tr("Cannot write configuration file"), tr("The temporary configuration file required to start synergy can not be written.")); - return ""; - } - - serverConfig().save(*m_pTempConfigFile); - filename = m_pTempConfigFile->fileName(); - - m_pTempConfigFile->close(); - } - else - { - if (!QFile::exists(m_pLineEditConfigFile->text())) - { - if (QMessageBox::warning(this, tr("Configuration filename invalid"), - tr("You have not filled in a valid configuration file for the synergy server. " - "Do you want to browse for the configuration file now?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes - || !on_m_pButtonBrowseConfigFile_clicked()) - return ""; - } - - filename = m_pLineEditConfigFile->text(); - } - return filename; + QString filename; + if (m_pRadioInternalConfig->isChecked()) + { + // TODO: no need to use a temporary file, since we need it to + // be permenant (since it'll be used for Windows services, etc). + m_pTempConfigFile = new QTemporaryFile(); + if (!m_pTempConfigFile->open()) + { + QMessageBox::critical(this, tr("Cannot write configuration file"), tr("The temporary configuration file required to start synergy can not be written.")); + return ""; + } + + serverConfig().save(*m_pTempConfigFile); + filename = m_pTempConfigFile->fileName(); + + m_pTempConfigFile->close(); + } + else + { + if (!QFile::exists(m_pLineEditConfigFile->text())) + { + if (QMessageBox::warning(this, tr("Configuration filename invalid"), + tr("You have not filled in a valid configuration file for the synergy server. " + "Do you want to browse for the configuration file now?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes + || !on_m_pButtonBrowseConfigFile_clicked()) + return ""; + } + + filename = m_pLineEditConfigFile->text(); + } + return filename; } QString MainWindow::address() { - QString i = appConfig().interface(); - return (!i.isEmpty() ? i : "") + ":" + QString::number(appConfig().port()); + QString i = appConfig().interface(); + return (!i.isEmpty() ? i : "") + ":" + QString::number(appConfig().port()); } QString MainWindow::appPath(const QString& name) { - return appConfig().synergyProgramDir() + name; + return appConfig().synergyProgramDir() + name; } bool MainWindow::serverArgs(QStringList& args, QString& app) { - app = appPath(appConfig().synergysName()); + app = appPath(appConfig().synergysName()); - if (!QFile::exists(app)) - { - QMessageBox::warning(this, tr("Synergy server not found"), - tr("The executable for the synergy server does not exist.")); - return false; - } + if (!QFile::exists(app)) + { + QMessageBox::warning(this, tr("Synergy server not found"), + tr("The executable for the synergy server does not exist.")); + return false; + } #if defined(Q_OS_WIN) - // wrap in quotes so a malicious user can't start \Program.exe as admin. - app = QString("\"%1\"").arg(app); + // wrap in quotes so a malicious user can't start \Program.exe as admin. + app = QString("\"%1\"").arg(app); #endif - if (appConfig().logToFile()) - { - appConfig().persistLogDir(); + if (appConfig().logToFile()) + { + appConfig().persistLogDir(); - args << "--log" << appConfig().logFilenameCmd(); - } + args << "--log" << appConfig().logFilenameCmd(); + } - QString configFilename = this->configFilename(); + QString configFilename = this->configFilename(); #if defined(Q_OS_WIN) - // wrap in quotes in case username contains spaces. - configFilename = QString("\"%1\"").arg(configFilename); + // wrap in quotes in case username contains spaces. + configFilename = QString("\"%1\"").arg(configFilename); #endif - args << "-c" << configFilename << "--address" << address(); + args << "-c" << configFilename << "--address" << address(); #if defined(Q_OS_WIN) - // pass in physical resolution and primary screen center - // TODO: get this information in the core binary even when - // high DPI is used - int height = QApplication::desktop()->height(); - int width = QApplication::desktop()->width(); - - QRect rec = QApplication::desktop()->screenGeometry(); - int heightCenter = rec.height() / 2; - int widthCenter = rec.width() / 2; - - appendLogDebug(tr("screen resolution: %1 %2 primary screen center: %3 %4") - .arg(width).arg(height).arg(widthCenter).arg(heightCenter)); - - args << "--res-w" << QString::number(width); - args << "--res-h" << QString::number(height); - args << "--prm-wc" << QString::number(widthCenter); - args << "--prm-hc" << QString::number(heightCenter); + // pass in physical resolution and primary screen center + // TODO: get this information in the core binary even when + // high DPI is used + int height = QApplication::desktop()->height(); + int width = QApplication::desktop()->width(); + + QRect rec = QApplication::desktop()->screenGeometry(); + int heightCenter = rec.height() / 2; + int widthCenter = rec.width() / 2; + + appendLogDebug(tr("screen resolution: %1 %2 primary screen center: %3 %4") + .arg(width).arg(height).arg(widthCenter).arg(heightCenter)); + + args << "--res-w" << QString::number(width); + args << "--res-h" << QString::number(height); + args << "--prm-wc" << QString::number(widthCenter); + args << "--prm-hc" << QString::number(heightCenter); #endif - return true; + return true; } void MainWindow::stopSynergy() { - appendLogDebug("stopping process"); + appendLogDebug("stopping process"); - m_ExpectedRunningState = kStopped; + m_ExpectedRunningState = kStopped; - if (appConfig().processMode() == Service) - { - stopService(); - } - else if (appConfig().processMode() == Desktop) - { - stopDesktop(); - } + if (appConfig().processMode() == Service) + { + stopService(); + } + else if (appConfig().processMode() == Desktop) + { + stopDesktop(); + } - setSynergyState(synergyDisconnected); + setSynergyState(synergyDisconnected); - // HACK: deleting the object deletes the physical file, which is - // bad, since it could be in use by the Windows service! - //delete m_pTempConfigFile; - m_pTempConfigFile = NULL; + // HACK: deleting the object deletes the physical file, which is + // bad, since it could be in use by the Windows service! + //delete m_pTempConfigFile; + m_pTempConfigFile = NULL; - // reset so that new connects cause auto-hide. - m_AlreadyHidden = false; + // reset so that new connects cause auto-hide. + m_AlreadyHidden = false; } void MainWindow::stopService() { - // send empty command to stop service from laucning anything. - m_IpcClient.sendCommand("", appConfig().elevateMode()); + // send empty command to stop service from laucning anything. + m_IpcClient.sendCommand("", appConfig().elevateMode()); } void MainWindow::stopDesktop() { - QMutexLocker locker(&m_StopDesktopMutex); - if (!synergyProcess()) { - return; - } + QMutexLocker locker(&m_StopDesktopMutex); + if (!synergyProcess()) { + return; + } - appendLogInfo("stopping synergy desktop process"); + appendLogInfo("stopping synergy desktop process"); - if (synergyProcess()->isOpen()) { - synergyProcess()->close(); - } + if (synergyProcess()->isOpen()) { + synergyProcess()->close(); + } - delete synergyProcess(); - setSynergyProcess(NULL); + delete synergyProcess(); + setSynergyProcess(NULL); } void MainWindow::synergyFinished(int exitCode, QProcess::ExitStatus) { - if (exitCode == 0) { - appendLogInfo(QString("process exited normally")); - } - else { - appendLogError(QString("process exited with error code: %1").arg(exitCode)); - } + if (exitCode == 0) { + appendLogInfo(QString("process exited normally")); + } + else { + appendLogError(QString("process exited with error code: %1").arg(exitCode)); + } - if (m_ExpectedRunningState == kStarted) { - QTimer::singleShot(1000, this, SLOT(startSynergy())); - appendLogInfo(QString("detected process not running, auto restarting")); - } - else { - setSynergyState(synergyDisconnected); - } + if (m_ExpectedRunningState == kStarted) { + QTimer::singleShot(1000, this, SLOT(startSynergy())); + appendLogInfo(QString("detected process not running, auto restarting")); + } + else { + setSynergyState(synergyDisconnected); + } } void MainWindow::setSynergyState(qSynergyState state) { - if (synergyState() == state) - return; - - if (state == synergyConnected || state == synergyConnecting) - { - disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); - connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger())); - m_pButtonToggleStart->setText(tr("&Stop")); - m_pButtonApply->setEnabled(true); - } - else if (state == synergyDisconnected) - { - disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger())); - connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); - m_pButtonToggleStart->setText(tr("&Start")); - m_pButtonApply->setEnabled(false); - } - - bool connected = false; - if (state == synergyConnected || state == synergyTransfering) { - connected = true; - } - - m_pActionStartSynergy->setEnabled(!connected); - m_pActionStopSynergy->setEnabled(connected); - - switch (state) - { - case synergyConnected: { - if (m_AppConfig->getCryptoEnabled()) { - m_pLabelPadlock->show(); - } - else { - m_pLabelPadlock->hide(); - } - - setStatus(tr("Synergy is running.")); - - break; - } - case synergyConnecting: - m_pLabelPadlock->hide(); - setStatus(tr("Synergy is starting.")); - break; - case synergyDisconnected: - m_pLabelPadlock->hide(); - setStatus(tr("Synergy is not running.")); - break; - case synergyTransfering: - break; - } - - setIcon(state); - - m_SynergyState = state; + if (synergyState() == state) + return; + + if (state == synergyConnected || state == synergyConnecting) + { + disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); + connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger())); + m_pButtonToggleStart->setText(tr("&Stop")); + m_pButtonApply->setEnabled(true); + } + else if (state == synergyDisconnected) + { + disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger())); + connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); + m_pButtonToggleStart->setText(tr("&Start")); + m_pButtonApply->setEnabled(false); + } + + bool connected = false; + if (state == synergyConnected || state == synergyTransfering) { + connected = true; + } + + m_pActionStartSynergy->setEnabled(!connected); + m_pActionStopSynergy->setEnabled(connected); + + switch (state) + { + case synergyConnected: { + if (m_AppConfig->getCryptoEnabled()) { + m_pLabelPadlock->show(); + } + else { + m_pLabelPadlock->hide(); + } + + setStatus(tr("Synergy is running.")); + + break; + } + case synergyConnecting: + m_pLabelPadlock->hide(); + setStatus(tr("Synergy is starting.")); + break; + case synergyDisconnected: + m_pLabelPadlock->hide(); + setStatus(tr("Synergy is not running.")); + break; + case synergyTransfering: + break; + } + + setIcon(state); + + m_SynergyState = state; } void MainWindow::setVisible(bool visible) { - QMainWindow::setVisible(visible); - m_pActionMinimize->setEnabled(visible); - m_pActionRestore->setEnabled(!visible); + QMainWindow::setVisible(visible); + m_pActionMinimize->setEnabled(visible); + m_pActionRestore->setEnabled(!visible); #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 // lion - // dock hide only supported on lion :( - ProcessSerialNumber psn = { 0, kCurrentProcess }; - GetCurrentProcess(&psn); - if (visible) - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - else - TransformProcessType(&psn, kProcessTransformToBackgroundApplication); + // dock hide only supported on lion :( + ProcessSerialNumber psn = { 0, kCurrentProcess }; + GetCurrentProcess(&psn); + if (visible) + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + else + TransformProcessType(&psn, kProcessTransformToBackgroundApplication); #endif } QString MainWindow::getIPAddresses() { - QList addresses = QNetworkInterface::allAddresses(); + QList addresses = QNetworkInterface::allAddresses(); - bool hinted = false; - QString result; - for (int i = 0; i < addresses.size(); i++) { - if (addresses[i].protocol() == QAbstractSocket::IPv4Protocol && - addresses[i] != QHostAddress(QHostAddress::LocalHost)) { + bool hinted = false; + QString result; + for (int i = 0; i < addresses.size(); i++) { + if (addresses[i].protocol() == QAbstractSocket::IPv4Protocol && + addresses[i] != QHostAddress(QHostAddress::LocalHost)) { - QString address = addresses[i].toString(); - QString format = "%1, "; + QString address = addresses[i].toString(); + QString format = "%1, "; - // usually 192.168.x.x is a useful ip for the user, so indicate - // this by making it bold. - if (!hinted && address.startsWith("192.168")) { - hinted = true; - format = "%1, "; - } + // usually 192.168.x.x is a useful ip for the user, so indicate + // this by making it bold. + if (!hinted && address.startsWith("192.168")) { + hinted = true; + format = "%1, "; + } - result += format.arg(address); - } - } + result += format.arg(address); + } + } - if (result == "") { - return tr("Unknown"); - } + if (result == "") { + return tr("Unknown"); + } - // remove trailing comma. - result.chop(2); + // remove trailing comma. + result.chop(2); - return result; + return result; } QString MainWindow::getScreenName() { - if (appConfig().screenName() == "") { - return QHostInfo::localHostName(); - } - else { - return appConfig().screenName(); - } + if (appConfig().screenName() == "") { + return QHostInfo::localHostName(); + } + else { + return appConfig().screenName(); + } } void MainWindow::changeEvent(QEvent* event) { - if (event != 0) - { - switch (event->type()) - { - case QEvent::LanguageChange: - { - retranslateUi(this); - retranslateMenuBar(); + if (event != 0) + { + switch (event->type()) + { + case QEvent::LanguageChange: + { + retranslateUi(this); + retranslateMenuBar(); - proofreadInfo(); + proofreadInfo(); - break; - } - default: - QMainWindow::changeEvent(event); - } - } + break; + } + default: + QMainWindow::changeEvent(event); + } + } } void MainWindow::updateZeroconfService() { - QMutexLocker locker(&m_UpdateZeroconfMutex); + QMutexLocker locker(&m_UpdateZeroconfMutex); - if (isBonjourRunning()) { - if (!m_AppConfig->wizardShouldRun()) { - if (m_pZeroconfService) { - delete m_pZeroconfService; - m_pZeroconfService = NULL; - } + if (isBonjourRunning()) { + if (!m_AppConfig->wizardShouldRun()) { + if (m_pZeroconfService) { + delete m_pZeroconfService; + m_pZeroconfService = NULL; + } - if (m_AppConfig->autoConfig() || synergyType() == synergyServer) { - m_pZeroconfService = new ZeroconfService(this); - } - } - } + if (m_AppConfig->autoConfig() || synergyType() == synergyServer) { + m_pZeroconfService = new ZeroconfService(this); + } + } + } } void MainWindow::serverDetected(const QString name) { - if (m_pComboServerList->findText(name) == -1) { - // Note: the first added item triggers startSynergy - m_pComboServerList->addItem(name); - } + if (m_pComboServerList->findText(name) == -1) { + // Note: the first added item triggers startSynergy + m_pComboServerList->addItem(name); + } - if (m_pComboServerList->count() > 1) { - m_pComboServerList->show(); - } + if (m_pComboServerList->count() > 1) { + m_pComboServerList->show(); + } } void MainWindow::setEdition(Edition edition) { - setWindowTitle(getEditionName(edition)); - if (m_AppConfig->getCryptoEnabled()) { - m_pSslCertificate = new SslCertificate(this); - m_pSslCertificate->generateCertificate(); - } - updateLocalFingerprint(); - saveSettings(); + setWindowTitle(getEditionName(edition)); + if (m_AppConfig->getCryptoEnabled()) { + m_pSslCertificate = new SslCertificate(this); + m_pSslCertificate->generateCertificate(); + } + updateLocalFingerprint(); + saveSettings(); } -void MainWindow::beginTrial() +void MainWindow::beginTrial(bool isExpiring) { - this->m_trialWidget->show(); + if (isExpiring) { + this->m_trialWidget->show(); + } } -void MainWindow::endTrial() +void MainWindow::endTrial(bool isExpired) { - this->m_trialWidget->hide(); + if (!isExpired) { + this->m_trialWidget->hide(); + } } void MainWindow::updateLocalFingerprint() { - if (m_AppConfig->getCryptoEnabled() && Fingerprint::local().fileExists()) { - m_pLabelFingerprint->setVisible(true); - m_pLabelLocalFingerprint->setVisible(true); - m_pLabelLocalFingerprint->setText(Fingerprint::local().readFirst()); - } - else { - m_pLabelFingerprint->setVisible(false); - m_pLabelLocalFingerprint->setVisible(false); - } + if (m_AppConfig->getCryptoEnabled() && Fingerprint::local().fileExists()) { + m_pLabelFingerprint->setVisible(true); + m_pLabelLocalFingerprint->setVisible(true); + m_pLabelLocalFingerprint->setText(Fingerprint::local().readFirst()); + } + else { + m_pLabelFingerprint->setVisible(false); + m_pLabelLocalFingerprint->setVisible(false); + } } SubscriptionManager& MainWindow::subscriptionManager() const { - return *m_SubscriptionManager; + return *m_SubscriptionManager; } void MainWindow::on_m_pGroupClient_toggled(bool on) { - m_pGroupServer->setChecked(!on); - if (on) { - updateZeroconfService(); - } + m_pGroupServer->setChecked(!on); + if (on) { + updateZeroconfService(); + } } void MainWindow::on_m_pGroupServer_toggled(bool on) { - m_pGroupClient->setChecked(!on); - if (on) { - updateZeroconfService(); - } + m_pGroupClient->setChecked(!on); + if (on) { + updateZeroconfService(); + } } bool MainWindow::on_m_pButtonBrowseConfigFile_clicked() { - QString fileName = QFileDialog::getOpenFileName(this, tr("Browse for a synergys config file"), QString(), synergyConfigFilter); + QString fileName = QFileDialog::getOpenFileName(this, tr("Browse for a synergys config file"), QString(), synergyConfigFilter); - if (!fileName.isEmpty()) - { - m_pLineEditConfigFile->setText(fileName); - return true; - } + if (!fileName.isEmpty()) + { + m_pLineEditConfigFile->setText(fileName); + return true; + } - return false; + return false; } bool MainWindow::on_m_pActionSave_triggered() { - QString fileName = QFileDialog::getSaveFileName(this, tr("Save configuration as...")); + 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; - } + if (!fileName.isEmpty() && !serverConfig().save(fileName)) + { + QMessageBox::warning(this, tr("Save failed"), tr("Could not save configuration to file.")); + return true; + } - return false; + return false; } void MainWindow::on_m_pActionAbout_triggered() { - AboutDialog dlg(this, appPath(appConfig().synergycName())); - dlg.exec(); + AboutDialog dlg(this, appPath(appConfig().synergycName())); + dlg.exec(); } void MainWindow::on_m_pActionSettings_triggered() { - ProcessMode lastProcessMode = appConfig().processMode(); + ProcessMode lastProcessMode = appConfig().processMode(); - SettingsDialog dlg(this, appConfig()); - dlg.exec(); + SettingsDialog dlg(this, appConfig()); + dlg.exec(); - if (lastProcessMode != appConfig().processMode()) - { - onModeChanged(true, true); - } + if (lastProcessMode != appConfig().processMode()) + { + onModeChanged(true, true); + } } void MainWindow::autoAddScreen(const QString name) { - if (!m_ServerConfig.ignoreAutoConfigClient()) { - int r = m_ServerConfig.autoAddScreen(name); - if (r != kAutoAddScreenOk) { - switch (r) { - case kAutoAddScreenManualServer: - showConfigureServer( - tr("Please add the server (%1) to the grid.") - .arg(appConfig().screenName())); - break; - - case kAutoAddScreenManualClient: - showConfigureServer( - tr("Please drag the new client screen (%1) " - "to the desired position on the grid.") - .arg(name)); - break; - } - } - else { - restartSynergy(); - } - } + if (!m_ServerConfig.ignoreAutoConfigClient()) { + int r = m_ServerConfig.autoAddScreen(name); + if (r != kAutoAddScreenOk) { + switch (r) { + case kAutoAddScreenManualServer: + showConfigureServer( + tr("Please add the server (%1) to the grid.") + .arg(appConfig().screenName())); + break; + + case kAutoAddScreenManualClient: + showConfigureServer( + tr("Please drag the new client screen (%1) " + "to the desired position on the grid.") + .arg(name)); + break; + } + } + else { + restartSynergy(); + } + } } void MainWindow::showConfigureServer(const QString& message) { - ServerConfigDialog dlg(this, serverConfig(), appConfig().screenName()); - dlg.message(message); - dlg.exec(); + ServerConfigDialog dlg(this, serverConfig(), appConfig().screenName()); + dlg.message(message); + dlg.exec(); } void MainWindow::on_m_pButtonConfigureServer_clicked() { - showConfigureServer(); + showConfigureServer(); } void MainWindow::on_m_pActivate_triggered() { - ActivationDialog activationDialog(this, appConfig(), subscriptionManager()); - activationDialog.exec(); + ActivationDialog activationDialog(this, appConfig(), subscriptionManager()); + activationDialog.exec(); } void MainWindow::on_m_pButtonApply_clicked() { - restartSynergy(); + restartSynergy(); } #if defined(Q_OS_WIN) bool MainWindow::isServiceRunning(QString name) { - SC_HANDLE hSCManager; - hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); - if (hSCManager == NULL) { - appendLogError("failed to open a service controller manager, error: " + - GetLastError()); - return false; - } - - SC_HANDLE hService; - int length = name.length(); - wchar_t* array = new wchar_t[length + 1]; - name.toWCharArray(array); - array[length] = '\0'; - - hService = OpenService(hSCManager, array, SERVICE_QUERY_STATUS); - - delete[] array; - - if (hService == NULL) { - appendLogDebug("failed to open service: " + name); - return false; - } - - SERVICE_STATUS status; - if (QueryServiceStatus(hService, &status)) { - if (status.dwCurrentState == SERVICE_RUNNING) { - return true; - } - } + SC_HANDLE hSCManager; + hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + if (hSCManager == NULL) { + appendLogError("failed to open a service controller manager, error: " + + GetLastError()); + return false; + } + + SC_HANDLE hService; + int length = name.length(); + wchar_t* array = new wchar_t[length + 1]; + name.toWCharArray(array); + array[length] = '\0'; + + hService = OpenService(hSCManager, array, SERVICE_QUERY_STATUS); + + delete[] array; + + if (hService == NULL) { + appendLogDebug("failed to open service: " + name); + return false; + } + + SERVICE_STATUS status; + if (QueryServiceStatus(hService, &status)) { + if (status.dwCurrentState == SERVICE_RUNNING) { + return true; + } + } #else bool MainWindow::isServiceRunning() { #endif - return false; + return false; } bool MainWindow::isBonjourRunning() { - bool result = false; + bool result = false; #if defined(Q_OS_WIN) - result = isServiceRunning("Bonjour Service"); + result = isServiceRunning("Bonjour Service"); #else - result = true; + result = true; #endif - return result; + return result; } void MainWindow::downloadBonjour() { #if defined(Q_OS_WIN) - QUrl url; - int arch = getProcessorArch(); - if (arch == kProcessorArchWin32) { - url.setUrl(bonjourBaseUrl + bonjourFilename32); - appendLogInfo("downloading 32-bit Bonjour"); - } - else if (arch == kProcessorArchWin64) { - url.setUrl(bonjourBaseUrl + bonjourFilename64); - appendLogInfo("downloading 64-bit Bonjour"); - } - else { - QMessageBox::critical( - this, tr("Synergy"), - tr("Failed to detect system architecture.")); - return; - } - - if (m_pDataDownloader == NULL) { - m_pDataDownloader = new DataDownloader(this); - connect(m_pDataDownloader, SIGNAL(isComplete()), SLOT(installBonjour())); - } - - m_pDataDownloader->download(url); - - if (m_DownloadMessageBox == NULL) { - m_DownloadMessageBox = new QMessageBox(this); - m_DownloadMessageBox->setWindowTitle("Synergy"); - m_DownloadMessageBox->setIcon(QMessageBox::Information); - m_DownloadMessageBox->setText("Installing Bonjour, please wait..."); - m_DownloadMessageBox->setStandardButtons(0); - m_pCancelButton = m_DownloadMessageBox->addButton( - tr("Cancel"), QMessageBox::RejectRole); - } - - m_DownloadMessageBox->exec(); - - if (m_DownloadMessageBox->clickedButton() == m_pCancelButton) { - m_pDataDownloader->cancel(); - } + QUrl url; + int arch = getProcessorArch(); + if (arch == kProcessorArchWin32) { + url.setUrl(bonjourBaseUrl + bonjourFilename32); + appendLogInfo("downloading 32-bit Bonjour"); + } + else if (arch == kProcessorArchWin64) { + url.setUrl(bonjourBaseUrl + bonjourFilename64); + appendLogInfo("downloading 64-bit Bonjour"); + } + else { + QMessageBox::critical( + this, tr("Synergy"), + tr("Failed to detect system architecture.")); + return; + } + + if (m_pDataDownloader == NULL) { + m_pDataDownloader = new DataDownloader(this); + connect(m_pDataDownloader, SIGNAL(isComplete()), SLOT(installBonjour())); + } + + m_pDataDownloader->download(url); + + if (m_DownloadMessageBox == NULL) { + m_DownloadMessageBox = new QMessageBox(this); + m_DownloadMessageBox->setWindowTitle("Synergy"); + m_DownloadMessageBox->setIcon(QMessageBox::Information); + m_DownloadMessageBox->setText("Installing Bonjour, please wait..."); + m_DownloadMessageBox->setStandardButtons(0); + m_pCancelButton = m_DownloadMessageBox->addButton( + tr("Cancel"), QMessageBox::RejectRole); + } + + m_DownloadMessageBox->exec(); + + if (m_DownloadMessageBox->clickedButton() == m_pCancelButton) { + m_pDataDownloader->cancel(); + } #endif } void MainWindow::installBonjour() { #if defined(Q_OS_WIN) - QString tempLocation = QDesktopServices::storageLocation( - QDesktopServices::TempLocation); - QString filename = tempLocation; - filename.append("\\").append(bonjourTargetFilename); - QFile file(filename); - if (!file.open(QIODevice::WriteOnly)) { - m_DownloadMessageBox->hide(); - - QMessageBox::warning( - this, "Synergy", - tr("Failed to download Bonjour installer to location: %1") - .arg(tempLocation)); - return; - } - - file.write(m_pDataDownloader->data()); - file.close(); - - QStringList arguments; - arguments.append("/i"); - QString winFilename = QDir::toNativeSeparators(filename); - arguments.append(winFilename); - arguments.append("/passive"); - if (m_BonjourInstall == NULL) { - m_BonjourInstall = new CommandProcess("msiexec", arguments); - } - - QThread* thread = new QThread; - connect(m_BonjourInstall, SIGNAL(finished()), this, - SLOT(bonjourInstallFinished())); - connect(m_BonjourInstall, SIGNAL(finished()), thread, SLOT(quit())); - connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - - m_BonjourInstall->moveToThread(thread); - thread->start(); - - QMetaObject::invokeMethod(m_BonjourInstall, "run", Qt::QueuedConnection); - - m_DownloadMessageBox->hide(); + QString tempLocation = QDesktopServices::storageLocation( + QDesktopServices::TempLocation); + QString filename = tempLocation; + filename.append("\\").append(bonjourTargetFilename); + QFile file(filename); + if (!file.open(QIODevice::WriteOnly)) { + m_DownloadMessageBox->hide(); + + QMessageBox::warning( + this, "Synergy", + tr("Failed to download Bonjour installer to location: %1") + .arg(tempLocation)); + return; + } + + file.write(m_pDataDownloader->data()); + file.close(); + + QStringList arguments; + arguments.append("/i"); + QString winFilename = QDir::toNativeSeparators(filename); + arguments.append(winFilename); + arguments.append("/passive"); + if (m_BonjourInstall == NULL) { + m_BonjourInstall = new CommandProcess("msiexec", arguments); + } + + QThread* thread = new QThread; + connect(m_BonjourInstall, SIGNAL(finished()), this, + SLOT(bonjourInstallFinished())); + connect(m_BonjourInstall, SIGNAL(finished()), thread, SLOT(quit())); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + + m_BonjourInstall->moveToThread(thread); + thread->start(); + + QMetaObject::invokeMethod(m_BonjourInstall, "run", Qt::QueuedConnection); + + m_DownloadMessageBox->hide(); #endif } void MainWindow::promptAutoConfig() { - if (!isBonjourRunning()) { - int r = QMessageBox::question( - this, tr("Synergy"), - tr("Do you want to enable auto config and install Bonjour?\n\n" - "This feature helps you establish the connection."), - QMessageBox::Yes | QMessageBox::No); + if (!isBonjourRunning()) { + int r = QMessageBox::question( + this, tr("Synergy"), + tr("Do you want to enable auto config and install Bonjour?\n\n" + "This feature helps you establish the connection."), + QMessageBox::Yes | QMessageBox::No); - if (r == QMessageBox::Yes) { - m_AppConfig->setAutoConfig(true); - downloadBonjour(); - } - else { - m_AppConfig->setAutoConfig(false); - m_pCheckBoxAutoConfig->setChecked(false); - } - } + if (r == QMessageBox::Yes) { + m_AppConfig->setAutoConfig(true); + downloadBonjour(); + } + else { + m_AppConfig->setAutoConfig(false); + m_pCheckBoxAutoConfig->setChecked(false); + } + } - m_AppConfig->setAutoConfigPrompted(true); + m_AppConfig->setAutoConfigPrompted(true); } void MainWindow::on_m_pComboServerList_currentIndexChanged(QString ) { - if (m_pComboServerList->count() != 0) { - restartSynergy(); - } + if (m_pComboServerList->count() != 0) { + restartSynergy(); + } } void MainWindow::on_m_pCheckBoxAutoConfig_toggled(bool checked) { - if (!isBonjourRunning() && checked) { - if (!m_SuppressAutoConfigWarning) { - int r = QMessageBox::information( - this, tr("Synergy"), - tr("Auto config feature requires Bonjour.\n\n" - "Do you want to install Bonjour?"), - QMessageBox::Yes | QMessageBox::No); + if (!isBonjourRunning() && checked) { + if (!m_SuppressAutoConfigWarning) { + int r = QMessageBox::information( + this, tr("Synergy"), + tr("Auto config feature requires Bonjour.\n\n" + "Do you want to install Bonjour?"), + QMessageBox::Yes | QMessageBox::No); - if (r == QMessageBox::Yes) { - downloadBonjour(); - } - } + if (r == QMessageBox::Yes) { + downloadBonjour(); + } + } - m_pCheckBoxAutoConfig->setChecked(false); - return; - } + m_pCheckBoxAutoConfig->setChecked(false); + return; + } - m_pLineEditHostname->setDisabled(checked); - appConfig().setAutoConfig(checked); - updateZeroconfService(); + m_pLineEditHostname->setDisabled(checked); + appConfig().setAutoConfig(checked); + updateZeroconfService(); - if (!checked) { - m_pComboServerList->clear(); - m_pComboServerList->hide(); - } + if (!checked) { + m_pComboServerList->clear(); + m_pComboServerList->hide(); + } } void MainWindow::bonjourInstallFinished() { - appendLogInfo("Bonjour install finished"); + appendLogInfo("Bonjour install finished"); - m_pCheckBoxAutoConfig->setChecked(true); + m_pCheckBoxAutoConfig->setChecked(true); } void MainWindow::on_windowShown() { - if (!m_AppConfig->activationHasRun() && (m_AppConfig->edition() == kUnregistered)) { - ActivationDialog activationDialog (this, appConfig(), subscriptionManager()); - activationDialog.exec(); - } + if (!m_AppConfig->activationHasRun() && (m_AppConfig->edition() == kUnregistered)) { + ActivationDialog activationDialog (this, appConfig(), subscriptionManager()); + activationDialog.exec(); + } } QString MainWindow::getProfileRootForArg() { - CoreInterface coreInterface; - QString dir = coreInterface.getProfileDir(); + CoreInterface coreInterface; + QString dir = coreInterface.getProfileDir(); - // HACK: strip our app name since we're returning the root dir. + // HACK: strip our app name since we're returning the root dir. #if defined(Q_OS_WIN) - dir.replace("\\Synergy", ""); + dir.replace("\\Synergy", ""); #else - dir.replace("/.synergy", ""); + dir.replace("/.synergy", ""); #endif - return QString("\"%1\"").arg(dir); + return QString("\"%1\"").arg(dir); } diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 22cddabe6..c00afdba7 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -62,174 +62,174 @@ class SubscriptionManager; class MainWindow : public QMainWindow, public Ui::MainWindowBase { - Q_OBJECT - - friend class QSynergyApplication; - friend class SetupWizard; - friend class ActivationDialog; - friend class SettingsDialog; - - public: - enum qSynergyState - { - synergyDisconnected, - synergyConnecting, - synergyConnected, - synergyTransfering - }; - - enum qSynergyType - { - synergyClient, - synergyServer - }; - - enum qLevel { - Error, - Info - }; - - enum qRuningState { - kStarted, - kStopped - }; - - public: - MainWindow(QSettings& settings, AppConfig& appConfig, - SubscriptionManager& subscriptionManager); - ~MainWindow(); - - public: - void setVisible(bool visible); - int synergyType() const { return m_pGroupClient->isChecked() ? synergyClient : synergyServer; } - int synergyState() const { return m_SynergyState; } - QString hostname() const { return m_pLineEditHostname->text(); } - QString configFilename(); - QString address(); - QString appPath(const QString& name); - void open(); - void clearLog(); - VersionChecker& versionChecker() { return m_VersionChecker; } - QString getScreenName(); - ServerConfig& serverConfig() { return m_ServerConfig; } - void showConfigureServer(const QString& message); - void showConfigureServer() { showConfigureServer(""); } - void autoAddScreen(const QString name); - void updateZeroconfService(); - void serverDetected(const QString name); - void updateLocalFingerprint(); - SubscriptionManager& subscriptionManager() const; - - public slots: - void setEdition(Edition edition); - void beginTrial(); - void endTrial(); - void appendLogRaw(const QString& text); - void appendLogInfo(const QString& text); - void appendLogDebug(const QString& text); - void appendLogError(const QString& text); - void startSynergy(); - - protected slots: - void sslToggled(bool enabled); - void on_m_pGroupClient_toggled(bool on); - void on_m_pGroupServer_toggled(bool on); - bool on_m_pButtonBrowseConfigFile_clicked(); - void on_m_pButtonConfigureServer_clicked(); - bool on_m_pActionSave_triggered(); - void on_m_pActionAbout_triggered(); - void on_m_pActionSettings_triggered(); - void on_m_pActivate_triggered(); - void synergyFinished(int exitCode, QProcess::ExitStatus); - void trayActivated(QSystemTrayIcon::ActivationReason reason); - void stopSynergy(); - void logOutput(); - void logError(); - void updateFound(const QString& version); - void bonjourInstallFinished(); - - protected: - QSettings& settings() { return m_Settings; } - AppConfig& appConfig() { return *m_AppConfig; } - QProcess* synergyProcess() { return m_pSynergy; } - void setSynergyProcess(QProcess* p) { m_pSynergy = p; } - void initConnections(); - void createMenuBar(); - void createStatusBar(); - void createTrayIcon(); - void loadSettings(); - void saveSettings(); - void setIcon(qSynergyState state); - void setSynergyState(qSynergyState state); - bool checkForApp(int which, QString& app); - bool clientArgs(QStringList& args, QString& app); - bool serverArgs(QStringList& args, QString& app); - void setStatus(const QString& status); - void sendIpcMessage(qIpcMessageType type, const char* buffer, bool showErrors); - void onModeChanged(bool startDesktop, bool applyService); - void updateStateFromLogLine(const QString& line); - QString getIPAddresses(); - void stopService(); - void stopDesktop(); - void changeEvent(QEvent* event); - void retranslateMenuBar(); + Q_OBJECT + + friend class QSynergyApplication; + friend class SetupWizard; + friend class ActivationDialog; + friend class SettingsDialog; + + public: + enum qSynergyState + { + synergyDisconnected, + synergyConnecting, + synergyConnected, + synergyTransfering + }; + + enum qSynergyType + { + synergyClient, + synergyServer + }; + + enum qLevel { + Error, + Info + }; + + enum qRuningState { + kStarted, + kStopped + }; + + public: + MainWindow(QSettings& settings, AppConfig& appConfig, + SubscriptionManager& subscriptionManager); + ~MainWindow(); + + public: + void setVisible(bool visible); + int synergyType() const { return m_pGroupClient->isChecked() ? synergyClient : synergyServer; } + int synergyState() const { return m_SynergyState; } + QString hostname() const { return m_pLineEditHostname->text(); } + QString configFilename(); + QString address(); + QString appPath(const QString& name); + void open(); + void clearLog(); + VersionChecker& versionChecker() { return m_VersionChecker; } + QString getScreenName(); + ServerConfig& serverConfig() { return m_ServerConfig; } + void showConfigureServer(const QString& message); + void showConfigureServer() { showConfigureServer(""); } + void autoAddScreen(const QString name); + void updateZeroconfService(); + void serverDetected(const QString name); + void updateLocalFingerprint(); + SubscriptionManager& subscriptionManager() const; + + public slots: + void setEdition(Edition edition); + void beginTrial(bool isExpiring); + void endTrial(bool isExpired); + void appendLogRaw(const QString& text); + void appendLogInfo(const QString& text); + void appendLogDebug(const QString& text); + void appendLogError(const QString& text); + void startSynergy(); + + protected slots: + void sslToggled(bool enabled); + void on_m_pGroupClient_toggled(bool on); + void on_m_pGroupServer_toggled(bool on); + bool on_m_pButtonBrowseConfigFile_clicked(); + void on_m_pButtonConfigureServer_clicked(); + bool on_m_pActionSave_triggered(); + void on_m_pActionAbout_triggered(); + void on_m_pActionSettings_triggered(); + void on_m_pActivate_triggered(); + void synergyFinished(int exitCode, QProcess::ExitStatus); + void trayActivated(QSystemTrayIcon::ActivationReason reason); + void stopSynergy(); + void logOutput(); + void logError(); + void updateFound(const QString& version); + void bonjourInstallFinished(); + + protected: + QSettings& settings() { return m_Settings; } + AppConfig& appConfig() { return *m_AppConfig; } + QProcess* synergyProcess() { return m_pSynergy; } + void setSynergyProcess(QProcess* p) { m_pSynergy = p; } + void initConnections(); + void createMenuBar(); + void createStatusBar(); + void createTrayIcon(); + void loadSettings(); + void saveSettings(); + void setIcon(qSynergyState state); + void setSynergyState(qSynergyState state); + bool checkForApp(int which, QString& app); + bool clientArgs(QStringList& args, QString& app); + bool serverArgs(QStringList& args, QString& app); + void setStatus(const QString& status); + void sendIpcMessage(qIpcMessageType type, const char* buffer, bool showErrors); + void onModeChanged(bool startDesktop, bool applyService); + void updateStateFromLogLine(const QString& line); + QString getIPAddresses(); + void stopService(); + void stopDesktop(); + void changeEvent(QEvent* event); + void retranslateMenuBar(); #if defined(Q_OS_WIN) - bool isServiceRunning(QString name); + bool isServiceRunning(QString name); #else - bool isServiceRunning(); + bool isServiceRunning(); #endif - bool isBonjourRunning(); - void downloadBonjour(); - void promptAutoConfig(); - QString getProfileRootForArg(); - void checkConnected(const QString& line); - void checkFingerprint(const QString& line); - bool autoHide(); - QString getTimeStamp(); - void restartSynergy(); - void proofreadInfo(); - - void showEvent (QShowEvent*); - - private: - QSettings& m_Settings; - AppConfig* m_AppConfig; - SubscriptionManager* m_SubscriptionManager; - QProcess* m_pSynergy; - int m_SynergyState; - ServerConfig m_ServerConfig; - QTemporaryFile* m_pTempConfigFile; - QSystemTrayIcon* m_pTrayIcon; - QMenu* m_pTrayIconMenu; - bool m_AlreadyHidden; - VersionChecker m_VersionChecker; - IpcClient m_IpcClient; - QMenuBar* m_pMenuBar; - QMenu* m_pMenuFile; - QMenu* m_pMenuEdit; - QMenu* m_pMenuWindow; - QMenu* m_pMenuHelp; - ZeroconfService* m_pZeroconfService; - DataDownloader* m_pDataDownloader; - QMessageBox* m_DownloadMessageBox; - QAbstractButton* m_pCancelButton; - QMutex m_UpdateZeroconfMutex; - bool m_SuppressAutoConfigWarning; - CommandProcess* m_BonjourInstall; - bool m_SuppressEmptyServerWarning; - qRuningState m_ExpectedRunningState; - QMutex m_StopDesktopMutex; - SslCertificate* m_pSslCertificate; + bool isBonjourRunning(); + void downloadBonjour(); + void promptAutoConfig(); + QString getProfileRootForArg(); + void checkConnected(const QString& line); + void checkFingerprint(const QString& line); + bool autoHide(); + QString getTimeStamp(); + void restartSynergy(); + void proofreadInfo(); + + void showEvent (QShowEvent*); + + private: + QSettings& m_Settings; + AppConfig* m_AppConfig; + SubscriptionManager* m_SubscriptionManager; + QProcess* m_pSynergy; + int m_SynergyState; + ServerConfig m_ServerConfig; + QTemporaryFile* m_pTempConfigFile; + QSystemTrayIcon* m_pTrayIcon; + QMenu* m_pTrayIconMenu; + bool m_AlreadyHidden; + VersionChecker m_VersionChecker; + IpcClient m_IpcClient; + QMenuBar* m_pMenuBar; + QMenu* m_pMenuFile; + QMenu* m_pMenuEdit; + QMenu* m_pMenuWindow; + QMenu* m_pMenuHelp; + ZeroconfService* m_pZeroconfService; + DataDownloader* m_pDataDownloader; + QMessageBox* m_DownloadMessageBox; + QAbstractButton* m_pCancelButton; + QMutex m_UpdateZeroconfMutex; + bool m_SuppressAutoConfigWarning; + CommandProcess* m_BonjourInstall; + bool m_SuppressEmptyServerWarning; + qRuningState m_ExpectedRunningState; + QMutex m_StopDesktopMutex; + SslCertificate* m_pSslCertificate; private slots: - void on_m_pCheckBoxAutoConfig_toggled(bool checked); - void on_m_pComboServerList_currentIndexChanged(QString ); - void on_m_pButtonApply_clicked(); - void installBonjour(); - void on_windowShown(); + void on_m_pCheckBoxAutoConfig_toggled(bool checked); + void on_m_pComboServerList_currentIndexChanged(QString ); + void on_m_pButtonApply_clicked(); + void installBonjour(); + void on_windowShown(); signals: - void windowShown(); + void windowShown(); }; #endif diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index 86d425364..684ac82ab 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -22,75 +22,82 @@ #include SubscriptionManager::SubscriptionManager(AppConfig* appConfig) : - m_AppConfig(appConfig), - m_serialKey(appConfig->edition()) { - try { - setSerialKey(m_AppConfig->serialKey()); - } catch (...) { - m_AppConfig->setSerialKey(""); - } + m_AppConfig(appConfig), + m_serialKey(appConfig->edition()) { + try { + setSerialKey(m_AppConfig->serialKey()); + } catch (...) { + m_AppConfig->setSerialKey(""); + m_AppConfig->saveSettings(); + } } SerialKey SubscriptionManager::setSerialKey(QString serialKeyString) { - SerialKey serialKey (serialKeyString.toStdString()); - if (!serialKey.isValid (::time(0))) { - throw std::runtime_error ("Invalid serial key"); - } - - if (serialKey != m_serialKey) { - using std::swap; - swap (serialKey, m_serialKey); - - m_AppConfig->setSerialKey (serialKeyString); - notifyActivation ("serial:" + serialKeyString); - emit serialKeyChanged (m_serialKey); - - if (m_serialKey.edition() != serialKey.edition()) { - m_AppConfig->setEdition (m_serialKey.edition()); - emit editionChanged (m_serialKey.edition()); - } - - if (m_serialKey.isTrial() != serialKey.isTrial()) { - if (m_serialKey.isTrial()) { - emit beginTrial(); - } else { - emit endTrial(); - } - } - } - - return serialKey; + SerialKey serialKey (serialKeyString.toStdString()); + if (!serialKey.isValid (::time(0))) { + throw std::runtime_error ("Invalid serial key"); + } + + if (serialKey != m_serialKey) { + using std::swap; + swap (serialKey, m_serialKey); + + m_AppConfig->setSerialKey (serialKeyString); + notifyActivation ("serial:" + serialKeyString); + emit serialKeyChanged (m_serialKey); + + if (m_serialKey.edition() != serialKey.edition()) { + m_AppConfig->setEdition (m_serialKey.edition()); + emit editionChanged (m_serialKey.edition()); + } + + if (serialKey.isTrial()) { + emit endTrial(false); + } + + if (m_serialKey.isTrial()) { + emit beginTrial(m_serialKey.isExpiring(::time(0))); + } + + m_AppConfig->saveSettings(); + } + + return serialKey; } -Edition SubscriptionManager::edition() const +Edition SubscriptionManager::activeLicense() const { - return m_serialKey.edition(); + return m_serialKey.edition(); } -bool SubscriptionManager::isTrial() const +void SubscriptionManager::update() const { - return m_serialKey.isTrial(); + emit serialKeyChanged (m_serialKey); + emit editionChanged (m_serialKey.edition()); + if (m_serialKey.isTrial()) { + emit beginTrial(m_serialKey.isExpiring(::time(0))); + } } void SubscriptionManager::skipActivation() { - notifyActivation ("skip:unknown"); + notifyActivation ("skip:unknown"); } void SubscriptionManager::notifyActivation(QString identity) { - ActivationNotifier* notifier = new ActivationNotifier(); - notifier->setIdentity(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())); + 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(); + notifier->moveToThread(thread); + thread->start(); - QMetaObject::invokeMethod(notifier, "notify", Qt::QueuedConnection); + QMetaObject::invokeMethod(notifier, "notify", Qt::QueuedConnection); } diff --git a/src/gui/src/SubscriptionManager.h b/src/gui/src/SubscriptionManager.h index eff9112d2..56c3e48cb 100644 --- a/src/gui/src/SubscriptionManager.h +++ b/src/gui/src/SubscriptionManager.h @@ -25,25 +25,25 @@ class AppConfig; class SubscriptionManager: public QObject { - Q_OBJECT + Q_OBJECT public: - SubscriptionManager(AppConfig* appConfig); - SerialKey setSerialKey(QString serialKey); - Edition edition() const; - bool isTrial() const; - void skipActivation(); + SubscriptionManager(AppConfig* appConfig); + SerialKey setSerialKey(QString serialKey); + void update() const; + Edition activeLicense() const; + void skipActivation(); private: - void notifyActivation(QString identity); - + void notifyActivation(QString identity); + private: - AppConfig* m_AppConfig; - SerialKey m_serialKey; - + AppConfig* m_AppConfig; + SerialKey m_serialKey; + signals: - void serialKeyChanged (SerialKey); - void editionChanged (Edition); - void beginTrial (); - void endTrial (); + void serialKeyChanged (SerialKey) const; + void editionChanged (Edition) const; + void beginTrial (bool expiring) const; + void endTrial (bool expired) const; }; diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index 7b4f48050..fb4544d49 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -1,11 +1,11 @@ /* * 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 @@ -26,194 +26,194 @@ using namespace std; SerialKey::SerialKey(Edition edition): - m_userLimit(1), - m_warnTime(ULLONG_MAX), - m_expireTime(ULLONG_MAX), - m_edition(edition), - m_trial(false), - m_valid(true) + m_userLimit(1), + m_warnTime(ULLONG_MAX), + m_expireTime(ULLONG_MAX), + m_edition(edition), + m_trial(false), + m_valid(true) { } SerialKey::SerialKey(std::string serial) : - m_userLimit(1), - m_warnTime(0), - m_expireTime(0), - m_edition(Edition::kBasic), - m_trial(true), - m_valid(false) + m_userLimit(1), + m_warnTime(0), + m_expireTime(0), + m_edition(Edition::kBasic), + m_trial(true), + m_valid(false) { - string plainText = decode(serial); - if (!plainText.empty()) { - parse(plainText); - } + string plainText = decode(serial); + if (!plainText.empty()) { + parse(plainText); + } } bool SerialKey::isValid(time_t currentTime) const { - bool result = false; - - if (m_valid) { - if (m_trial) { - if (currentTime < m_expireTime) { - result = true; - } - } - else { - result = true; - } - } - - return result; + bool result = false; + + if (m_valid) { + if (m_trial) { + if (currentTime < m_expireTime) { + result = true; + } + } + else { + result = true; + } + } + + return result; } bool SerialKey::isExpiring(time_t currentTime) const { - bool result = false; - - if (m_valid) { - if (m_warnTime <= currentTime && currentTime < m_expireTime) { - result = true; - } - } - - return result; + bool result = false; + + if (m_valid) { + if (m_warnTime <= currentTime && currentTime < m_expireTime) { + result = true; + } + } + + return result; } bool SerialKey::isExpired(time_t currentTime) const { - bool result = false; - - if (m_valid) { - if (m_expireTime <= currentTime) { - result = true; - } - } - - return result; + bool result = false; + + if (m_valid) { + if (m_expireTime <= currentTime) { + result = true; + } + } + + return result; } bool SerialKey::isTrial() const { - return m_trial; + return m_trial; } -Edition +Edition SerialKey::edition() const { - return m_edition; + return m_edition; } time_t SerialKey::daysLeft(time_t currentTime) const { - unsigned long long timeLeft = 0; - unsigned long long const day = 60 * 60 * 24; - - if (currentTime < m_expireTime) { - timeLeft = m_expireTime - currentTime; - } - - unsigned long long dayLeft = 0; - dayLeft = timeLeft % day != 0 ? 1 : 0; - - return timeLeft / day + dayLeft; + unsigned long long timeLeft = 0; + unsigned long long const day = 60 * 60 * 24; + + if (currentTime < m_expireTime) { + timeLeft = m_expireTime - currentTime; + } + + unsigned long long dayLeft = 0; + dayLeft = timeLeft % day != 0 ? 1 : 0; + + return timeLeft / day + dayLeft; } std::string SerialKey::decode(const std::string& serial) const { - static const char* const lut = "0123456789ABCDEF"; - 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 char* p = std::lower_bound(lut, lut + 16, a); - const char* 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; + static const char* const lut = "0123456789ABCDEF"; + 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 char* p = std::lower_bound(lut, lut + 16, a); + const char* 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; } void SerialKey::parse(std::string plainSerial) { - string parityStart = plainSerial.substr(0, 1); - string parityEnd = plainSerial.substr(plainSerial.length() - 1, 1); - - // check for parity chars { and }, record parity result, then remove them. - if (parityStart == "{" && parityEnd == "}") { - plainSerial = plainSerial.substr(1, plainSerial.length() - 2); - - // tokenize serialised subscription. - vector parts; - std::string::size_type pos = 0; - bool look = true; - while (look) { - std::string::size_type start = pos; - pos = plainSerial.find(";", pos); - if (pos == string::npos) { - pos = plainSerial.length(); - look = false; - } - parts.push_back(plainSerial.substr(start, pos - start)); - pos += 1; - } - - if ((parts.size() == 8) - && (parts.at(0).find("v1") != string::npos)) { - // e.g.: {v1;basic;Bob;1;email;company name;1398297600;1398384000} - m_edition = getEdition(parts.at(1)); - m_name = parts.at(2); - m_trial = false; - sscanf(parts.at(3).c_str(), "%d", &m_userLimit); - m_email = parts.at(4); - m_company = parts.at(5); - sscanf(parts.at(6).c_str(), "%lld", &m_warnTime); - sscanf(parts.at(7).c_str(), "%lld", &m_expireTime); - m_valid = true; - } - else if ((parts.size() == 9) - && (parts.at(0).find("v2") != string::npos)) { - // e.g.: {v2;trial;basic;Bob;1;email;company name;1398297600;1398384000} - m_trial = parts.at(1) == "trial" ? true : false; - m_edition = getEdition(parts.at(2)); - m_name = parts.at(3); - sscanf(parts.at(4).c_str(), "%d", &m_userLimit); - m_email = parts.at(5); - m_company = parts.at(6); - sscanf(parts.at(7).c_str(), "%lld", &m_warnTime); - sscanf(parts.at(8).c_str(), "%lld", &m_expireTime); - m_valid = true; - } - } + string parityStart = plainSerial.substr(0, 1); + string parityEnd = plainSerial.substr(plainSerial.length() - 1, 1); + + // check for parity chars { and }, record parity result, then remove them. + if (parityStart == "{" && parityEnd == "}") { + plainSerial = plainSerial.substr(1, plainSerial.length() - 2); + + // tokenize serialised subscription. + vector parts; + std::string::size_type pos = 0; + bool look = true; + while (look) { + std::string::size_type start = pos; + pos = plainSerial.find(";", pos); + if (pos == string::npos) { + pos = plainSerial.length(); + look = false; + } + parts.push_back(plainSerial.substr(start, pos - start)); + pos += 1; + } + + if ((parts.size() == 8) + && (parts.at(0).find("v1") != string::npos)) { + // e.g.: {v1;basic;Bob;1;email;company name;1398297600;1398384000} + m_edition = parseEdition(parts.at(1)); + m_name = parts.at(2); + m_trial = false; + sscanf(parts.at(3).c_str(), "%d", &m_userLimit); + m_email = parts.at(4); + m_company = parts.at(5); + sscanf(parts.at(6).c_str(), "%lld", &m_warnTime); + sscanf(parts.at(7).c_str(), "%lld", &m_expireTime); + m_valid = true; + } + else if ((parts.size() == 9) + && (parts.at(0).find("v2") != string::npos)) { + // e.g.: {v2;trial;basic;Bob;1;email;company name;1398297600;1398384000} + m_trial = parts.at(1) == "trial" ? true : false; + m_edition = parseEdition(parts.at(2)); + m_name = parts.at(3); + sscanf(parts.at(4).c_str(), "%d", &m_userLimit); + m_email = parts.at(5); + m_company = parts.at(6); + sscanf(parts.at(7).c_str(), "%lld", &m_warnTime); + sscanf(parts.at(8).c_str(), "%lld", &m_expireTime); + m_valid = true; + } + } } Edition -SerialKey::getEdition(std::string editionStr) +SerialKey::parseEdition(std::string editionStr) { - Edition e = Edition::kBasic; - if (editionStr == "pro") { - e = Edition::kPro; - } - - return e; + Edition e = Edition::kBasic; + if (editionStr == "pro") { + e = Edition::kPro; + } + + return e; } diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h index 03d0ab98d..de9a44507 100644 --- a/src/lib/shared/SerialKey.h +++ b/src/lib/shared/SerialKey.h @@ -1,11 +1,11 @@ /* * 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 @@ -26,61 +26,61 @@ #endif class SerialKey { - friend bool operator== (SerialKey const&, SerialKey const&); + friend bool operator== (SerialKey const&, SerialKey const&); public: - explicit SerialKey(Edition edition = Edition::kUnregistered); - explicit SerialKey(std::string serial); - - bool isValid(time_t currentTime) const; - bool isExpiring(time_t currentTime) const; - bool isExpired(time_t currentTime) const; - bool isTrial() const; - time_t daysLeft(time_t currentTime) const; - Edition edition() const; + explicit SerialKey(Edition edition = Edition::kUnregistered); + explicit SerialKey(std::string serial); + + bool isValid(time_t currentTime) const; + bool isExpiring(time_t currentTime) const; + bool isExpired(time_t currentTime) const; + bool isTrial() const; + time_t daysLeft(time_t currentTime) const; + Edition edition() const; private: - std::string decode(const std::string& serial) const; - void parse(std::string plainSerial); - Edition getEdition(std::string editionStr); + std::string decode(const std::string& serial) const; + void parse(std::string plainSerial); + Edition parseEdition(std::string editionStr); #ifdef TEST_ENV private: - FRIEND_TEST(SerialKeyTests, decode_empty_returnEmptyString); - FRIEND_TEST(SerialKeyTests, decode_invalidDigit_returnEmptyString); - FRIEND_TEST(SerialKeyTests, decode_validSerial_returnPlainText); - FRIEND_TEST(SerialKeyTests, parse_noParty_invalid); - FRIEND_TEST(SerialKeyTests, parse_invalidPartsLenghth_invalid); - FRIEND_TEST(SerialKeyTests, parse_validV1Serial_valid); - FRIEND_TEST(SerialKeyTests, parse_validV2Serial_valid); + FRIEND_TEST(SerialKeyTests, decode_empty_returnEmptyString); + FRIEND_TEST(SerialKeyTests, decode_invalidDigit_returnEmptyString); + FRIEND_TEST(SerialKeyTests, decode_validSerial_returnPlainText); + FRIEND_TEST(SerialKeyTests, parse_noParty_invalid); + FRIEND_TEST(SerialKeyTests, parse_invalidPartsLenghth_invalid); + FRIEND_TEST(SerialKeyTests, parse_validV1Serial_valid); + FRIEND_TEST(SerialKeyTests, parse_validV2Serial_valid); #endif - + private: - std::string m_name; - std::string m_email; - std::string m_company; - unsigned m_userLimit; - unsigned long long m_warnTime; - unsigned long long m_expireTime; - Edition m_edition; - bool m_trial; - bool m_valid; + std::string m_name; + std::string m_email; + std::string m_company; + unsigned m_userLimit; + unsigned long long m_warnTime; + unsigned long long m_expireTime; + Edition m_edition; + bool m_trial; + bool m_valid; }; inline bool operator== (SerialKey const& lhs, SerialKey const& rhs) { - return (lhs.m_name == rhs.m_name) && - (lhs.m_email == rhs.m_email) && - (lhs.m_company == rhs.m_company) && - (lhs.m_userLimit == rhs.m_userLimit) && - (lhs.m_warnTime == rhs.m_warnTime) && - (lhs.m_expireTime == rhs.m_expireTime) && - (lhs.m_edition == rhs.m_edition) && - (lhs.m_trial == rhs.m_trial) && - (lhs.m_valid == rhs.m_valid); + return (lhs.m_name == rhs.m_name) && + (lhs.m_email == rhs.m_email) && + (lhs.m_company == rhs.m_company) && + (lhs.m_userLimit == rhs.m_userLimit) && + (lhs.m_warnTime == rhs.m_warnTime) && + (lhs.m_expireTime == rhs.m_expireTime) && + (lhs.m_edition == rhs.m_edition) && + (lhs.m_trial == rhs.m_trial) && + (lhs.m_valid == rhs.m_valid); } inline bool operator!= (SerialKey const& lhs, SerialKey const& rhs) { - return !(lhs == rhs); + return !(lhs == rhs); } From 3ee9ac5d49a12f0f33acc6898e42510d59cb7a47 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Sat, 15 Oct 2016 15:48:07 +0100 Subject: [PATCH 136/241] #5657 Remove C++11 enum qualifier --- src/lib/shared/SerialKey.cpp | 6 +++--- src/lib/shared/SerialKey.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index fb4544d49..77b353dd7 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -39,7 +39,7 @@ SerialKey::SerialKey(std::string serial) : m_userLimit(1), m_warnTime(0), m_expireTime(0), - m_edition(Edition::kBasic), + m_edition(kBasic), m_trial(true), m_valid(false) { @@ -210,9 +210,9 @@ SerialKey::parse(std::string plainSerial) Edition SerialKey::parseEdition(std::string editionStr) { - Edition e = Edition::kBasic; + Edition e = kBasic; if (editionStr == "pro") { - e = Edition::kPro; + e = kPro; } return e; diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h index de9a44507..01884d863 100644 --- a/src/lib/shared/SerialKey.h +++ b/src/lib/shared/SerialKey.h @@ -28,7 +28,7 @@ class SerialKey { friend bool operator== (SerialKey const&, SerialKey const&); public: - explicit SerialKey(Edition edition = Edition::kUnregistered); + explicit SerialKey(Edition edition = kUnregistered); explicit SerialKey(std::string serial); bool isValid(time_t currentTime) const; From b20d04d80c6684a1f829efd7a4283dc28450d093 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Sat, 15 Oct 2016 15:59:27 +0100 Subject: [PATCH 137/241] #5657 Add missing include for runtime_error --- src/gui/src/SubscriptionManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index 684ac82ab..17a1b4322 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -19,6 +19,7 @@ #include "EditionType.h" #include "AppConfig.h" #include +#include #include SubscriptionManager::SubscriptionManager(AppConfig* appConfig) : From 0dd0e65e2a100ce8021e82a040cacaa9eb25c423 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Sat, 15 Oct 2016 16:07:05 +0100 Subject: [PATCH 138/241] #5657 Remove more C++11 enum qualifiers --- src/gui/src/ActivationDialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 9d3b43f33..62bc0e235 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -39,7 +39,7 @@ ActivationDialog::~ActivationDialog() void ActivationDialog::reject() { - if (m_subscriptionManager->activeLicense() == Edition::kUnregistered) { + if (m_subscriptionManager->activeLicense() == kUnregistered) { CancelActivationDialog cancelActivationDialog(this); if (QDialog::Accepted == cancelActivationDialog.exec()) { m_subscriptionManager->skipActivation(); @@ -71,7 +71,7 @@ void ActivationDialog::accept() return; } - if (m_subscriptionManager->activeLicense() != Edition::kUnregistered) { + if (m_subscriptionManager->activeLicense() != kUnregistered) { message.information(this, "Activated!", tr("Thanks for activating %1!").arg (getEditionName(m_subscriptionManager->activeLicense()))); From e14ff8935b6153db8e56ad8f7e589021050fb54e Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Sat, 15 Oct 2016 16:25:04 +0100 Subject: [PATCH 139/241] #5657 Fix SerialKey unit test --- src/test/unittests/shared/SerialKeyTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/unittests/shared/SerialKeyTests.cpp b/src/test/unittests/shared/SerialKeyTests.cpp index 9535e15df..a16be1e20 100644 --- a/src/test/unittests/shared/SerialKeyTests.cpp +++ b/src/test/unittests/shared/SerialKeyTests.cpp @@ -135,7 +135,7 @@ TEST(SerialKeyTests, isExpiring_validV2TrialBasicSerial_returnFalse) // {v2;trial;basic;Bob;1;email;company name;0;86400} SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); EXPECT_EQ(true, serial.isTrial()); - EXPECT_FALSE(serial.isExpiring(0)); + EXPECT_TRUE(serial.isExpiring(0)); EXPECT_EQ(kBasic, serial.edition()); } From e05ced287cbf980a6cd489b033175b92771438c0 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 17 Oct 2016 11:57:32 +0100 Subject: [PATCH 140/241] #5657 Enable external links on trial label --- src/gui/res/MainWindowBase.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/res/MainWindowBase.ui b/src/gui/res/MainWindowBase.ui index ba00f2d25..ac70cfd96 100644 --- a/src/gui/res/MainWindowBase.ui +++ b/src/gui/res/MainWindowBase.ui @@ -57,6 +57,9 @@ <html><head/><body><p><span style=" font-weight:600;">6</span> days of your Synergy Pro trial remain. <a href="http://symless.com/pricing?src=gui"><span style=" text-decoration: underline; color:#0000ff;">Buy now!</span></a></p></body></html> + + true +
From 714b2f64408f9b019cb2f0e35a1abf3b971c914c Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 17 Oct 2016 15:26:42 +0100 Subject: [PATCH 141/241] #5657 Make trial expiry notification live --- src/gui/res/MainWindowBase.ui | 2 +- src/gui/src/ActivationDialog.cpp | 21 ++++++++---- src/gui/src/MainWindow.cpp | 20 +++++++++-- src/gui/src/QUtility.cpp | 13 -------- src/gui/src/QUtility.h | 1 - src/gui/src/SubscriptionManager.cpp | 58 +++++++++++++++++++++++++++----- src/gui/src/SubscriptionManager.h | 10 ++++-- src/lib/shared/SerialKey.cpp | 66 ++++++++++++++++++++++++++++++++++--- src/lib/shared/SerialKey.h | 5 ++- 9 files changed, 154 insertions(+), 42 deletions(-) diff --git a/src/gui/res/MainWindowBase.ui b/src/gui/res/MainWindowBase.ui index ac70cfd96..b3fb0a75b 100644 --- a/src/gui/res/MainWindowBase.ui +++ b/src/gui/res/MainWindowBase.ui @@ -55,7 +55,7 @@ - <html><head/><body><p><span style=" font-weight:600;">6</span> days of your Synergy Pro trial remain. <a href="http://symless.com/pricing?src=gui"><span style=" text-decoration: underline; color:#0000ff;">Buy now!</span></a></p></body></html> + <html><head/><body><p><span style=" font-weight:600;">%1</span> days of your Synergy Pro trial remain. <a href="http://symless.com/pricing?src=gui"><span style=" text-decoration: underline; color:#0000ff;">Buy now!</span></a></p></body></html> true diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 62bc0e235..1dbef32e9 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -39,7 +39,7 @@ ActivationDialog::~ActivationDialog() void ActivationDialog::reject() { - if (m_subscriptionManager->activeLicense() == kUnregistered) { + if (m_subscriptionManager->activeEdition() == kUnregistered) { CancelActivationDialog cancelActivationDialog(this); if (QDialog::Accepted == cancelActivationDialog.exec()) { m_subscriptionManager->skipActivation(); @@ -53,28 +53,35 @@ void ActivationDialog::reject() void ActivationDialog::accept() { QMessageBox message; - QString error; - m_appConfig->activationHasRun(true); m_appConfig->saveSettings(); + std::pair result; try { QString serialKey = ui->m_pTextEditSerialKey->toPlainText(); - m_subscriptionManager->setSerialKey(serialKey); + result = m_subscriptionManager->setSerialKey(serialKey); } catch (std::exception& e) { message.critical(this, "Unknown Error", tr("An error occurred while trying to activate Synergy. " "Please contact the helpdesk, and provide the " - "following details.\n\n%1").arg(e.what())); + "following information:\n\n%1").arg(e.what())); refreshSerialKey(); return; } - if (m_subscriptionManager->activeLicense() != kUnregistered) { + if (!result.first) { + message.critical(this, "Activation failed", + tr("%1").arg(result.second)); + refreshSerialKey(); + return; + } + + if (m_subscriptionManager->activeEdition() != kUnregistered) { message.information(this, "Activated!", tr("Thanks for activating %1!").arg - (getEditionName(m_subscriptionManager->activeLicense()))); + (m_subscriptionManager->activeEditionName())); } + QDialog::accept(); } diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index ad9d37d04..762446276 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -154,7 +154,7 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig, connect (m_AppConfig, SIGNAL(sslToggled(bool)), this, SLOT(sslToggled(bool)), Qt::QueuedConnection); - m_SubscriptionManager->update(); + m_SubscriptionManager->refresh(); } MainWindow::~MainWindow() @@ -547,6 +547,10 @@ void MainWindow::startSynergy() args << "--name" << getScreenName(); + if (!appConfig().serialKey().isEmpty()) { + args << "--serial-key " << appConfig().serialKey(); + } + if (desktopMode) { setSynergyProcess(new QProcess(this)); @@ -1038,7 +1042,7 @@ void MainWindow::serverDetected(const QString name) void MainWindow::setEdition(Edition edition) { - setWindowTitle(getEditionName(edition)); + setWindowTitle(m_SubscriptionManager->getEditionName (edition)); if (m_AppConfig->getCryptoEnabled()) { m_pSslCertificate = new SslCertificate(this); m_pSslCertificate->generateCertificate(); @@ -1050,8 +1054,19 @@ void MainWindow::setEdition(Edition edition) void MainWindow::beginTrial(bool isExpiring) { if (isExpiring) { + QString expiringNotice = "

%1 days of " + "your Synergy Pro trial remain. " + "Buy now!" + "

"; + expiringNotice = expiringNotice.arg + (m_SubscriptionManager->serialKey().daysLeft(::time(0))); + this->m_trialLabel->setText(expiringNotice); this->m_trialWidget->show(); } + setWindowTitle (m_SubscriptionManager->activeEditionName()); } void MainWindow::endTrial(bool isExpired) @@ -1059,6 +1074,7 @@ void MainWindow::endTrial(bool isExpired) if (!isExpired) { this->m_trialWidget->hide(); } + setWindowTitle (m_SubscriptionManager->activeEditionName()); } void MainWindow::updateLocalFingerprint() diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index c35f96388..589e74cfd 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -43,19 +43,6 @@ void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData) } } -QString -getEditionName (int edition) { - if (edition == kBasic) { - return "Synergy Basic"; - } - else if (edition == kPro) { - return "Synergy Pro"; - } - else { - return "Synergy (UNREGISTERED)"; - } -} - QString hash(const QString& string) { QByteArray data = string.toUtf8(); diff --git a/src/gui/src/QUtility.h b/src/gui/src/QUtility.h index ca00d06fa..0738d96cc 100644 --- a/src/gui/src/QUtility.h +++ b/src/gui/src/QUtility.h @@ -29,4 +29,3 @@ QString hash(const QString& string); QString getFirstMacAddress(); qProcessorArch getProcessorArch(); QString getOSInformation(); -QString getEditionName (int edition); diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index 17a1b4322..b42cc98db 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -20,6 +20,7 @@ #include "AppConfig.h" #include #include +#include #include SubscriptionManager::SubscriptionManager(AppConfig* appConfig) : @@ -28,18 +29,24 @@ SubscriptionManager::SubscriptionManager(AppConfig* appConfig) : try { setSerialKey(m_AppConfig->serialKey()); } catch (...) { + /* Remove garbage serial keys from the registry */ m_AppConfig->setSerialKey(""); + m_AppConfig->setEdition(kUnregistered); m_AppConfig->saveSettings(); } } -SerialKey +std::pair SubscriptionManager::setSerialKey(QString serialKeyString) { + std::pair ret (true, ""); + SerialKey serialKey (serialKeyString.toStdString()); - if (!serialKey.isValid (::time(0))) { - throw std::runtime_error ("Invalid serial key"); - } + if (serialKey.isExpired(::time(0))) { + ret.first = false; + ret.second = "Serial key expired"; + return ret; + } if (serialKey != m_serialKey) { using std::swap; @@ -65,15 +72,28 @@ SubscriptionManager::setSerialKey(QString serialKeyString) m_AppConfig->saveSettings(); } - return serialKey; + return ret; +} + +Edition +SubscriptionManager::activeEdition() const +{ + return m_serialKey.edition(); } -Edition SubscriptionManager::activeLicense() const +QString +SubscriptionManager::activeEditionName() const { - return m_serialKey.edition(); + return getEditionName(activeEdition(), m_serialKey.isTrial()); } -void SubscriptionManager::update() const +SerialKey +SubscriptionManager::serialKey() const +{ + return m_serialKey; +} + +void SubscriptionManager::refresh() const { emit serialKeyChanged (m_serialKey); emit editionChanged (m_serialKey.edition()); @@ -84,7 +104,27 @@ void SubscriptionManager::update() const void SubscriptionManager::skipActivation() { - notifyActivation ("skip:unknown"); + notifyActivation ("skip:unknown"); +} + +QString +SubscriptionManager::getEditionName(Edition const edition, bool trial) +{ + std::string name ("Synergy "); + switch (edition) { + case kUnregistered: + name += "(UNREGISTERED)"; + return QString::fromUtf8 (name.c_str(), name.size()); + case kBasic: + name += "Basic"; + break; + default: + name += "Pro"; + } + if (trial) { + name += " (Trial)"; + } + return QString::fromUtf8 (name.c_str(), name.size()); } void SubscriptionManager::notifyActivation(QString identity) diff --git a/src/gui/src/SubscriptionManager.h b/src/gui/src/SubscriptionManager.h index 56c3e48cb..5023ddc2b 100644 --- a/src/gui/src/SubscriptionManager.h +++ b/src/gui/src/SubscriptionManager.h @@ -20,6 +20,7 @@ #include #include #include +#include class AppConfig; @@ -29,10 +30,13 @@ class SubscriptionManager: public QObject public: SubscriptionManager(AppConfig* appConfig); - SerialKey setSerialKey(QString serialKey); - void update() const; - Edition activeLicense() const; + std::pair setSerialKey(QString serialKey); + void refresh() const; + Edition activeEdition() const; + QString activeEditionName() const; + SerialKey serialKey() const; void skipActivation(); + static QString getEditionName(Edition edition, bool trial = false); private: void notifyActivation(QString identity); diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index 77b353dd7..ef3588efc 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -22,6 +22,9 @@ #include #include #include +#include +#include +#include using namespace std; @@ -47,6 +50,9 @@ SerialKey::SerialKey(std::string serial) : if (!plainText.empty()) { parse(plainText); } + if (!m_valid) { + throw std::runtime_error ("Invalid serial key"); + } } bool @@ -105,7 +111,51 @@ SerialKey::isTrial() const Edition SerialKey::edition() const { - return m_edition; + return m_edition; +} + +std::string +SerialKey::editionString() const +{ + switch (edition()) { + case kBasic: + return "basic"; + case kPro: + return "pro"; + default: { + std::ostringstream oss; + oss << static_cast(edition()); + return oss.str(); + } + } +} + +static std::string +hexEncode (std::string const& str) { + std::ostringstream oss; + for (size_t i = 0; i < str.size(); ++i) { + int c = str[i]; + oss << std::setfill('0') << std::hex << std::setw(2) + << std::uppercase; + oss << c; + } + return oss.str(); +} + +std::string +SerialKey::toString() const +{ + std::ostringstream oss; + oss << "v2;"; + oss << (isTrial() ? "trial" : "lifetime") << ";"; + oss << editionString() << ";"; + oss << m_name << ";"; + oss << m_userLimit << ";"; + oss << m_email << ";"; + oss << m_company << ";"; + oss << m_warnTime << ";"; + oss << m_expireTime; + return hexEncode(oss.str()); } time_t @@ -118,10 +168,16 @@ SerialKey::daysLeft(time_t currentTime) const timeLeft = m_expireTime - currentTime; } - unsigned long long dayLeft = 0; - dayLeft = timeLeft % day != 0 ? 1 : 0; + unsigned long long daysLeft = 0; + daysLeft = timeLeft % day != 0 ? 1 : 0; - return timeLeft / day + dayLeft; + return timeLeft / day + daysLeft; +} + +std::string +SerialKey::email() const +{ + return m_email; } std::string @@ -208,7 +264,7 @@ SerialKey::parse(std::string plainSerial) } Edition -SerialKey::parseEdition(std::string editionStr) +SerialKey::parseEdition(std::string const& editionStr) { Edition e = kBasic; if (editionStr == "pro") { diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h index 01884d863..dd73ad160 100644 --- a/src/lib/shared/SerialKey.h +++ b/src/lib/shared/SerialKey.h @@ -36,12 +36,15 @@ class SerialKey { bool isExpired(time_t currentTime) const; bool isTrial() const; time_t daysLeft(time_t currentTime) const; + std::string email() const; Edition edition() const; + std::string toString() const; private: std::string decode(const std::string& serial) const; void parse(std::string plainSerial); - Edition parseEdition(std::string editionStr); + Edition parseEdition(const std::string& editionStr); + std::string editionString() const; #ifdef TEST_ENV private: From 7eefa49c7772aafb230b3533102b65438b444e52 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 17 Oct 2016 16:12:33 +0100 Subject: [PATCH 142/241] #5657 Fix SerialKey construction in unit tests --- src/lib/shared/SerialKey.cpp | 4 +++- src/lib/shared/SerialKey.h | 5 +++-- src/test/unittests/shared/SerialKeyTests.cpp | 17 +++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index ef3588efc..51ee83db1 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -181,7 +181,7 @@ SerialKey::email() const } std::string -SerialKey::decode(const std::string& serial) const +SerialKey::decode(const std::string& serial) { static const char* const lut = "0123456789ABCDEF"; string output; @@ -215,6 +215,8 @@ SerialKey::parse(std::string plainSerial) string parityStart = plainSerial.substr(0, 1); string parityEnd = plainSerial.substr(plainSerial.length() - 1, 1); + m_valid = false; + // check for parity chars { and }, record parity result, then remove them. if (parityStart == "{" && parityEnd == "}") { plainSerial = plainSerial.substr(1, plainSerial.length() - 2); diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h index dd73ad160..ed0045c8a 100644 --- a/src/lib/shared/SerialKey.h +++ b/src/lib/shared/SerialKey.h @@ -40,10 +40,11 @@ class SerialKey { Edition edition() const; std::string toString() const; + static std::string decode(const std::string& serial); + static Edition parseEdition(const std::string& editionStr); + private: - std::string decode(const std::string& serial) const; void parse(std::string plainSerial); - Edition parseEdition(const std::string& editionStr); std::string editionString() const; #ifdef TEST_ENV diff --git a/src/test/unittests/shared/SerialKeyTests.cpp b/src/test/unittests/shared/SerialKeyTests.cpp index a16be1e20..0f072676e 100644 --- a/src/test/unittests/shared/SerialKeyTests.cpp +++ b/src/test/unittests/shared/SerialKeyTests.cpp @@ -23,42 +23,39 @@ TEST(SerialKeyTests, decode_empty_returnEmptyString) { - SerialKey serial(""); - std::string plainText = serial.decode(""); + std::string plainText = SerialKey::decode(""); EXPECT_EQ(0, plainText.size()); } TEST(SerialKeyTests, decode_invalidDigit_returnEmptyString) { - SerialKey serial(""); - std::string plainText = serial.decode("MOCKZ"); + std::string plainText = SerialKey::decode("MOCKZ"); EXPECT_EQ(0, plainText.size()); } TEST(SerialKeyTests, decode_validSerial_returnPlainText) { - SerialKey serial(""); - std::string plainText = serial.decode("53796E6572677920726F636B7321"); + std::string plainText = SerialKey::decode("53796E6572677920726F636B7321"); EXPECT_EQ("Synergy rocks!", plainText); } TEST(SerialKeyTests, parse_noParty_invalid) { - SerialKey serial(""); + SerialKey serial; serial.parse("MOCK"); EXPECT_FALSE(serial.isValid(0)); } TEST(SerialKeyTests, parse_invalidPartsLenghth_invalid) { - SerialKey serial(""); + SerialKey serial; serial.parse("{Synergy;Rocks}"); EXPECT_FALSE(serial.isValid(0)); } TEST(SerialKeyTests, parse_validV1Serial_valid) { - SerialKey serial(""); + SerialKey serial; serial.parse("{v1;basic;Bob;1;email;company name;0;86400}"); EXPECT_EQ(true, serial.isValid(0)); EXPECT_EQ(kBasic, serial.edition()); @@ -69,7 +66,7 @@ TEST(SerialKeyTests, parse_validV1Serial_valid) TEST(SerialKeyTests, parse_validV2Serial_valid) { - SerialKey serial(""); + SerialKey serial; serial.parse("{v2;trial;pro;Bob;1;email;company name;0;86400}"); EXPECT_EQ(true, serial.isValid(0)); EXPECT_EQ(kPro, serial.edition()); From b5a6ae0a944020d0ffe537f711ab83c8aeecf837 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 17 Oct 2016 16:27:40 +0100 Subject: [PATCH 143/241] #5657 Fix SerialKey expiring unit test --- src/test/unittests/shared/SerialKeyTests.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/unittests/shared/SerialKeyTests.cpp b/src/test/unittests/shared/SerialKeyTests.cpp index 0f072676e..dfb8685ba 100644 --- a/src/test/unittests/shared/SerialKeyTests.cpp +++ b/src/test/unittests/shared/SerialKeyTests.cpp @@ -1,11 +1,11 @@ /* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2016 Symless 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 @@ -129,10 +129,10 @@ TEST(SerialKeyTests, isValid_expiredV2TrialProSerial_invalid) TEST(SerialKeyTests, isExpiring_validV2TrialBasicSerial_returnFalse) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + // {v2;trial;basic;Bob;1;email;company name;1;86400} + SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B313B38363430307D"); EXPECT_EQ(true, serial.isTrial()); - EXPECT_TRUE(serial.isExpiring(0)); + EXPECT_FALSE(serial.isExpiring(0)); EXPECT_EQ(kBasic, serial.edition()); } From c7dc198d824153fcf37b78c89646c8f65aeff62a Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 17 Oct 2016 16:34:44 +0100 Subject: [PATCH 144/241] #5657 Fix SerialKey whitespace --- src/lib/shared/SerialKey.cpp | 256 +++++++++++++++++++++---------------------- src/lib/shared/SerialKey.h | 76 ++++++------- 2 files changed, 166 insertions(+), 166 deletions(-) diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index 51ee83db1..1ee077eb6 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -29,27 +29,27 @@ using namespace std; SerialKey::SerialKey(Edition edition): - m_userLimit(1), - m_warnTime(ULLONG_MAX), - m_expireTime(ULLONG_MAX), - m_edition(edition), - m_trial(false), - m_valid(true) + m_userLimit(1), + m_warnTime(ULLONG_MAX), + m_expireTime(ULLONG_MAX), + m_edition(edition), + m_trial(false), + m_valid(true) { } SerialKey::SerialKey(std::string serial) : - m_userLimit(1), - m_warnTime(0), - m_expireTime(0), - m_edition(kBasic), - m_trial(true), - m_valid(false) + m_userLimit(1), + m_warnTime(0), + m_expireTime(0), + m_edition(kBasic), + m_trial(true), + m_valid(false) { - string plainText = decode(serial); - if (!plainText.empty()) { - parse(plainText); - } + string plainText = decode(serial); + if (!plainText.empty()) { + parse(plainText); + } if (!m_valid) { throw std::runtime_error ("Invalid serial key"); } @@ -58,54 +58,54 @@ SerialKey::SerialKey(std::string serial) : bool SerialKey::isValid(time_t currentTime) const { - bool result = false; - - if (m_valid) { - if (m_trial) { - if (currentTime < m_expireTime) { - result = true; - } - } - else { - result = true; - } - } - - return result; + bool result = false; + + if (m_valid) { + if (m_trial) { + if (currentTime < m_expireTime) { + result = true; + } + } + else { + result = true; + } + } + + return result; } bool SerialKey::isExpiring(time_t currentTime) const { - bool result = false; + bool result = false; - if (m_valid) { - if (m_warnTime <= currentTime && currentTime < m_expireTime) { - result = true; - } - } + if (m_valid) { + if (m_warnTime <= currentTime && currentTime < m_expireTime) { + result = true; + } + } - return result; + return result; } bool SerialKey::isExpired(time_t currentTime) const { - bool result = false; + bool result = false; - if (m_valid) { - if (m_expireTime <= currentTime) { - result = true; - } - } + if (m_valid) { + if (m_expireTime <= currentTime) { + result = true; + } + } - return result; + return result; } bool SerialKey::isTrial() const { - return m_trial; + return m_trial; } Edition @@ -135,7 +135,7 @@ hexEncode (std::string const& str) { std::ostringstream oss; for (size_t i = 0; i < str.size(); ++i) { int c = str[i]; - oss << std::setfill('0') << std::hex << std::setw(2) + oss << std::setfill('0') << std::hex << std::setw(2) << std::uppercase; oss << c; } @@ -161,20 +161,20 @@ SerialKey::toString() const time_t SerialKey::daysLeft(time_t currentTime) const { - unsigned long long timeLeft = 0; - unsigned long long const day = 60 * 60 * 24; + unsigned long long timeLeft = 0; + unsigned long long const day = 60 * 60 * 24; - if (currentTime < m_expireTime) { - timeLeft = m_expireTime - currentTime; - } + if (currentTime < m_expireTime) { + timeLeft = m_expireTime - currentTime; + } - unsigned long long daysLeft = 0; - daysLeft = timeLeft % day != 0 ? 1 : 0; + unsigned long long daysLeft = 0; + daysLeft = timeLeft % day != 0 ? 1 : 0; return timeLeft / day + daysLeft; } -std::string +std::string SerialKey::email() const { return m_email; @@ -183,95 +183,95 @@ SerialKey::email() const std::string SerialKey::decode(const std::string& serial) { - static const char* const lut = "0123456789ABCDEF"; - string output; - size_t len = serial.length(); - if (len & 1) { - return output; - } + static const char* const lut = "0123456789ABCDEF"; + 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) { + output.reserve(len / 2); + for (size_t i = 0; i < len; i += 2) { - char a = serial[i]; - char b = serial[i + 1]; + char a = serial[i]; + char b = serial[i + 1]; - const char* p = std::lower_bound(lut, lut + 16, a); - const char* q = std::lower_bound(lut, lut + 16, b); + const char* p = std::lower_bound(lut, lut + 16, a); + const char* q = std::lower_bound(lut, lut + 16, b); - if (*q != b || *p != a) { - return output; - } + if (*q != b || *p != a) { + return output; + } - output.push_back(static_cast(((p - lut) << 4) | (q - lut))); - } + output.push_back(static_cast(((p - lut) << 4) | (q - lut))); + } - return output; + return output; } void SerialKey::parse(std::string plainSerial) { - string parityStart = plainSerial.substr(0, 1); - string parityEnd = plainSerial.substr(plainSerial.length() - 1, 1); - - m_valid = false; - - // check for parity chars { and }, record parity result, then remove them. - if (parityStart == "{" && parityEnd == "}") { - plainSerial = plainSerial.substr(1, plainSerial.length() - 2); - - // tokenize serialised subscription. - vector parts; - std::string::size_type pos = 0; - bool look = true; - while (look) { - std::string::size_type start = pos; - pos = plainSerial.find(";", pos); - if (pos == string::npos) { - pos = plainSerial.length(); - look = false; - } - parts.push_back(plainSerial.substr(start, pos - start)); - pos += 1; - } - - if ((parts.size() == 8) - && (parts.at(0).find("v1") != string::npos)) { - // e.g.: {v1;basic;Bob;1;email;company name;1398297600;1398384000} - m_edition = parseEdition(parts.at(1)); - m_name = parts.at(2); - m_trial = false; - sscanf(parts.at(3).c_str(), "%d", &m_userLimit); - m_email = parts.at(4); - m_company = parts.at(5); - sscanf(parts.at(6).c_str(), "%lld", &m_warnTime); - sscanf(parts.at(7).c_str(), "%lld", &m_expireTime); - m_valid = true; - } - else if ((parts.size() == 9) - && (parts.at(0).find("v2") != string::npos)) { - // e.g.: {v2;trial;basic;Bob;1;email;company name;1398297600;1398384000} - m_trial = parts.at(1) == "trial" ? true : false; - m_edition = parseEdition(parts.at(2)); - m_name = parts.at(3); - sscanf(parts.at(4).c_str(), "%d", &m_userLimit); - m_email = parts.at(5); - m_company = parts.at(6); - sscanf(parts.at(7).c_str(), "%lld", &m_warnTime); - sscanf(parts.at(8).c_str(), "%lld", &m_expireTime); - m_valid = true; - } - } + string parityStart = plainSerial.substr(0, 1); + string parityEnd = plainSerial.substr(plainSerial.length() - 1, 1); + + m_valid = false; + + // check for parity chars { and }, record parity result, then remove them. + if (parityStart == "{" && parityEnd == "}") { + plainSerial = plainSerial.substr(1, plainSerial.length() - 2); + + // tokenize serialised subscription. + vector parts; + std::string::size_type pos = 0; + bool look = true; + while (look) { + std::string::size_type start = pos; + pos = plainSerial.find(";", pos); + if (pos == string::npos) { + pos = plainSerial.length(); + look = false; + } + parts.push_back(plainSerial.substr(start, pos - start)); + pos += 1; + } + + if ((parts.size() == 8) + && (parts.at(0).find("v1") != string::npos)) { + // e.g.: {v1;basic;Bob;1;email;company name;1398297600;1398384000} + m_edition = parseEdition(parts.at(1)); + m_name = parts.at(2); + m_trial = false; + sscanf(parts.at(3).c_str(), "%d", &m_userLimit); + m_email = parts.at(4); + m_company = parts.at(5); + sscanf(parts.at(6).c_str(), "%lld", &m_warnTime); + sscanf(parts.at(7).c_str(), "%lld", &m_expireTime); + m_valid = true; + } + else if ((parts.size() == 9) + && (parts.at(0).find("v2") != string::npos)) { + // e.g.: {v2;trial;basic;Bob;1;email;company name;1398297600;1398384000} + m_trial = parts.at(1) == "trial" ? true : false; + m_edition = parseEdition(parts.at(2)); + m_name = parts.at(3); + sscanf(parts.at(4).c_str(), "%d", &m_userLimit); + m_email = parts.at(5); + m_company = parts.at(6); + sscanf(parts.at(7).c_str(), "%lld", &m_warnTime); + sscanf(parts.at(8).c_str(), "%lld", &m_expireTime); + m_valid = true; + } + } } Edition SerialKey::parseEdition(std::string const& editionStr) { - Edition e = kBasic; - if (editionStr == "pro") { - e = kPro; - } + Edition e = kBasic; + if (editionStr == "pro") { + e = kPro; + } - return e; + return e; } diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h index ed0045c8a..c6ae3f207 100644 --- a/src/lib/shared/SerialKey.h +++ b/src/lib/shared/SerialKey.h @@ -26,65 +26,65 @@ #endif class SerialKey { - friend bool operator== (SerialKey const&, SerialKey const&); + friend bool operator== (SerialKey const&, SerialKey const&); public: - explicit SerialKey(Edition edition = kUnregistered); - explicit SerialKey(std::string serial); + explicit SerialKey(Edition edition = kUnregistered); + explicit SerialKey(std::string serial); - bool isValid(time_t currentTime) const; - bool isExpiring(time_t currentTime) const; - bool isExpired(time_t currentTime) const; - bool isTrial() const; - time_t daysLeft(time_t currentTime) const; + bool isValid(time_t currentTime) const; + bool isExpiring(time_t currentTime) const; + bool isExpired(time_t currentTime) const; + bool isTrial() const; + time_t daysLeft(time_t currentTime) const; std::string email() const; - Edition edition() const; + Edition edition() const; std::string toString() const; - static std::string decode(const std::string& serial); - static Edition parseEdition(const std::string& editionStr); + static std::string decode(const std::string& serial); + static Edition parseEdition(const std::string& editionStr); private: - void parse(std::string plainSerial); + void parse(std::string plainSerial); std::string editionString() const; #ifdef TEST_ENV private: - FRIEND_TEST(SerialKeyTests, decode_empty_returnEmptyString); - FRIEND_TEST(SerialKeyTests, decode_invalidDigit_returnEmptyString); - FRIEND_TEST(SerialKeyTests, decode_validSerial_returnPlainText); - FRIEND_TEST(SerialKeyTests, parse_noParty_invalid); - FRIEND_TEST(SerialKeyTests, parse_invalidPartsLenghth_invalid); - FRIEND_TEST(SerialKeyTests, parse_validV1Serial_valid); - FRIEND_TEST(SerialKeyTests, parse_validV2Serial_valid); + FRIEND_TEST(SerialKeyTests, decode_empty_returnEmptyString); + FRIEND_TEST(SerialKeyTests, decode_invalidDigit_returnEmptyString); + FRIEND_TEST(SerialKeyTests, decode_validSerial_returnPlainText); + FRIEND_TEST(SerialKeyTests, parse_noParty_invalid); + FRIEND_TEST(SerialKeyTests, parse_invalidPartsLenghth_invalid); + FRIEND_TEST(SerialKeyTests, parse_validV1Serial_valid); + FRIEND_TEST(SerialKeyTests, parse_validV2Serial_valid); #endif private: - std::string m_name; - std::string m_email; - std::string m_company; - unsigned m_userLimit; - unsigned long long m_warnTime; - unsigned long long m_expireTime; - Edition m_edition; - bool m_trial; - bool m_valid; + std::string m_name; + std::string m_email; + std::string m_company; + unsigned m_userLimit; + unsigned long long m_warnTime; + unsigned long long m_expireTime; + Edition m_edition; + bool m_trial; + bool m_valid; }; inline bool operator== (SerialKey const& lhs, SerialKey const& rhs) { - return (lhs.m_name == rhs.m_name) && - (lhs.m_email == rhs.m_email) && - (lhs.m_company == rhs.m_company) && - (lhs.m_userLimit == rhs.m_userLimit) && - (lhs.m_warnTime == rhs.m_warnTime) && - (lhs.m_expireTime == rhs.m_expireTime) && - (lhs.m_edition == rhs.m_edition) && - (lhs.m_trial == rhs.m_trial) && - (lhs.m_valid == rhs.m_valid); + return (lhs.m_name == rhs.m_name) && + (lhs.m_email == rhs.m_email) && + (lhs.m_company == rhs.m_company) && + (lhs.m_userLimit == rhs.m_userLimit) && + (lhs.m_warnTime == rhs.m_warnTime) && + (lhs.m_expireTime == rhs.m_expireTime) && + (lhs.m_edition == rhs.m_edition) && + (lhs.m_trial == rhs.m_trial) && + (lhs.m_valid == rhs.m_valid); } inline bool operator!= (SerialKey const& lhs, SerialKey const& rhs) { - return !(lhs == rhs); + return !(lhs == rhs); } From 8b4d7abfb01da7742b0a133670230e526600a90b Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 17 Oct 2016 17:08:26 +0100 Subject: [PATCH 145/241] #5657 Remove SerialKey::m_valid --- src/gui/src/ActivationDialog.cpp | 92 +- src/gui/src/MainWindow.cpp | 1848 +++++++++++++++++++------------------- src/lib/shared/SerialKey.cpp | 53 +- src/lib/shared/SerialKey.h | 7 +- 4 files changed, 992 insertions(+), 1008 deletions(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 1dbef32e9..9e7f5e827 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -15,73 +15,81 @@ #include ActivationDialog::ActivationDialog(QWidget* parent, AppConfig& appConfig, - SubscriptionManager& subscriptionManager) : - QDialog(parent), - ui(new Ui::ActivationDialog), - m_appConfig(&appConfig), - m_subscriptionManager (&subscriptionManager) + SubscriptionManager& subscriptionManager) : + QDialog(parent), + ui(new Ui::ActivationDialog), + m_appConfig(&appConfig), + m_subscriptionManager (&subscriptionManager) { - ui->setupUi(this); - refreshSerialKey(); + ui->setupUi(this); + refreshSerialKey(); } void ActivationDialog::refreshSerialKey() { - ui->m_pTextEditSerialKey->setText(m_appConfig->serialKey()); - ui->m_pTextEditSerialKey->setFocus(); - ui->m_pTextEditSerialKey->moveCursor(QTextCursor::End); + ui->m_pTextEditSerialKey->setText(m_appConfig->serialKey()); + ui->m_pTextEditSerialKey->setFocus(); + ui->m_pTextEditSerialKey->moveCursor(QTextCursor::End); } ActivationDialog::~ActivationDialog() { - delete ui; + delete ui; } void ActivationDialog::reject() { - if (m_subscriptionManager->activeEdition() == kUnregistered) { - CancelActivationDialog cancelActivationDialog(this); - if (QDialog::Accepted == cancelActivationDialog.exec()) { - m_subscriptionManager->skipActivation(); - m_appConfig->activationHasRun(true); - m_appConfig->saveSettings(); - } - } - QDialog::reject(); + if (m_subscriptionManager->activeEdition() == kUnregistered) { + CancelActivationDialog cancelActivationDialog(this); + if (QDialog::Accepted == cancelActivationDialog.exec()) { + m_subscriptionManager->skipActivation(); + m_appConfig->activationHasRun(true); + m_appConfig->saveSettings(); + } + } + QDialog::reject(); } void ActivationDialog::accept() { - QMessageBox message; - m_appConfig->activationHasRun(true); - m_appConfig->saveSettings(); + QMessageBox message; + m_appConfig->activationHasRun(true); + m_appConfig->saveSettings(); std::pair result; - try { - QString serialKey = ui->m_pTextEditSerialKey->toPlainText(); - result = m_subscriptionManager->setSerialKey(serialKey); - } - catch (std::exception& e) { - message.critical(this, "Unknown Error", - tr("An error occurred while trying to activate Synergy. " - "Please contact the helpdesk, and provide the " - "following information:\n\n%1").arg(e.what())); - refreshSerialKey(); - return; - } + try { + QString serialKey = ui->m_pTextEditSerialKey->toPlainText(); + result = m_subscriptionManager->setSerialKey(serialKey); + } + catch (std::exception& e) { + message.critical(this, "Unknown Error", + tr("An error occurred while trying to activate Synergy. " + "Please contact the helpdesk, and provide the " + "following information:\n\n%1").arg(e.what())); + refreshSerialKey(); + return; + } if (!result.first) { - message.critical(this, "Activation failed", + message.critical(this, "Activation failed", tr("%1").arg(result.second)); refreshSerialKey(); return; } - if (m_subscriptionManager->activeEdition() != kUnregistered) { - message.information(this, "Activated!", - tr("Thanks for activating %1!").arg - (m_subscriptionManager->activeEditionName())); - } + Edition edition = m_subscriptionManager->activeEdition(); + if (edition != kUnregistered) { + if (m_subscriptionManager->serialKey().isTrial()) { + message.information(this, "Thanks!", + tr("Thanks for trying %1!").arg + (m_subscriptionManager->getEditionName(edition))); + } + else { + message.information(this, "Activated!", + tr("Thanks for activating %1!").arg + (m_subscriptionManager->getEditionName(edition))); + } + } - QDialog::accept(); + QDialog::accept(); } diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 762446276..64a9f50d3 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -70,990 +70,990 @@ static const QString synergyConfigFilter(QObject::tr("Synergy Configurations (*. static const char* synergyIconFiles[] = { - ":/res/icons/16x16/synergy-disconnected.png", - ":/res/icons/16x16/synergy-disconnected.png", - ":/res/icons/16x16/synergy-connected.png", - ":/res/icons/16x16/synergy-transfering.png" + ":/res/icons/16x16/synergy-disconnected.png", + ":/res/icons/16x16/synergy-disconnected.png", + ":/res/icons/16x16/synergy-connected.png", + ":/res/icons/16x16/synergy-transfering.png" }; MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig, - SubscriptionManager& subscriptionManager) : - m_Settings(settings), - m_AppConfig(&appConfig), - m_SubscriptionManager(&subscriptionManager), - m_pSynergy(NULL), - m_SynergyState(synergyDisconnected), - m_ServerConfig(&m_Settings, 5, 3, m_AppConfig->screenName(), this), - m_pTempConfigFile(NULL), - m_pTrayIcon(NULL), - m_pTrayIconMenu(NULL), - m_AlreadyHidden(false), - m_pMenuBar(NULL), - m_pMenuFile(NULL), - m_pMenuEdit(NULL), - m_pMenuWindow(NULL), - m_pMenuHelp(NULL), - m_pZeroconfService(NULL), - m_pDataDownloader(NULL), - m_DownloadMessageBox(NULL), - m_pCancelButton(NULL), - m_SuppressAutoConfigWarning(false), - m_BonjourInstall(NULL), - m_SuppressEmptyServerWarning(false), - m_ExpectedRunningState(kStopped), - m_pSslCertificate(NULL) -{ - setupUi(this); - - createMenuBar(); - loadSettings(); - initConnections(); - - m_pWidgetUpdate->hide(); - m_VersionChecker.setApp(appPath(appConfig.synergycName())); - m_pLabelScreenName->setText(getScreenName()); - m_pLabelIpAddresses->setText(getIPAddresses()); + SubscriptionManager& subscriptionManager) : + m_Settings(settings), + m_AppConfig(&appConfig), + m_SubscriptionManager(&subscriptionManager), + m_pSynergy(NULL), + m_SynergyState(synergyDisconnected), + m_ServerConfig(&m_Settings, 5, 3, m_AppConfig->screenName(), this), + m_pTempConfigFile(NULL), + m_pTrayIcon(NULL), + m_pTrayIconMenu(NULL), + m_AlreadyHidden(false), + m_pMenuBar(NULL), + m_pMenuFile(NULL), + m_pMenuEdit(NULL), + m_pMenuWindow(NULL), + m_pMenuHelp(NULL), + m_pZeroconfService(NULL), + m_pDataDownloader(NULL), + m_DownloadMessageBox(NULL), + m_pCancelButton(NULL), + m_SuppressAutoConfigWarning(false), + m_BonjourInstall(NULL), + m_SuppressEmptyServerWarning(false), + m_ExpectedRunningState(kStopped), + m_pSslCertificate(NULL) +{ + setupUi(this); + + createMenuBar(); + loadSettings(); + initConnections(); + + m_pWidgetUpdate->hide(); + m_VersionChecker.setApp(appPath(appConfig.synergycName())); + m_pLabelScreenName->setText(getScreenName()); + m_pLabelIpAddresses->setText(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(appendLogNote(const QString&))); - m_IpcClient.connectToHost(); + // 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(appendLogNote(const QString&))); + m_IpcClient.connectToHost(); #endif - // change default size based on os + // change default size based on os #if defined(Q_OS_MAC) - resize(720, 550); - setMinimumSize(size()); + resize(720, 550); + setMinimumSize(size()); #elif defined(Q_OS_LINUX) - resize(700, 530); - setMinimumSize(size()); + resize(700, 530); + setMinimumSize(size()); #endif - m_SuppressAutoConfigWarning = true; - m_pCheckBoxAutoConfig->setChecked(appConfig.autoConfig()); - m_SuppressAutoConfigWarning = false; + m_SuppressAutoConfigWarning = true; + m_pCheckBoxAutoConfig->setChecked(appConfig.autoConfig()); + m_SuppressAutoConfigWarning = false; - m_pComboServerList->hide(); - m_pLabelPadlock->hide(); - m_trialWidget->hide(); + m_pComboServerList->hide(); + m_pLabelPadlock->hide(); + m_trialWidget->hide(); - connect (this, SIGNAL(windowShown()), - this, SLOT(on_windowShown()), Qt::QueuedConnection); + connect (this, SIGNAL(windowShown()), + this, SLOT(on_windowShown()), Qt::QueuedConnection); - connect (m_SubscriptionManager, SIGNAL(editionChanged(Edition)), - this, SLOT(setEdition(Edition)), Qt::QueuedConnection); + connect (m_SubscriptionManager, SIGNAL(editionChanged(Edition)), + this, SLOT(setEdition(Edition)), Qt::QueuedConnection); - connect (m_SubscriptionManager, SIGNAL(beginTrial(bool)), - this, SLOT(beginTrial(bool)), Qt::QueuedConnection); + connect (m_SubscriptionManager, SIGNAL(beginTrial(bool)), + this, SLOT(beginTrial(bool)), Qt::QueuedConnection); - connect (m_SubscriptionManager, SIGNAL(endTrial(bool)), - this, SLOT(endTrial(bool)), Qt::QueuedConnection); + connect (m_SubscriptionManager, SIGNAL(endTrial(bool)), + this, SLOT(endTrial(bool)), Qt::QueuedConnection); - connect (m_AppConfig, SIGNAL(sslToggled(bool)), - this, SLOT(sslToggled(bool)), Qt::QueuedConnection); + connect (m_AppConfig, SIGNAL(sslToggled(bool)), + this, SLOT(sslToggled(bool)), Qt::QueuedConnection); - m_SubscriptionManager->refresh(); + m_SubscriptionManager->refresh(); } MainWindow::~MainWindow() { - if (appConfig().processMode() == Desktop) { - m_ExpectedRunningState = kStopped; - stopDesktop(); - } + if (appConfig().processMode() == Desktop) { + m_ExpectedRunningState = kStopped; + stopDesktop(); + } - saveSettings(); + saveSettings(); - delete m_pZeroconfService; + delete m_pZeroconfService; - if (m_DownloadMessageBox != NULL) { - delete m_DownloadMessageBox; - } + if (m_DownloadMessageBox != NULL) { + delete m_DownloadMessageBox; + } - if (m_BonjourInstall != NULL) { - delete m_BonjourInstall; - } + if (m_BonjourInstall != NULL) { + delete m_BonjourInstall; + } - delete m_pSslCertificate; + delete m_pSslCertificate; } void MainWindow::open() { - createTrayIcon(); + createTrayIcon(); - if (!autoHide()) { - showNormal(); - } + if (!autoHide()) { + showNormal(); + } - m_VersionChecker.checkLatest(); + m_VersionChecker.checkLatest(); - if (!appConfig().autoConfigPrompted()) { - promptAutoConfig(); - } + if (!appConfig().autoConfigPrompted()) { + promptAutoConfig(); + } - // only start if user has previously started. this stops the gui from - // auto hiding before the user has configured synergy (which of course - // confuses first time users, who think synergy has crashed). - if (appConfig().startedBefore() && appConfig().processMode() == Desktop) { - m_SuppressEmptyServerWarning = true; - startSynergy(); - m_SuppressEmptyServerWarning = false; - } + // only start if user has previously started. this stops the gui from + // auto hiding before the user has configured synergy (which of course + // confuses first time users, who think synergy has crashed). + if (appConfig().startedBefore() && appConfig().processMode() == Desktop) { + m_SuppressEmptyServerWarning = true; + startSynergy(); + m_SuppressEmptyServerWarning = false; + } } void MainWindow::onModeChanged(bool startDesktop, bool applyService) { - if (appConfig().processMode() == Service) - { - // ensure that the apply button actually does something, since desktop - // mode screws around with connecting/disconnecting the action. - disconnect(m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); - connect(m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); - - if (applyService) - { - stopDesktop(); - startSynergy(); - } - } - else if ((appConfig().processMode() == Desktop) && startDesktop) - { - stopService(); - startSynergy(); - } + if (appConfig().processMode() == Service) + { + // ensure that the apply button actually does something, since desktop + // mode screws around with connecting/disconnecting the action. + disconnect(m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); + connect(m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); + + if (applyService) + { + stopDesktop(); + startSynergy(); + } + } + else if ((appConfig().processMode() == Desktop) && startDesktop) + { + stopService(); + startSynergy(); + } } void MainWindow::setStatus(const QString &status) { - m_pStatusLabel->setText(status); + m_pStatusLabel->setText(status); } void MainWindow::createTrayIcon() { - m_pTrayIconMenu = new QMenu(this); + m_pTrayIconMenu = new QMenu(this); - m_pTrayIconMenu->addAction(m_pActionStartSynergy); - m_pTrayIconMenu->addAction(m_pActionStopSynergy); - m_pTrayIconMenu->addSeparator(); + m_pTrayIconMenu->addAction(m_pActionStartSynergy); + m_pTrayIconMenu->addAction(m_pActionStopSynergy); + m_pTrayIconMenu->addSeparator(); - m_pTrayIconMenu->addAction(m_pActionMinimize); - m_pTrayIconMenu->addAction(m_pActionRestore); - m_pTrayIconMenu->addSeparator(); - m_pTrayIconMenu->addAction(m_pActionQuit); + m_pTrayIconMenu->addAction(m_pActionMinimize); + m_pTrayIconMenu->addAction(m_pActionRestore); + m_pTrayIconMenu->addSeparator(); + m_pTrayIconMenu->addAction(m_pActionQuit); - m_pTrayIcon = new QSystemTrayIcon(this); - m_pTrayIcon->setContextMenu(m_pTrayIconMenu); + m_pTrayIcon = new QSystemTrayIcon(this); + m_pTrayIcon->setContextMenu(m_pTrayIconMenu); - connect(m_pTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), - this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))); + connect(m_pTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))); - setIcon(synergyDisconnected); + setIcon(synergyDisconnected); - m_pTrayIcon->show(); + m_pTrayIcon->show(); } void MainWindow::retranslateMenuBar() { - m_pMenuFile->setTitle(tr("&File")); - m_pMenuEdit->setTitle(tr("&Edit")); - m_pMenuWindow->setTitle(tr("&Window")); - m_pMenuHelp->setTitle(tr("&Help")); + m_pMenuFile->setTitle(tr("&File")); + m_pMenuEdit->setTitle(tr("&Edit")); + m_pMenuWindow->setTitle(tr("&Window")); + m_pMenuHelp->setTitle(tr("&Help")); } void MainWindow::createMenuBar() { - m_pMenuBar = new QMenuBar(this); - m_pMenuFile = new QMenu("", m_pMenuBar); - m_pMenuEdit = new QMenu("", m_pMenuBar); - m_pMenuWindow = new QMenu("", m_pMenuBar); - m_pMenuHelp = new QMenu("", m_pMenuBar); - retranslateMenuBar(); + m_pMenuBar = new QMenuBar(this); + m_pMenuFile = new QMenu("", m_pMenuBar); + m_pMenuEdit = new QMenu("", m_pMenuBar); + m_pMenuWindow = new QMenu("", m_pMenuBar); + m_pMenuHelp = new QMenu("", m_pMenuBar); + retranslateMenuBar(); - m_pMenuBar->addAction(m_pMenuFile->menuAction()); - m_pMenuBar->addAction(m_pMenuEdit->menuAction()); + m_pMenuBar->addAction(m_pMenuFile->menuAction()); + m_pMenuBar->addAction(m_pMenuEdit->menuAction()); #if !defined(Q_OS_MAC) - m_pMenuBar->addAction(m_pMenuWindow->menuAction()); + m_pMenuBar->addAction(m_pMenuWindow->menuAction()); #endif - m_pMenuBar->addAction(m_pMenuHelp->menuAction()); + m_pMenuBar->addAction(m_pMenuHelp->menuAction()); - m_pMenuFile->addAction(m_pActionStartSynergy); - m_pMenuFile->addAction(m_pActionStopSynergy); - m_pMenuFile->addSeparator(); - m_pMenuFile->addAction(m_pActivate); - m_pMenuFile->addSeparator(); - m_pMenuFile->addAction(m_pActionSave); - m_pMenuFile->addSeparator(); - m_pMenuFile->addAction(m_pActionQuit); - m_pMenuEdit->addAction(m_pActionSettings); - m_pMenuWindow->addAction(m_pActionMinimize); - m_pMenuWindow->addAction(m_pActionRestore); - m_pMenuHelp->addAction(m_pActionAbout); + m_pMenuFile->addAction(m_pActionStartSynergy); + m_pMenuFile->addAction(m_pActionStopSynergy); + m_pMenuFile->addSeparator(); + m_pMenuFile->addAction(m_pActivate); + m_pMenuFile->addSeparator(); + m_pMenuFile->addAction(m_pActionSave); + m_pMenuFile->addSeparator(); + m_pMenuFile->addAction(m_pActionQuit); + m_pMenuEdit->addAction(m_pActionSettings); + m_pMenuWindow->addAction(m_pActionMinimize); + m_pMenuWindow->addAction(m_pActionRestore); + m_pMenuHelp->addAction(m_pActionAbout); - setMenuBar(m_pMenuBar); + setMenuBar(m_pMenuBar); } void MainWindow::loadSettings() { - // the next two must come BEFORE loading groupServerChecked and groupClientChecked or - // disabling and/or enabling the right widgets won't automatically work - m_pRadioExternalConfig->setChecked(settings().value("useExternalConfig", false).toBool()); - m_pRadioInternalConfig->setChecked(settings().value("useInternalConfig", true).toBool()); + // the next two must come BEFORE loading groupServerChecked and groupClientChecked or + // disabling and/or enabling the right widgets won't automatically work + m_pRadioExternalConfig->setChecked(settings().value("useExternalConfig", false).toBool()); + m_pRadioInternalConfig->setChecked(settings().value("useInternalConfig", true).toBool()); - m_pGroupServer->setChecked(settings().value("groupServerChecked", false).toBool()); - m_pLineEditConfigFile->setText(settings().value("configFile", QDir::homePath() + "/" + synergyConfigName).toString()); - m_pGroupClient->setChecked(settings().value("groupClientChecked", true).toBool()); - m_pLineEditHostname->setText(settings().value("serverHostname").toString()); + m_pGroupServer->setChecked(settings().value("groupServerChecked", false).toBool()); + m_pLineEditConfigFile->setText(settings().value("configFile", QDir::homePath() + "/" + synergyConfigName).toString()); + m_pGroupClient->setChecked(settings().value("groupClientChecked", true).toBool()); + m_pLineEditHostname->setText(settings().value("serverHostname").toString()); } void MainWindow::initConnections() { - connect(m_pActionMinimize, SIGNAL(triggered()), this, SLOT(hide())); - connect(m_pActionRestore, SIGNAL(triggered()), this, SLOT(showNormal())); - connect(m_pActionStartSynergy, SIGNAL(triggered()), this, SLOT(startSynergy())); - connect(m_pActionStopSynergy, SIGNAL(triggered()), this, SLOT(stopSynergy())); - connect(m_pActionQuit, SIGNAL(triggered()), qApp, SLOT(quit())); - connect(&m_VersionChecker, SIGNAL(updateFound(const QString&)), this, SLOT(updateFound(const QString&))); + connect(m_pActionMinimize, SIGNAL(triggered()), this, SLOT(hide())); + connect(m_pActionRestore, SIGNAL(triggered()), this, SLOT(showNormal())); + connect(m_pActionStartSynergy, SIGNAL(triggered()), this, SLOT(startSynergy())); + connect(m_pActionStopSynergy, SIGNAL(triggered()), this, SLOT(stopSynergy())); + connect(m_pActionQuit, SIGNAL(triggered()), qApp, SLOT(quit())); + connect(&m_VersionChecker, SIGNAL(updateFound(const QString&)), this, SLOT(updateFound(const QString&))); } void MainWindow::saveSettings() { - // program settings - settings().setValue("groupServerChecked", m_pGroupServer->isChecked()); - settings().setValue("useExternalConfig", m_pRadioExternalConfig->isChecked()); - settings().setValue("configFile", m_pLineEditConfigFile->text()); - settings().setValue("useInternalConfig", m_pRadioInternalConfig->isChecked()); - settings().setValue("groupClientChecked", m_pGroupClient->isChecked()); - settings().setValue("serverHostname", m_pLineEditHostname->text()); + // program settings + settings().setValue("groupServerChecked", m_pGroupServer->isChecked()); + settings().setValue("useExternalConfig", m_pRadioExternalConfig->isChecked()); + settings().setValue("configFile", m_pLineEditConfigFile->text()); + settings().setValue("useInternalConfig", m_pRadioInternalConfig->isChecked()); + settings().setValue("groupClientChecked", m_pGroupClient->isChecked()); + settings().setValue("serverHostname", m_pLineEditHostname->text()); - settings().sync(); + settings().sync(); } void MainWindow::setIcon(qSynergyState state) { - QIcon icon; - icon.addFile(synergyIconFiles[state]); + QIcon icon; + icon.addFile(synergyIconFiles[state]); - if (m_pTrayIcon) - m_pTrayIcon->setIcon(icon); + if (m_pTrayIcon) + m_pTrayIcon->setIcon(icon); } void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason) { #ifndef Q_OS_WIN - if (reason == QSystemTrayIcon::DoubleClick) - { - if (isVisible()) - { - hide(); - } - else - { - showNormal(); - activateWindow(); - } - } + if (reason == QSystemTrayIcon::DoubleClick) + { + if (isVisible()) + { + hide(); + } + else + { + showNormal(); + activateWindow(); + } + } #endif } void MainWindow::logOutput() { - if (m_pSynergy) - { - QString text(m_pSynergy->readAllStandardOutput()); - foreach(QString line, text.split(QRegExp("\r|\n|\r\n"))) - { - if (!line.isEmpty()) - { - appendLogRaw(line); - } - } - } + if (m_pSynergy) + { + QString text(m_pSynergy->readAllStandardOutput()); + foreach(QString line, text.split(QRegExp("\r|\n|\r\n"))) + { + if (!line.isEmpty()) + { + appendLogRaw(line); + } + } + } } void MainWindow::logError() { - if (m_pSynergy) - { - appendLogRaw(m_pSynergy->readAllStandardError()); - } + if (m_pSynergy) + { + appendLogRaw(m_pSynergy->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(DOWNLOAD_URL)); + 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(DOWNLOAD_URL)); } void MainWindow::appendLogInfo(const QString& text) { - appendLogRaw(getTimeStamp() + " INFO: " + text); + appendLogRaw(getTimeStamp() + " INFO: " + text); } void MainWindow::appendLogDebug(const QString& text) { - if (appConfig().logLevel() >= 4) { - appendLogRaw(getTimeStamp() + " DEBUG: " + text); - } + if (appConfig().logLevel() >= 4) { + appendLogRaw(getTimeStamp() + " DEBUG: " + text); + } } void MainWindow::appendLogError(const QString& text) { - appendLogRaw(getTimeStamp() + " ERROR: " + text); + appendLogRaw(getTimeStamp() + " ERROR: " + text); } void MainWindow::appendLogRaw(const QString& text) { - foreach(QString line, text.split(QRegExp("\r|\n|\r\n"))) { - if (!line.isEmpty()) { - m_pLogOutput->append(line); - updateStateFromLogLine(line); - } - } + foreach(QString line, text.split(QRegExp("\r|\n|\r\n"))) { + if (!line.isEmpty()) { + m_pLogOutput->append(line); + updateStateFromLogLine(line); + } + } } void MainWindow::updateStateFromLogLine(const QString &line) { - checkConnected(line); - checkFingerprint(line); + checkConnected(line); + checkFingerprint(line); } void MainWindow::checkConnected(const QString& line) { - // TODO: implement ipc connection state messages to replace this hack. - if (line.contains("started server") || - line.contains("connected to server") || - line.contains("watchdog status: ok")) - { - setSynergyState(synergyConnected); - - if (!appConfig().startedBefore() && isVisible()) { - QMessageBox::information( - this, "Synergy", - tr("Synergy is now connected. You can close the " - "config window and Synergy will remain connected in " - "the background.")); - - appConfig().setStartedBefore(true); - appConfig().saveSettings(); - } - } + // TODO: implement ipc connection state messages to replace this hack. + if (line.contains("started server") || + line.contains("connected to server") || + line.contains("watchdog status: ok")) + { + setSynergyState(synergyConnected); + + if (!appConfig().startedBefore() && isVisible()) { + QMessageBox::information( + this, "Synergy", + tr("Synergy is now connected. You can close the " + "config window and Synergy will remain connected in " + "the background.")); + + appConfig().setStartedBefore(true); + appConfig().saveSettings(); + } + } } void MainWindow::checkFingerprint(const QString& line) { - QRegExp fingerprintRegex(".*server fingerprint: ([A-F0-9:]+)"); - if (!fingerprintRegex.exactMatch(line)) { - return; - } - - QString fingerprint = fingerprintRegex.cap(1); - if (Fingerprint::trustedServers().isTrusted(fingerprint)) { - return; - } - - static bool messageBoxAlreadyShown = false; - - if (!messageBoxAlreadyShown) { - stopSynergy(); - - messageBoxAlreadyShown = true; - QMessageBox::StandardButton fingerprintReply = - QMessageBox::information( - this, tr("Security question"), - tr("Do you trust this fingerprint?\n\n" - "%1\n\n" - "This is a server fingerprint. You should compare this " - "fingerprint to the one on your server's screen. If the " - "two don't match exactly, then it's probably not the server " - "you're expecting (it could be a malicious user).\n\n" - "To automatically trust this fingerprint for future " - "connections, click Yes. To reject this fingerprint and " - "disconnect from the server, click No.") - .arg(fingerprint), - QMessageBox::Yes | QMessageBox::No); - - if (fingerprintReply == QMessageBox::Yes) { - // restart core process after trusting fingerprint. - Fingerprint::trustedServers().trust(fingerprint); - startSynergy(); - } - - messageBoxAlreadyShown = false; - } + QRegExp fingerprintRegex(".*server fingerprint: ([A-F0-9:]+)"); + if (!fingerprintRegex.exactMatch(line)) { + return; + } + + QString fingerprint = fingerprintRegex.cap(1); + if (Fingerprint::trustedServers().isTrusted(fingerprint)) { + return; + } + + static bool messageBoxAlreadyShown = false; + + if (!messageBoxAlreadyShown) { + stopSynergy(); + + messageBoxAlreadyShown = true; + QMessageBox::StandardButton fingerprintReply = + QMessageBox::information( + this, tr("Security question"), + tr("Do you trust this fingerprint?\n\n" + "%1\n\n" + "This is a server fingerprint. You should compare this " + "fingerprint to the one on your server's screen. If the " + "two don't match exactly, then it's probably not the server " + "you're expecting (it could be a malicious user).\n\n" + "To automatically trust this fingerprint for future " + "connections, click Yes. To reject this fingerprint and " + "disconnect from the server, click No.") + .arg(fingerprint), + QMessageBox::Yes | QMessageBox::No); + + if (fingerprintReply == QMessageBox::Yes) { + // restart core process after trusting fingerprint. + Fingerprint::trustedServers().trust(fingerprint); + startSynergy(); + } + + messageBoxAlreadyShown = false; + } } bool MainWindow::autoHide() { - if ((appConfig().processMode() == Desktop) && - appConfig().getAutoHide()) { - hide(); - return true; - } + if ((appConfig().processMode() == Desktop) && + appConfig().getAutoHide()) { + hide(); + return true; + } - return false; + return false; } QString MainWindow::getTimeStamp() { - QDateTime current = QDateTime::currentDateTime(); - return '[' + current.toString(Qt::ISODate) + ']'; + QDateTime current = QDateTime::currentDateTime(); + return '[' + current.toString(Qt::ISODate) + ']'; } void MainWindow::restartSynergy() { - stopSynergy(); - startSynergy(); + stopSynergy(); + startSynergy(); } void MainWindow::proofreadInfo() { - setEdition(m_AppConfig->edition()); // Why is this here? + setEdition(m_AppConfig->edition()); // Why is this here? - int oldState = m_SynergyState; - m_SynergyState = synergyDisconnected; - setSynergyState((qSynergyState)oldState); + int oldState = m_SynergyState; + m_SynergyState = synergyDisconnected; + setSynergyState((qSynergyState)oldState); } void MainWindow::showEvent(QShowEvent* event) { - QMainWindow::showEvent(event); - emit windowShown(); + QMainWindow::showEvent(event); + emit windowShown(); } void MainWindow::clearLog() { - m_pLogOutput->clear(); + m_pLogOutput->clear(); } void MainWindow::startSynergy() { - bool desktopMode = appConfig().processMode() == Desktop; - bool serviceMode = appConfig().processMode() == Service; + bool desktopMode = appConfig().processMode() == Desktop; + bool serviceMode = appConfig().processMode() == Service; - appendLogDebug("starting process"); - m_ExpectedRunningState = kStarted; - setSynergyState(synergyConnecting); + appendLogDebug("starting process"); + m_ExpectedRunningState = kStarted; + setSynergyState(synergyConnecting); - QString app; - QStringList args; + QString app; + QStringList args; - args << "-f" << "--no-tray" << "--debug" << appConfig().logLevelText(); + args << "-f" << "--no-tray" << "--debug" << appConfig().logLevelText(); - args << "--name" << getScreenName(); + args << "--name" << getScreenName(); if (!appConfig().serialKey().isEmpty()) { - args << "--serial-key " << appConfig().serialKey(); + args << "--serial-key" << appConfig().serialKey(); } - if (desktopMode) - { - setSynergyProcess(new QProcess(this)); - } - else - { - // tell client/server to talk to daemon through ipc. - args << "--ipc"; + if (desktopMode) + { + setSynergyProcess(new QProcess(this)); + } + else + { + // tell client/server to talk to daemon through ipc. + args << "--ipc"; #if defined(Q_OS_WIN) - // tell the client/server to shut down when a ms windows desk - // is switched; this is because we may need to elevate or not - // based on which desk the user is in (login always needs - // elevation, where as default desk does not). - // Note that this is only enabled when synergy is set to elevate - // 'as needed' (e.g. on a UAC dialog popup) in order to prevent - // unnecessary restarts when synergy was started elevated or - // when it is not allowed to elevate. In these cases restarting - // the server is fruitless. - if (appConfig().elevateMode() == ElevateAsNeeded) { - args << "--stop-on-desk-switch"; - } + // tell the client/server to shut down when a ms windows desk + // is switched; this is because we may need to elevate or not + // based on which desk the user is in (login always needs + // elevation, where as default desk does not). + // Note that this is only enabled when synergy is set to elevate + // 'as needed' (e.g. on a UAC dialog popup) in order to prevent + // unnecessary restarts when synergy was started elevated or + // when it is not allowed to elevate. In these cases restarting + // the server is fruitless. + if (appConfig().elevateMode() == ElevateAsNeeded) { + args << "--stop-on-desk-switch"; + } #endif - } + } #ifndef Q_OS_LINUX - if (m_ServerConfig.enableDragAndDrop()) { - args << "--enable-drag-drop"; - } + if (m_ServerConfig.enableDragAndDrop()) { + args << "--enable-drag-drop"; + } #endif - if (m_AppConfig->getCryptoEnabled()) { - args << "--enable-crypto"; - } + if (m_AppConfig->getCryptoEnabled()) { + args << "--enable-crypto"; + } #if defined(Q_OS_WIN) - // on windows, the profile directory changes depending on the user that - // launched the process (e.g. when launched with elevation). setting the - // profile dir on launch ensures it uses the same profile dir is used - // no matter how its relaunched. - args << "--profile-dir" << getProfileRootForArg(); + // on windows, the profile directory changes depending on the user that + // launched the process (e.g. when launched with elevation). setting the + // profile dir on launch ensures it uses the same profile dir is used + // no matter how its relaunched. + args << "--profile-dir" << getProfileRootForArg(); #endif - if ((synergyType() == synergyClient && !clientArgs(args, app)) - || (synergyType() == synergyServer && !serverArgs(args, app))) - { - stopSynergy(); - return; - } - - if (desktopMode) - { - connect(synergyProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(synergyFinished(int, QProcess::ExitStatus))); - connect(synergyProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(logOutput())); - connect(synergyProcess(), SIGNAL(readyReadStandardError()), this, SLOT(logError())); - } - - // put a space between last log output and new instance. - if (!m_pLogOutput->toPlainText().isEmpty()) - appendLogRaw(""); - - appendLogInfo("starting " + QString(synergyType() == synergyServer ? "server" : "client")); - - qDebug() << args; - - // show command if debug log level... - if (appConfig().logLevel() >= 4) { - appendLogInfo(QString("command: %1 %2").arg(app, args.join(" "))); - } - - appendLogInfo("config file: " + configFilename()); - appendLogInfo("log level: " + appConfig().logLevelText()); - - if (appConfig().logToFile()) - appendLogInfo("log file: " + appConfig().logFilename()); - - if (desktopMode) - { - synergyProcess()->start(app, args); - if (!synergyProcess()->waitForStarted()) - { - show(); - QMessageBox::warning(this, tr("Program can not be started"), QString(tr("The executable

%1

could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.").arg(app))); - return; - } - } - - if (serviceMode) - { - QString command(app + " " + args.join(" ")); - m_IpcClient.sendCommand(command, appConfig().elevateMode()); - } + if ((synergyType() == synergyClient && !clientArgs(args, app)) + || (synergyType() == synergyServer && !serverArgs(args, app))) + { + stopSynergy(); + return; + } + + if (desktopMode) + { + connect(synergyProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(synergyFinished(int, QProcess::ExitStatus))); + connect(synergyProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(logOutput())); + connect(synergyProcess(), SIGNAL(readyReadStandardError()), this, SLOT(logError())); + } + + // put a space between last log output and new instance. + if (!m_pLogOutput->toPlainText().isEmpty()) + appendLogRaw(""); + + appendLogInfo("starting " + QString(synergyType() == synergyServer ? "server" : "client")); + + qDebug() << args; + + // show command if debug log level... + if (appConfig().logLevel() >= 4) { + appendLogInfo(QString("command: %1 %2").arg(app, args.join(" "))); + } + + appendLogInfo("config file: " + configFilename()); + appendLogInfo("log level: " + appConfig().logLevelText()); + + if (appConfig().logToFile()) + appendLogInfo("log file: " + appConfig().logFilename()); + + if (desktopMode) + { + synergyProcess()->start(app, args); + if (!synergyProcess()->waitForStarted()) + { + show(); + QMessageBox::warning(this, tr("Program can not be started"), QString(tr("The executable

%1

could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.").arg(app))); + return; + } + } + + if (serviceMode) + { + QString command(app + " " + args.join(" ")); + m_IpcClient.sendCommand(command, appConfig().elevateMode()); + } } void MainWindow::sslToggled (bool enabled) { - if (enabled) { - m_pSslCertificate = new SslCertificate(this); - m_pSslCertificate->generateCertificate(); - } - updateLocalFingerprint(); + if (enabled) { + m_pSslCertificate = new SslCertificate(this); + m_pSslCertificate->generateCertificate(); + } + updateLocalFingerprint(); } bool MainWindow::clientArgs(QStringList& args, QString& app) { - app = appPath(appConfig().synergycName()); + app = appPath(appConfig().synergycName()); - if (!QFile::exists(app)) - { - show(); - QMessageBox::warning(this, tr("Synergy client not found"), - tr("The executable for the synergy client does not exist.")); - return false; - } + if (!QFile::exists(app)) + { + show(); + QMessageBox::warning(this, tr("Synergy client not found"), + tr("The executable for the synergy client does not exist.")); + return false; + } #if defined(Q_OS_WIN) - // wrap in quotes so a malicious user can't start \Program.exe as admin. - app = QString("\"%1\"").arg(app); + // wrap in quotes so a malicious user can't start \Program.exe as admin. + app = QString("\"%1\"").arg(app); #endif - if (appConfig().logToFile()) - { - appConfig().persistLogDir(); - args << "--log" << appConfig().logFilenameCmd(); - } + if (appConfig().logToFile()) + { + appConfig().persistLogDir(); + args << "--log" << appConfig().logFilenameCmd(); + } - // check auto config first, if it is disabled or no server detected, - // use line edit host name if it is not empty - if (m_pCheckBoxAutoConfig->isChecked()) { - if (m_pComboServerList->count() != 0) { - QString serverIp = m_pComboServerList->currentText(); - args << serverIp + ":" + QString::number(appConfig().port()); - return true; - } - } + // check auto config first, if it is disabled or no server detected, + // use line edit host name if it is not empty + if (m_pCheckBoxAutoConfig->isChecked()) { + if (m_pComboServerList->count() != 0) { + QString serverIp = m_pComboServerList->currentText(); + args << serverIp + ":" + QString::number(appConfig().port()); + return true; + } + } - if (m_pLineEditHostname->text().isEmpty()) { - show(); - if (!m_SuppressEmptyServerWarning) { - QMessageBox::warning(this, tr("Hostname is empty"), - tr("Please fill in a hostname for the synergy client to connect to.")); - } - return false; - } + if (m_pLineEditHostname->text().isEmpty()) { + show(); + if (!m_SuppressEmptyServerWarning) { + QMessageBox::warning(this, tr("Hostname is empty"), + tr("Please fill in a hostname for the synergy client to connect to.")); + } + return false; + } - args << m_pLineEditHostname->text() + ":" + QString::number(appConfig().port()); + args << m_pLineEditHostname->text() + ":" + QString::number(appConfig().port()); - return true; + return true; } QString MainWindow::configFilename() { - QString filename; - if (m_pRadioInternalConfig->isChecked()) - { - // TODO: no need to use a temporary file, since we need it to - // be permenant (since it'll be used for Windows services, etc). - m_pTempConfigFile = new QTemporaryFile(); - if (!m_pTempConfigFile->open()) - { - QMessageBox::critical(this, tr("Cannot write configuration file"), tr("The temporary configuration file required to start synergy can not be written.")); - return ""; - } - - serverConfig().save(*m_pTempConfigFile); - filename = m_pTempConfigFile->fileName(); - - m_pTempConfigFile->close(); - } - else - { - if (!QFile::exists(m_pLineEditConfigFile->text())) - { - if (QMessageBox::warning(this, tr("Configuration filename invalid"), - tr("You have not filled in a valid configuration file for the synergy server. " - "Do you want to browse for the configuration file now?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes - || !on_m_pButtonBrowseConfigFile_clicked()) - return ""; - } - - filename = m_pLineEditConfigFile->text(); - } - return filename; + QString filename; + if (m_pRadioInternalConfig->isChecked()) + { + // TODO: no need to use a temporary file, since we need it to + // be permenant (since it'll be used for Windows services, etc). + m_pTempConfigFile = new QTemporaryFile(); + if (!m_pTempConfigFile->open()) + { + QMessageBox::critical(this, tr("Cannot write configuration file"), tr("The temporary configuration file required to start synergy can not be written.")); + return ""; + } + + serverConfig().save(*m_pTempConfigFile); + filename = m_pTempConfigFile->fileName(); + + m_pTempConfigFile->close(); + } + else + { + if (!QFile::exists(m_pLineEditConfigFile->text())) + { + if (QMessageBox::warning(this, tr("Configuration filename invalid"), + tr("You have not filled in a valid configuration file for the synergy server. " + "Do you want to browse for the configuration file now?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes + || !on_m_pButtonBrowseConfigFile_clicked()) + return ""; + } + + filename = m_pLineEditConfigFile->text(); + } + return filename; } QString MainWindow::address() { - QString i = appConfig().interface(); - return (!i.isEmpty() ? i : "") + ":" + QString::number(appConfig().port()); + QString i = appConfig().interface(); + return (!i.isEmpty() ? i : "") + ":" + QString::number(appConfig().port()); } QString MainWindow::appPath(const QString& name) { - return appConfig().synergyProgramDir() + name; + return appConfig().synergyProgramDir() + name; } bool MainWindow::serverArgs(QStringList& args, QString& app) { - app = appPath(appConfig().synergysName()); + app = appPath(appConfig().synergysName()); - if (!QFile::exists(app)) - { - QMessageBox::warning(this, tr("Synergy server not found"), - tr("The executable for the synergy server does not exist.")); - return false; - } + if (!QFile::exists(app)) + { + QMessageBox::warning(this, tr("Synergy server not found"), + tr("The executable for the synergy server does not exist.")); + return false; + } #if defined(Q_OS_WIN) - // wrap in quotes so a malicious user can't start \Program.exe as admin. - app = QString("\"%1\"").arg(app); + // wrap in quotes so a malicious user can't start \Program.exe as admin. + app = QString("\"%1\"").arg(app); #endif - if (appConfig().logToFile()) - { - appConfig().persistLogDir(); + if (appConfig().logToFile()) + { + appConfig().persistLogDir(); - args << "--log" << appConfig().logFilenameCmd(); - } + args << "--log" << appConfig().logFilenameCmd(); + } - QString configFilename = this->configFilename(); + QString configFilename = this->configFilename(); #if defined(Q_OS_WIN) - // wrap in quotes in case username contains spaces. - configFilename = QString("\"%1\"").arg(configFilename); + // wrap in quotes in case username contains spaces. + configFilename = QString("\"%1\"").arg(configFilename); #endif - args << "-c" << configFilename << "--address" << address(); + args << "-c" << configFilename << "--address" << address(); #if defined(Q_OS_WIN) - // pass in physical resolution and primary screen center - // TODO: get this information in the core binary even when - // high DPI is used - int height = QApplication::desktop()->height(); - int width = QApplication::desktop()->width(); - - QRect rec = QApplication::desktop()->screenGeometry(); - int heightCenter = rec.height() / 2; - int widthCenter = rec.width() / 2; - - appendLogDebug(tr("screen resolution: %1 %2 primary screen center: %3 %4") - .arg(width).arg(height).arg(widthCenter).arg(heightCenter)); - - args << "--res-w" << QString::number(width); - args << "--res-h" << QString::number(height); - args << "--prm-wc" << QString::number(widthCenter); - args << "--prm-hc" << QString::number(heightCenter); + // pass in physical resolution and primary screen center + // TODO: get this information in the core binary even when + // high DPI is used + int height = QApplication::desktop()->height(); + int width = QApplication::desktop()->width(); + + QRect rec = QApplication::desktop()->screenGeometry(); + int heightCenter = rec.height() / 2; + int widthCenter = rec.width() / 2; + + appendLogDebug(tr("screen resolution: %1 %2 primary screen center: %3 %4") + .arg(width).arg(height).arg(widthCenter).arg(heightCenter)); + + args << "--res-w" << QString::number(width); + args << "--res-h" << QString::number(height); + args << "--prm-wc" << QString::number(widthCenter); + args << "--prm-hc" << QString::number(heightCenter); #endif - return true; + return true; } void MainWindow::stopSynergy() { - appendLogDebug("stopping process"); + appendLogDebug("stopping process"); - m_ExpectedRunningState = kStopped; + m_ExpectedRunningState = kStopped; - if (appConfig().processMode() == Service) - { - stopService(); - } - else if (appConfig().processMode() == Desktop) - { - stopDesktop(); - } + if (appConfig().processMode() == Service) + { + stopService(); + } + else if (appConfig().processMode() == Desktop) + { + stopDesktop(); + } - setSynergyState(synergyDisconnected); + setSynergyState(synergyDisconnected); - // HACK: deleting the object deletes the physical file, which is - // bad, since it could be in use by the Windows service! - //delete m_pTempConfigFile; - m_pTempConfigFile = NULL; + // HACK: deleting the object deletes the physical file, which is + // bad, since it could be in use by the Windows service! + //delete m_pTempConfigFile; + m_pTempConfigFile = NULL; - // reset so that new connects cause auto-hide. - m_AlreadyHidden = false; + // reset so that new connects cause auto-hide. + m_AlreadyHidden = false; } void MainWindow::stopService() { - // send empty command to stop service from laucning anything. - m_IpcClient.sendCommand("", appConfig().elevateMode()); + // send empty command to stop service from laucning anything. + m_IpcClient.sendCommand("", appConfig().elevateMode()); } void MainWindow::stopDesktop() { - QMutexLocker locker(&m_StopDesktopMutex); - if (!synergyProcess()) { - return; - } + QMutexLocker locker(&m_StopDesktopMutex); + if (!synergyProcess()) { + return; + } - appendLogInfo("stopping synergy desktop process"); + appendLogInfo("stopping synergy desktop process"); - if (synergyProcess()->isOpen()) { - synergyProcess()->close(); - } + if (synergyProcess()->isOpen()) { + synergyProcess()->close(); + } - delete synergyProcess(); - setSynergyProcess(NULL); + delete synergyProcess(); + setSynergyProcess(NULL); } void MainWindow::synergyFinished(int exitCode, QProcess::ExitStatus) { - if (exitCode == 0) { - appendLogInfo(QString("process exited normally")); - } - else { - appendLogError(QString("process exited with error code: %1").arg(exitCode)); - } + if (exitCode == 0) { + appendLogInfo(QString("process exited normally")); + } + else { + appendLogError(QString("process exited with error code: %1").arg(exitCode)); + } - if (m_ExpectedRunningState == kStarted) { - QTimer::singleShot(1000, this, SLOT(startSynergy())); - appendLogInfo(QString("detected process not running, auto restarting")); - } - else { - setSynergyState(synergyDisconnected); - } + if (m_ExpectedRunningState == kStarted) { + QTimer::singleShot(1000, this, SLOT(startSynergy())); + appendLogInfo(QString("detected process not running, auto restarting")); + } + else { + setSynergyState(synergyDisconnected); + } } void MainWindow::setSynergyState(qSynergyState state) { - if (synergyState() == state) - return; - - if (state == synergyConnected || state == synergyConnecting) - { - disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); - connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger())); - m_pButtonToggleStart->setText(tr("&Stop")); - m_pButtonApply->setEnabled(true); - } - else if (state == synergyDisconnected) - { - disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger())); - connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); - m_pButtonToggleStart->setText(tr("&Start")); - m_pButtonApply->setEnabled(false); - } - - bool connected = false; - if (state == synergyConnected || state == synergyTransfering) { - connected = true; - } - - m_pActionStartSynergy->setEnabled(!connected); - m_pActionStopSynergy->setEnabled(connected); - - switch (state) - { - case synergyConnected: { - if (m_AppConfig->getCryptoEnabled()) { - m_pLabelPadlock->show(); - } - else { - m_pLabelPadlock->hide(); - } - - setStatus(tr("Synergy is running.")); - - break; - } - case synergyConnecting: - m_pLabelPadlock->hide(); - setStatus(tr("Synergy is starting.")); - break; - case synergyDisconnected: - m_pLabelPadlock->hide(); - setStatus(tr("Synergy is not running.")); - break; - case synergyTransfering: - break; - } - - setIcon(state); - - m_SynergyState = state; + if (synergyState() == state) + return; + + if (state == synergyConnected || state == synergyConnecting) + { + disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); + connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger())); + m_pButtonToggleStart->setText(tr("&Stop")); + m_pButtonApply->setEnabled(true); + } + else if (state == synergyDisconnected) + { + disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger())); + connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger())); + m_pButtonToggleStart->setText(tr("&Start")); + m_pButtonApply->setEnabled(false); + } + + bool connected = false; + if (state == synergyConnected || state == synergyTransfering) { + connected = true; + } + + m_pActionStartSynergy->setEnabled(!connected); + m_pActionStopSynergy->setEnabled(connected); + + switch (state) + { + case synergyConnected: { + if (m_AppConfig->getCryptoEnabled()) { + m_pLabelPadlock->show(); + } + else { + m_pLabelPadlock->hide(); + } + + setStatus(tr("Synergy is running.")); + + break; + } + case synergyConnecting: + m_pLabelPadlock->hide(); + setStatus(tr("Synergy is starting.")); + break; + case synergyDisconnected: + m_pLabelPadlock->hide(); + setStatus(tr("Synergy is not running.")); + break; + case synergyTransfering: + break; + } + + setIcon(state); + + m_SynergyState = state; } void MainWindow::setVisible(bool visible) { - QMainWindow::setVisible(visible); - m_pActionMinimize->setEnabled(visible); - m_pActionRestore->setEnabled(!visible); + QMainWindow::setVisible(visible); + m_pActionMinimize->setEnabled(visible); + m_pActionRestore->setEnabled(!visible); #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 // lion - // dock hide only supported on lion :( - ProcessSerialNumber psn = { 0, kCurrentProcess }; - GetCurrentProcess(&psn); - if (visible) - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - else - TransformProcessType(&psn, kProcessTransformToBackgroundApplication); + // dock hide only supported on lion :( + ProcessSerialNumber psn = { 0, kCurrentProcess }; + GetCurrentProcess(&psn); + if (visible) + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + else + TransformProcessType(&psn, kProcessTransformToBackgroundApplication); #endif } QString MainWindow::getIPAddresses() { - QList addresses = QNetworkInterface::allAddresses(); + QList addresses = QNetworkInterface::allAddresses(); - bool hinted = false; - QString result; - for (int i = 0; i < addresses.size(); i++) { - if (addresses[i].protocol() == QAbstractSocket::IPv4Protocol && - addresses[i] != QHostAddress(QHostAddress::LocalHost)) { + bool hinted = false; + QString result; + for (int i = 0; i < addresses.size(); i++) { + if (addresses[i].protocol() == QAbstractSocket::IPv4Protocol && + addresses[i] != QHostAddress(QHostAddress::LocalHost)) { - QString address = addresses[i].toString(); - QString format = "%1, "; + QString address = addresses[i].toString(); + QString format = "%1, "; - // usually 192.168.x.x is a useful ip for the user, so indicate - // this by making it bold. - if (!hinted && address.startsWith("192.168")) { - hinted = true; - format = "%1, "; - } + // usually 192.168.x.x is a useful ip for the user, so indicate + // this by making it bold. + if (!hinted && address.startsWith("192.168")) { + hinted = true; + format = "%1, "; + } - result += format.arg(address); - } - } + result += format.arg(address); + } + } - if (result == "") { - return tr("Unknown"); - } + if (result == "") { + return tr("Unknown"); + } - // remove trailing comma. - result.chop(2); + // remove trailing comma. + result.chop(2); - return result; + return result; } QString MainWindow::getScreenName() { - if (appConfig().screenName() == "") { - return QHostInfo::localHostName(); - } - else { - return appConfig().screenName(); - } + if (appConfig().screenName() == "") { + return QHostInfo::localHostName(); + } + else { + return appConfig().screenName(); + } } void MainWindow::changeEvent(QEvent* event) { - if (event != 0) - { - switch (event->type()) - { - case QEvent::LanguageChange: - { - retranslateUi(this); - retranslateMenuBar(); - - proofreadInfo(); - - break; - } - default: - QMainWindow::changeEvent(event); - } - } + if (event != 0) + { + switch (event->type()) + { + case QEvent::LanguageChange: + { + retranslateUi(this); + retranslateMenuBar(); + + proofreadInfo(); + + break; + } + default: + QMainWindow::changeEvent(event); + } + } } void MainWindow::updateZeroconfService() { - QMutexLocker locker(&m_UpdateZeroconfMutex); + QMutexLocker locker(&m_UpdateZeroconfMutex); - if (isBonjourRunning()) { - if (!m_AppConfig->wizardShouldRun()) { - if (m_pZeroconfService) { - delete m_pZeroconfService; - m_pZeroconfService = NULL; - } + if (isBonjourRunning()) { + if (!m_AppConfig->wizardShouldRun()) { + if (m_pZeroconfService) { + delete m_pZeroconfService; + m_pZeroconfService = NULL; + } - if (m_AppConfig->autoConfig() || synergyType() == synergyServer) { - m_pZeroconfService = new ZeroconfService(this); - } - } - } + if (m_AppConfig->autoConfig() || synergyType() == synergyServer) { + m_pZeroconfService = new ZeroconfService(this); + } + } + } } void MainWindow::serverDetected(const QString name) { - if (m_pComboServerList->findText(name) == -1) { - // Note: the first added item triggers startSynergy - m_pComboServerList->addItem(name); - } + if (m_pComboServerList->findText(name) == -1) { + // Note: the first added item triggers startSynergy + m_pComboServerList->addItem(name); + } - if (m_pComboServerList->count() > 1) { - m_pComboServerList->show(); - } + if (m_pComboServerList->count() > 1) { + m_pComboServerList->show(); + } } void MainWindow::setEdition(Edition edition) { - setWindowTitle(m_SubscriptionManager->getEditionName (edition)); - if (m_AppConfig->getCryptoEnabled()) { - m_pSslCertificate = new SslCertificate(this); - m_pSslCertificate->generateCertificate(); - } - updateLocalFingerprint(); - saveSettings(); + setWindowTitle(m_SubscriptionManager->getEditionName (edition)); + if (m_AppConfig->getCryptoEnabled()) { + m_pSslCertificate = new SslCertificate(this); + m_pSslCertificate->generateCertificate(); + } + updateLocalFingerprint(); + saveSettings(); } void MainWindow::beginTrial(bool isExpiring) { - if (isExpiring) { + if (isExpiring) { QString expiringNotice = "

%1 days of " "your Synergy Pro trial remain. setText(fileName); - return true; - } + if (!fileName.isEmpty()) + { + m_pLineEditConfigFile->setText(fileName); + return true; + } - return false; + return false; } bool MainWindow::on_m_pActionSave_triggered() { - QString fileName = QFileDialog::getSaveFileName(this, tr("Save configuration as...")); + 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; - } + if (!fileName.isEmpty() && !serverConfig().save(fileName)) + { + QMessageBox::warning(this, tr("Save failed"), tr("Could not save configuration to file.")); + return true; + } - return false; + return false; } void MainWindow::on_m_pActionAbout_triggered() { - AboutDialog dlg(this, appPath(appConfig().synergycName())); - dlg.exec(); + AboutDialog dlg(this, appPath(appConfig().synergycName())); + dlg.exec(); } void MainWindow::on_m_pActionSettings_triggered() { - ProcessMode lastProcessMode = appConfig().processMode(); + ProcessMode lastProcessMode = appConfig().processMode(); - SettingsDialog dlg(this, appConfig()); - dlg.exec(); + SettingsDialog dlg(this, appConfig()); + dlg.exec(); - if (lastProcessMode != appConfig().processMode()) - { - onModeChanged(true, true); - } + if (lastProcessMode != appConfig().processMode()) + { + onModeChanged(true, true); + } } void MainWindow::autoAddScreen(const QString name) { - if (!m_ServerConfig.ignoreAutoConfigClient()) { - int r = m_ServerConfig.autoAddScreen(name); - if (r != kAutoAddScreenOk) { - switch (r) { - case kAutoAddScreenManualServer: - showConfigureServer( - tr("Please add the server (%1) to the grid.") - .arg(appConfig().screenName())); - break; - - case kAutoAddScreenManualClient: - showConfigureServer( - tr("Please drag the new client screen (%1) " - "to the desired position on the grid.") - .arg(name)); - break; - } - } - else { - restartSynergy(); - } - } + if (!m_ServerConfig.ignoreAutoConfigClient()) { + int r = m_ServerConfig.autoAddScreen(name); + if (r != kAutoAddScreenOk) { + switch (r) { + case kAutoAddScreenManualServer: + showConfigureServer( + tr("Please add the server (%1) to the grid.") + .arg(appConfig().screenName())); + break; + + case kAutoAddScreenManualClient: + showConfigureServer( + tr("Please drag the new client screen (%1) " + "to the desired position on the grid.") + .arg(name)); + break; + } + } + else { + restartSynergy(); + } + } } void MainWindow::showConfigureServer(const QString& message) { - ServerConfigDialog dlg(this, serverConfig(), appConfig().screenName()); - dlg.message(message); - dlg.exec(); + ServerConfigDialog dlg(this, serverConfig(), appConfig().screenName()); + dlg.message(message); + dlg.exec(); } void MainWindow::on_m_pButtonConfigureServer_clicked() { - showConfigureServer(); + showConfigureServer(); } void MainWindow::on_m_pActivate_triggered() { - ActivationDialog activationDialog(this, appConfig(), subscriptionManager()); - activationDialog.exec(); + ActivationDialog activationDialog(this, appConfig(), subscriptionManager()); + activationDialog.exec(); } void MainWindow::on_m_pButtonApply_clicked() { - restartSynergy(); + restartSynergy(); } #if defined(Q_OS_WIN) bool MainWindow::isServiceRunning(QString name) { - SC_HANDLE hSCManager; - hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); - if (hSCManager == NULL) { - appendLogError("failed to open a service controller manager, error: " + - GetLastError()); - return false; - } - - SC_HANDLE hService; - int length = name.length(); - wchar_t* array = new wchar_t[length + 1]; - name.toWCharArray(array); - array[length] = '\0'; - - hService = OpenService(hSCManager, array, SERVICE_QUERY_STATUS); - - delete[] array; - - if (hService == NULL) { - appendLogDebug("failed to open service: " + name); - return false; - } - - SERVICE_STATUS status; - if (QueryServiceStatus(hService, &status)) { - if (status.dwCurrentState == SERVICE_RUNNING) { - return true; - } - } + SC_HANDLE hSCManager; + hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); + if (hSCManager == NULL) { + appendLogError("failed to open a service controller manager, error: " + + GetLastError()); + return false; + } + + SC_HANDLE hService; + int length = name.length(); + wchar_t* array = new wchar_t[length + 1]; + name.toWCharArray(array); + array[length] = '\0'; + + hService = OpenService(hSCManager, array, SERVICE_QUERY_STATUS); + + delete[] array; + + if (hService == NULL) { + appendLogDebug("failed to open service: " + name); + return false; + } + + SERVICE_STATUS status; + if (QueryServiceStatus(hService, &status)) { + if (status.dwCurrentState == SERVICE_RUNNING) { + return true; + } + } #else bool MainWindow::isServiceRunning() { #endif - return false; + return false; } bool MainWindow::isBonjourRunning() { - bool result = false; + bool result = false; #if defined(Q_OS_WIN) - result = isServiceRunning("Bonjour Service"); + result = isServiceRunning("Bonjour Service"); #else - result = true; + result = true; #endif - return result; + return result; } void MainWindow::downloadBonjour() { #if defined(Q_OS_WIN) - QUrl url; - int arch = getProcessorArch(); - if (arch == kProcessorArchWin32) { - url.setUrl(bonjourBaseUrl + bonjourFilename32); - appendLogInfo("downloading 32-bit Bonjour"); - } - else if (arch == kProcessorArchWin64) { - url.setUrl(bonjourBaseUrl + bonjourFilename64); - appendLogInfo("downloading 64-bit Bonjour"); - } - else { - QMessageBox::critical( - this, tr("Synergy"), - tr("Failed to detect system architecture.")); - return; - } - - if (m_pDataDownloader == NULL) { - m_pDataDownloader = new DataDownloader(this); - connect(m_pDataDownloader, SIGNAL(isComplete()), SLOT(installBonjour())); - } - - m_pDataDownloader->download(url); - - if (m_DownloadMessageBox == NULL) { - m_DownloadMessageBox = new QMessageBox(this); - m_DownloadMessageBox->setWindowTitle("Synergy"); - m_DownloadMessageBox->setIcon(QMessageBox::Information); - m_DownloadMessageBox->setText("Installing Bonjour, please wait..."); - m_DownloadMessageBox->setStandardButtons(0); - m_pCancelButton = m_DownloadMessageBox->addButton( - tr("Cancel"), QMessageBox::RejectRole); - } - - m_DownloadMessageBox->exec(); - - if (m_DownloadMessageBox->clickedButton() == m_pCancelButton) { - m_pDataDownloader->cancel(); - } + QUrl url; + int arch = getProcessorArch(); + if (arch == kProcessorArchWin32) { + url.setUrl(bonjourBaseUrl + bonjourFilename32); + appendLogInfo("downloading 32-bit Bonjour"); + } + else if (arch == kProcessorArchWin64) { + url.setUrl(bonjourBaseUrl + bonjourFilename64); + appendLogInfo("downloading 64-bit Bonjour"); + } + else { + QMessageBox::critical( + this, tr("Synergy"), + tr("Failed to detect system architecture.")); + return; + } + + if (m_pDataDownloader == NULL) { + m_pDataDownloader = new DataDownloader(this); + connect(m_pDataDownloader, SIGNAL(isComplete()), SLOT(installBonjour())); + } + + m_pDataDownloader->download(url); + + if (m_DownloadMessageBox == NULL) { + m_DownloadMessageBox = new QMessageBox(this); + m_DownloadMessageBox->setWindowTitle("Synergy"); + m_DownloadMessageBox->setIcon(QMessageBox::Information); + m_DownloadMessageBox->setText("Installing Bonjour, please wait..."); + m_DownloadMessageBox->setStandardButtons(0); + m_pCancelButton = m_DownloadMessageBox->addButton( + tr("Cancel"), QMessageBox::RejectRole); + } + + m_DownloadMessageBox->exec(); + + if (m_DownloadMessageBox->clickedButton() == m_pCancelButton) { + m_pDataDownloader->cancel(); + } #endif } void MainWindow::installBonjour() { #if defined(Q_OS_WIN) - QString tempLocation = QDesktopServices::storageLocation( - QDesktopServices::TempLocation); - QString filename = tempLocation; - filename.append("\\").append(bonjourTargetFilename); - QFile file(filename); - if (!file.open(QIODevice::WriteOnly)) { - m_DownloadMessageBox->hide(); - - QMessageBox::warning( - this, "Synergy", - tr("Failed to download Bonjour installer to location: %1") - .arg(tempLocation)); - return; - } - - file.write(m_pDataDownloader->data()); - file.close(); - - QStringList arguments; - arguments.append("/i"); - QString winFilename = QDir::toNativeSeparators(filename); - arguments.append(winFilename); - arguments.append("/passive"); - if (m_BonjourInstall == NULL) { - m_BonjourInstall = new CommandProcess("msiexec", arguments); - } - - QThread* thread = new QThread; - connect(m_BonjourInstall, SIGNAL(finished()), this, - SLOT(bonjourInstallFinished())); - connect(m_BonjourInstall, SIGNAL(finished()), thread, SLOT(quit())); - connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); - - m_BonjourInstall->moveToThread(thread); - thread->start(); - - QMetaObject::invokeMethod(m_BonjourInstall, "run", Qt::QueuedConnection); - - m_DownloadMessageBox->hide(); + QString tempLocation = QDesktopServices::storageLocation( + QDesktopServices::TempLocation); + QString filename = tempLocation; + filename.append("\\").append(bonjourTargetFilename); + QFile file(filename); + if (!file.open(QIODevice::WriteOnly)) { + m_DownloadMessageBox->hide(); + + QMessageBox::warning( + this, "Synergy", + tr("Failed to download Bonjour installer to location: %1") + .arg(tempLocation)); + return; + } + + file.write(m_pDataDownloader->data()); + file.close(); + + QStringList arguments; + arguments.append("/i"); + QString winFilename = QDir::toNativeSeparators(filename); + arguments.append(winFilename); + arguments.append("/passive"); + if (m_BonjourInstall == NULL) { + m_BonjourInstall = new CommandProcess("msiexec", arguments); + } + + QThread* thread = new QThread; + connect(m_BonjourInstall, SIGNAL(finished()), this, + SLOT(bonjourInstallFinished())); + connect(m_BonjourInstall, SIGNAL(finished()), thread, SLOT(quit())); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + + m_BonjourInstall->moveToThread(thread); + thread->start(); + + QMetaObject::invokeMethod(m_BonjourInstall, "run", Qt::QueuedConnection); + + m_DownloadMessageBox->hide(); #endif } void MainWindow::promptAutoConfig() { - if (!isBonjourRunning()) { - int r = QMessageBox::question( - this, tr("Synergy"), - tr("Do you want to enable auto config and install Bonjour?\n\n" - "This feature helps you establish the connection."), - QMessageBox::Yes | QMessageBox::No); - - if (r == QMessageBox::Yes) { - m_AppConfig->setAutoConfig(true); - downloadBonjour(); - } - else { - m_AppConfig->setAutoConfig(false); - m_pCheckBoxAutoConfig->setChecked(false); - } - } + if (!isBonjourRunning()) { + int r = QMessageBox::question( + this, tr("Synergy"), + tr("Do you want to enable auto config and install Bonjour?\n\n" + "This feature helps you establish the connection."), + QMessageBox::Yes | QMessageBox::No); + + if (r == QMessageBox::Yes) { + m_AppConfig->setAutoConfig(true); + downloadBonjour(); + } + else { + m_AppConfig->setAutoConfig(false); + m_pCheckBoxAutoConfig->setChecked(false); + } + } - m_AppConfig->setAutoConfigPrompted(true); + m_AppConfig->setAutoConfigPrompted(true); } void MainWindow::on_m_pComboServerList_currentIndexChanged(QString ) { - if (m_pComboServerList->count() != 0) { - restartSynergy(); - } + if (m_pComboServerList->count() != 0) { + restartSynergy(); + } } void MainWindow::on_m_pCheckBoxAutoConfig_toggled(bool checked) { - if (!isBonjourRunning() && checked) { - if (!m_SuppressAutoConfigWarning) { - int r = QMessageBox::information( - this, tr("Synergy"), - tr("Auto config feature requires Bonjour.\n\n" - "Do you want to install Bonjour?"), - QMessageBox::Yes | QMessageBox::No); - - if (r == QMessageBox::Yes) { - downloadBonjour(); - } - } - - m_pCheckBoxAutoConfig->setChecked(false); - return; - } + if (!isBonjourRunning() && checked) { + if (!m_SuppressAutoConfigWarning) { + int r = QMessageBox::information( + this, tr("Synergy"), + tr("Auto config feature requires Bonjour.\n\n" + "Do you want to install Bonjour?"), + QMessageBox::Yes | QMessageBox::No); + + if (r == QMessageBox::Yes) { + downloadBonjour(); + } + } + + m_pCheckBoxAutoConfig->setChecked(false); + return; + } - m_pLineEditHostname->setDisabled(checked); - appConfig().setAutoConfig(checked); - updateZeroconfService(); + m_pLineEditHostname->setDisabled(checked); + appConfig().setAutoConfig(checked); + updateZeroconfService(); - if (!checked) { - m_pComboServerList->clear(); - m_pComboServerList->hide(); - } + if (!checked) { + m_pComboServerList->clear(); + m_pComboServerList->hide(); + } } void MainWindow::bonjourInstallFinished() { - appendLogInfo("Bonjour install finished"); + appendLogInfo("Bonjour install finished"); - m_pCheckBoxAutoConfig->setChecked(true); + m_pCheckBoxAutoConfig->setChecked(true); } void MainWindow::on_windowShown() { - if (!m_AppConfig->activationHasRun() && (m_AppConfig->edition() == kUnregistered)) { - ActivationDialog activationDialog (this, appConfig(), subscriptionManager()); - activationDialog.exec(); - } + if (!m_AppConfig->activationHasRun() && (m_AppConfig->edition() == kUnregistered)) { + ActivationDialog activationDialog (this, appConfig(), subscriptionManager()); + activationDialog.exec(); + } } QString MainWindow::getProfileRootForArg() { - CoreInterface coreInterface; - QString dir = coreInterface.getProfileDir(); + CoreInterface coreInterface; + QString dir = coreInterface.getProfileDir(); - // HACK: strip our app name since we're returning the root dir. + // HACK: strip our app name since we're returning the root dir. #if defined(Q_OS_WIN) - dir.replace("\\Synergy", ""); + dir.replace("\\Synergy", ""); #else - dir.replace("/.synergy", ""); + dir.replace("/.synergy", ""); #endif - return QString("\"%1\"").arg(dir); + return QString("\"%1\"").arg(dir); } diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index 1ee077eb6..2e6316c5d 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -33,8 +33,7 @@ SerialKey::SerialKey(Edition edition): m_warnTime(ULLONG_MAX), m_expireTime(ULLONG_MAX), m_edition(edition), - m_trial(false), - m_valid(true) + m_trial(false) { } @@ -43,46 +42,25 @@ SerialKey::SerialKey(std::string serial) : m_warnTime(0), m_expireTime(0), m_edition(kBasic), - m_trial(true), - m_valid(false) + m_trial(true) { string plainText = decode(serial); + bool valid = false; if (!plainText.empty()) { - parse(plainText); + valid = parse(plainText); } - if (!m_valid) { + if (!valid) { throw std::runtime_error ("Invalid serial key"); } } -bool -SerialKey::isValid(time_t currentTime) const -{ - bool result = false; - - if (m_valid) { - if (m_trial) { - if (currentTime < m_expireTime) { - result = true; - } - } - else { - result = true; - } - } - - return result; -} - bool SerialKey::isExpiring(time_t currentTime) const { bool result = false; - if (m_valid) { - if (m_warnTime <= currentTime && currentTime < m_expireTime) { - result = true; - } + if (m_warnTime <= currentTime && currentTime < m_expireTime) { + result = true; } return result; @@ -93,12 +71,11 @@ SerialKey::isExpired(time_t currentTime) const { bool result = false; - if (m_valid) { - if (m_expireTime <= currentTime) { - result = true; - } + if (m_expireTime <= currentTime) { + result = true; } + return result; } @@ -209,13 +186,13 @@ SerialKey::decode(const std::string& serial) return output; } -void +bool SerialKey::parse(std::string plainSerial) { string parityStart = plainSerial.substr(0, 1); string parityEnd = plainSerial.substr(plainSerial.length() - 1, 1); - m_valid = false; + bool valid = false; // check for parity chars { and }, record parity result, then remove them. if (parityStart == "{" && parityEnd == "}") { @@ -247,7 +224,7 @@ SerialKey::parse(std::string plainSerial) m_company = parts.at(5); sscanf(parts.at(6).c_str(), "%lld", &m_warnTime); sscanf(parts.at(7).c_str(), "%lld", &m_expireTime); - m_valid = true; + valid = true; } else if ((parts.size() == 9) && (parts.at(0).find("v2") != string::npos)) { @@ -260,9 +237,11 @@ SerialKey::parse(std::string plainSerial) m_company = parts.at(6); sscanf(parts.at(7).c_str(), "%lld", &m_warnTime); sscanf(parts.at(8).c_str(), "%lld", &m_expireTime); - m_valid = true; + valid = true; } } + + return valid; } Edition diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h index c6ae3f207..29b12a9d4 100644 --- a/src/lib/shared/SerialKey.h +++ b/src/lib/shared/SerialKey.h @@ -31,7 +31,6 @@ class SerialKey { explicit SerialKey(Edition edition = kUnregistered); explicit SerialKey(std::string serial); - bool isValid(time_t currentTime) const; bool isExpiring(time_t currentTime) const; bool isExpired(time_t currentTime) const; bool isTrial() const; @@ -44,7 +43,7 @@ class SerialKey { static Edition parseEdition(const std::string& editionStr); private: - void parse(std::string plainSerial); + bool parse(std::string plainSerial); std::string editionString() const; #ifdef TEST_ENV @@ -67,7 +66,6 @@ class SerialKey { unsigned long long m_expireTime; Edition m_edition; bool m_trial; - bool m_valid; }; @@ -80,8 +78,7 @@ operator== (SerialKey const& lhs, SerialKey const& rhs) { (lhs.m_warnTime == rhs.m_warnTime) && (lhs.m_expireTime == rhs.m_expireTime) && (lhs.m_edition == rhs.m_edition) && - (lhs.m_trial == rhs.m_trial) && - (lhs.m_valid == rhs.m_valid); + (lhs.m_trial == rhs.m_trial); } inline bool From 599415f0473611cf56c38d9d3735a2a9c6f8df28 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 17 Oct 2016 17:11:30 +0100 Subject: [PATCH 146/241] #5657 Only check trial times when using a trial --- src/lib/shared/SerialKey.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index 2e6316c5d..cdc7695f8 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -59,8 +59,10 @@ SerialKey::isExpiring(time_t currentTime) const { bool result = false; - if (m_warnTime <= currentTime && currentTime < m_expireTime) { - result = true; + if (m_trial) { + if (m_warnTime <= currentTime && currentTime < m_expireTime) { + result = true; + } } return result; @@ -71,11 +73,12 @@ SerialKey::isExpired(time_t currentTime) const { bool result = false; - if (m_expireTime <= currentTime) { - result = true; + if (m_trial) { + if (m_expireTime <= currentTime) { + result = true; + } } - return result; } From 88c59b4ca6ad49b0577faafc449799f82504886a Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 17 Oct 2016 17:37:30 +0100 Subject: [PATCH 147/241] #5657 Fix unit tests after SerialKey::isValid removal --- src/test/unittests/shared/SerialKeyTests.cpp | 69 ++++------------------------ 1 file changed, 9 insertions(+), 60 deletions(-) diff --git a/src/test/unittests/shared/SerialKeyTests.cpp b/src/test/unittests/shared/SerialKeyTests.cpp index dfb8685ba..126d26e8f 100644 --- a/src/test/unittests/shared/SerialKeyTests.cpp +++ b/src/test/unittests/shared/SerialKeyTests.cpp @@ -42,33 +42,33 @@ TEST(SerialKeyTests, decode_validSerial_returnPlainText) TEST(SerialKeyTests, parse_noParty_invalid) { SerialKey serial; - serial.parse("MOCK"); - EXPECT_FALSE(serial.isValid(0)); + bool r = serial.parse("MOCK"); + EXPECT_FALSE(r); } TEST(SerialKeyTests, parse_invalidPartsLenghth_invalid) { SerialKey serial; - serial.parse("{Synergy;Rocks}"); - EXPECT_FALSE(serial.isValid(0)); + bool r = serial.parse("{Synergy;Rocks}"); + EXPECT_FALSE(r); } TEST(SerialKeyTests, parse_validV1Serial_valid) { SerialKey serial; - serial.parse("{v1;basic;Bob;1;email;company name;0;86400}"); - EXPECT_EQ(true, serial.isValid(0)); + bool r = serial.parse("{v1;basic;Bob;1;email;company name;0;86400}"); + EXPECT_EQ(true, r); EXPECT_EQ(kBasic, serial.edition()); EXPECT_FALSE(serial.isExpired(0)); EXPECT_EQ(true, serial.daysLeft(0)); - EXPECT_EQ(true, serial.isExpiring(1)); + EXPECT_FALSE(serial.isExpiring(1)); } TEST(SerialKeyTests, parse_validV2Serial_valid) { SerialKey serial; - serial.parse("{v2;trial;pro;Bob;1;email;company name;0;86400}"); - EXPECT_EQ(true, serial.isValid(0)); + bool r = serial.parse("{v2;trial;pro;Bob;1;email;company name;0;86400}"); + EXPECT_EQ(true, r); EXPECT_EQ(kPro, serial.edition()); EXPECT_FALSE(serial.isExpired(0)); EXPECT_EQ(true, serial.daysLeft(0)); @@ -76,57 +76,6 @@ TEST(SerialKeyTests, parse_validV2Serial_valid) EXPECT_EQ(true, serial.isTrial()); } -TEST(SerialKeyTests, isValid_validV1BasicSerial_valid) -{ - // {v1;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76313B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(true, serial.isValid(0)); - EXPECT_EQ(kBasic, serial.edition()); -} - -TEST(SerialKeyTests, isValid_expiredV1ProSerial_valid) -{ - // {v1;pro;Bob;1;email;company name;0;86400} - SerialKey serial("7B76313B70726F3B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(true, serial.isValid(0)); - EXPECT_EQ(kPro, serial.edition()); -} - -TEST(SerialKeyTests, isValid_validV2LifetimeBasicSerial_valid) -{ - // {v2;lifetime;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B6C69666574696D653B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(true, serial.isValid(0)); - EXPECT_EQ(kBasic, serial.edition()); -} - -TEST(SerialKeyTests, isValid_validV2LifetimeProSerial_valid) -{ - // {v2;lifetime;pro;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B6C69666574696D653B70726F3B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(true, serial.isValid(0)); - EXPECT_EQ(kPro, serial.edition()); -} - -TEST(SerialKeyTests, isValid_validV2TrialBasicSerial_valid) -{ - // {v2;trial;basic;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(true, serial.isTrial()); - EXPECT_EQ(true, serial.isValid(0)); - EXPECT_EQ(kBasic, serial.edition()); - -} - -TEST(SerialKeyTests, isValid_expiredV2TrialProSerial_invalid) -{ - // {v2;trial;pro;Bob;1;email;company name;0;86400} - SerialKey serial("7B76323B747269616C3B70726F3B426F623B313B656D61696C3B636F6D70616E79206E616D653B303B38363430307D"); - EXPECT_EQ(true, serial.isTrial()); - EXPECT_FALSE(serial.isValid(86401)); - EXPECT_EQ(kPro, serial.edition()); -} - TEST(SerialKeyTests, isExpiring_validV2TrialBasicSerial_returnFalse) { // {v2;trial;basic;Bob;1;email;company name;1;86400} From 99dbdc5eb3b57d9c5d36d08964039d2fcadca357 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 17 Oct 2016 17:38:05 +0100 Subject: [PATCH 148/241] #5657 Use SerialKey class in ServerArgs --- src/lib/server/Server.cpp | 42 ++++++++++------------ src/lib/shared/SerialKey.h | 3 -- src/lib/synergy/ArgParser.cpp | 24 ++++++------- src/lib/synergy/ServerArgs.h | 7 ++-- .../unittests/synergy/ServerArgsParsingTests.cpp | 18 ++-------- 5 files changed, 37 insertions(+), 57 deletions(-) diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index a202648e7..b99e3cf1c 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -2,11 +2,11 @@ * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman - * + * * 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 @@ -453,15 +453,11 @@ Server::switchScreen(BaseClientProxy* dst, SInt32 x, SInt32 y, bool forScreensaver) { assert(dst != NULL); - + // if trial is expired, exit the process - if (!m_args.m_serial.empty()) { - SerialKey serial(m_args.m_serial); - if (!serial.isValid(std::time(0))) { - LOG((CLOG_ERR "trial is expired, aborting server")); - exit(kExitSuccess); - return; - } + if (!m_args.m_serial.isExpired(std::time(0))) { + LOG((CLOG_ERR "trial is expired, aborting server")); + exit(kExitSuccess); } #ifndef NDEBUG @@ -547,7 +543,7 @@ Server::jumpToScreen(BaseClientProxy* newScreen) // get the last cursor position on the target screen SInt32 x, y; newScreen->getJumpCursorPos(x, y); - + switchScreen(newScreen, x, y, false); } @@ -897,14 +893,14 @@ Server::isSwitchOkay(BaseClientProxy* newScreen, if (!preventSwitch && ( (this->m_switchNeedsShift && ((mods & KeyModifierShift) != KeyModifierShift)) || - (this->m_switchNeedsControl && ((mods & KeyModifierControl) != KeyModifierControl)) || + (this->m_switchNeedsControl && ((mods & KeyModifierControl) != KeyModifierControl)) || (this->m_switchNeedsAlt && ((mods & KeyModifierAlt) != KeyModifierAlt)) )) { LOG((CLOG_DEBUG1 "need modifiers to switch")); preventSwitch = true; stopSwitch(); - } - + } + return !preventSwitch; } @@ -1184,7 +1180,7 @@ Server::processOptions() } else if (id == kOptionClipboardSharing) { m_enableClipboard = (value != 0); - + if (m_enableClipboard == false) { LOG((CLOG_NOTE "clipboard sharing is disabled")); } @@ -1413,7 +1409,7 @@ Server::handleClientCloseTimeout(const Event&, void* vclient) void Server::handleSwitchToScreenEvent(const Event& event, void*) { - SwitchToScreenInfo* info = + SwitchToScreenInfo* info = static_cast(event.getData()); ClientList::const_iterator index = m_clients.find(info->m_screen); @@ -1428,7 +1424,7 @@ Server::handleSwitchToScreenEvent(const Event& event, void*) void Server::handleSwitchInDirectionEvent(const Event& event, void*) { - SwitchInDirectionInfo* info = + SwitchInDirectionInfo* info = static_cast(event.getData()); // jump to screen in chosen direction from center of this screen @@ -1718,7 +1714,7 @@ Server::onMouseUp(ButtonID id) m_ignoreFileTransfer = false; return; } - + if (m_args.m_enableDragDrop) { if (!m_screen->isOnScreen()) { String& file = m_screen->getDraggingFilename(); @@ -1823,7 +1819,7 @@ Server::onMouseMovePrimary(SInt32 x, SInt32 y) m_waitDragInfoThread = true; return true; } - + return false; } @@ -1839,7 +1835,7 @@ Server::sendDragInfoThread(void* arg) di.setFilename(dragFileList); m_dragFileList.push_back(di); } - + #if defined(__APPLE__) // on mac it seems that after faking a LMB up, system would signal back // to synergy a mouse up event, which doesn't happen on windows. as a @@ -1862,7 +1858,7 @@ Server::sendDragInfo(BaseClientProxy* newScreen) { String infoString; UInt32 fileCount = DragInformation::setupDragInfo(m_dragFileList, infoString); - + if (fileCount > 0) { char* info = NULL; size_t size = infoString.size(); @@ -2072,7 +2068,7 @@ Server::onFileChunkSending(const void* data) assert(m_active != NULL); // relay - m_active->fileChunkSending(chunk->m_chunk[0], &chunk->m_chunk[1], chunk->m_dataSize); + m_active->fileChunkSending(chunk->m_chunk[0], &chunk->m_chunk[1], chunk->m_dataSize); } void @@ -2381,7 +2377,7 @@ Server::sendFileToClient(const char* filename) if (m_sendFileThread != NULL) { StreamChunker::interruptFile(); } - + m_sendFileThread = new Thread( new TMethodJob( this, &Server::sendFileThread, diff --git a/src/lib/shared/SerialKey.h b/src/lib/shared/SerialKey.h index 29b12a9d4..dd9b71606 100644 --- a/src/lib/shared/SerialKey.h +++ b/src/lib/shared/SerialKey.h @@ -48,9 +48,6 @@ class SerialKey { #ifdef TEST_ENV private: - FRIEND_TEST(SerialKeyTests, decode_empty_returnEmptyString); - FRIEND_TEST(SerialKeyTests, decode_invalidDigit_returnEmptyString); - FRIEND_TEST(SerialKeyTests, decode_validSerial_returnPlainText); FRIEND_TEST(SerialKeyTests, parse_noParty_invalid); FRIEND_TEST(SerialKeyTests, parse_invalidPartsLenghth_invalid); FRIEND_TEST(SerialKeyTests, parse_validV1Serial_valid); diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index d16a6fa94..1af32843b 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -1,11 +1,11 @@ /* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2014-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 @@ -71,7 +71,7 @@ ArgParser::parseServerArgs(ServerArgs& args, int argc, const char* const* argv) DpiHelper::s_primaryHeightCenter = synergy::string::stringToSizeType(argv[++i]); } else if (isArg(i, argc, argv, "", "--serial-key", 1)) { - args.m_serial = argv[++i]; + args.m_serial = SerialKey(argv[++i]); } else { LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, args.m_pname, argv[i], args.m_pname)); @@ -110,7 +110,7 @@ ArgParser::parseClientArgs(ClientArgs& args, int argc, const char* const* argv) // ignore -- included for backwards compatibility } else if (isArg(i, argc, argv, NULL, "--yscroll", 1)) { - // define scroll + // define scroll args.m_yscroll = atoi(argv[++i]); } else { @@ -271,10 +271,10 @@ ArgParser::parseGenericArgs(int argc, const char* const* argv, int& i) argsBase().m_enableIpc = true; } else if (isArg(i, argc, argv, NULL, "--server")) { - // HACK: stop error happening when using portable (synergyp) + // HACK: stop error happening when using portable (synergyp) } else if (isArg(i, argc, argv, NULL, "--client")) { - // HACK: stop error happening when using portable (synergyp) + // HACK: stop error happening when using portable (synergyp) } else if (isArg(i, argc, argv, NULL, "--enable-drag-drop")) { bool useDragDrop = true; @@ -378,7 +378,7 @@ ArgParser::splitCommandString(String& command, std::vector& argv) else if (space > rightDoubleQuote){ searchDoubleQuotes(command, leftDoubleQuote, rightDoubleQuote, rightDoubleQuote + 1); } - + if (!ignoreThisSpace) { String subString = command.substr(startPos, space - startPos); @@ -444,7 +444,7 @@ ArgParser::getArgv(std::vector& argsArray) // them to the inner array. So caller only need to use // delete[] to delete the outer array const char** argv = new const char*[argc]; - + for (size_t i = 0; i < argc; i++) { argv[i] = argsArray[i].c_str(); } @@ -476,7 +476,7 @@ ArgParser::assembleCommand(std::vector& argsArray, String ignoreArg, in if (!result.empty()) { // remove the tail space - result = result.substr(0, result.size() - 1); + result = result.substr(0, result.size() - 1); } return result; @@ -493,13 +493,13 @@ bool ArgParser::checkUnexpectedArgs() { #if SYSAPI_WIN32 - // suggest that user installs as a windows service. when launched as + // suggest that user installs as a windows service. when launched as // service, process should automatically detect that it should run in // daemon mode. if (argsBase().m_daemon) { - LOG((CLOG_ERR + LOG((CLOG_ERR "the --daemon argument is not supported on windows. " - "instead, install %s as a service (--service install)", + "instead, install %s as a service (--service install)", argsBase().m_pname)); return true; } diff --git a/src/lib/synergy/ServerArgs.h b/src/lib/synergy/ServerArgs.h index 7c69fae64..e139d1102 100644 --- a/src/lib/synergy/ServerArgs.h +++ b/src/lib/synergy/ServerArgs.h @@ -1,11 +1,11 @@ /* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2014-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 @@ -18,6 +18,7 @@ #pragma once #include "synergy/ArgsBase.h" +#include "shared/SerialKey.h" class NetworkAddress; class Config; @@ -28,6 +29,6 @@ class ServerArgs : public ArgsBase { public: String m_configFile; - String m_serial; + SerialKey m_serial; Config* m_config; }; diff --git a/src/test/unittests/synergy/ServerArgsParsingTests.cpp b/src/test/unittests/synergy/ServerArgsParsingTests.cpp index 92db8c0d5..f931f7e5d 100644 --- a/src/test/unittests/synergy/ServerArgsParsingTests.cpp +++ b/src/test/unittests/synergy/ServerArgsParsingTests.cpp @@ -1,11 +1,11 @@ /* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2014-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 @@ -64,17 +64,3 @@ TEST(ServerArgsParsingTests, parseServerArgs_configArg_setConfigFile) EXPECT_EQ("mock_configFile", serverArgs.m_configFile); } - -TEST(ServerArgsParsingTests, parseServerArgs_serialArg_setSerial) -{ - NiceMock argParser; - ON_CALL(argParser, parseGenericArgs(_, _, _)).WillByDefault(Invoke(server_stubParseGenericArgs)); - ON_CALL(argParser, checkUnexpectedArgs()).WillByDefault(Invoke(server_stubCheckUnexpectedArgs)); - ServerArgs serverArgs; - const int argc = 3; - const char* kSerialCmd[argc] = { "stub", "--serial-key", "mock_serial" }; - - argParser.parseServerArgs(serverArgs, argc, kSerialCmd); - - EXPECT_EQ("mock_serial", serverArgs.m_serial); -} From 5a34da3ce0e5b56a85de9c03fbf2e7da09fd4eec Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 17 Oct 2016 17:49:14 +0100 Subject: [PATCH 149/241] #5657 Refresh UI when synergys reports trial expired --- src/gui/src/MainWindow.cpp | 11 +- src/gui/src/MainWindow.h | 323 +++++++++++++++++++++++---------------------- src/lib/server/Server.cpp | 2 +- 3 files changed, 172 insertions(+), 164 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 64a9f50d3..d88498997 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -411,12 +411,12 @@ void MainWindow::appendLogRaw(const QString& text) foreach(QString line, text.split(QRegExp("\r|\n|\r\n"))) { if (!line.isEmpty()) { m_pLogOutput->append(line); - updateStateFromLogLine(line); + updateFromLogLine(line); } } } -void MainWindow::updateStateFromLogLine(const QString &line) +void MainWindow::updateFromLogLine(const QString &line) { checkConnected(line); checkFingerprint(line); @@ -444,6 +444,13 @@ void MainWindow::checkConnected(const QString& line) } } +void MainWindow::checkLicense(const QString &line) +{ + if (line.contains("trial has expired")) { + m_SubscriptionManager->refresh(); + } +} + void MainWindow::checkFingerprint(const QString& line) { QRegExp fingerprintRegex(".*server fingerprint: ([A-F0-9:]+)"); diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index c00afdba7..27fff56a2 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -62,174 +62,175 @@ class SubscriptionManager; class MainWindow : public QMainWindow, public Ui::MainWindowBase { - Q_OBJECT - - friend class QSynergyApplication; - friend class SetupWizard; - friend class ActivationDialog; - friend class SettingsDialog; - - public: - enum qSynergyState - { - synergyDisconnected, - synergyConnecting, - synergyConnected, - synergyTransfering - }; - - enum qSynergyType - { - synergyClient, - synergyServer - }; - - enum qLevel { - Error, - Info - }; - - enum qRuningState { - kStarted, - kStopped - }; - - public: - MainWindow(QSettings& settings, AppConfig& appConfig, - SubscriptionManager& subscriptionManager); - ~MainWindow(); - - public: - void setVisible(bool visible); - int synergyType() const { return m_pGroupClient->isChecked() ? synergyClient : synergyServer; } - int synergyState() const { return m_SynergyState; } - QString hostname() const { return m_pLineEditHostname->text(); } - QString configFilename(); - QString address(); - QString appPath(const QString& name); - void open(); - void clearLog(); - VersionChecker& versionChecker() { return m_VersionChecker; } - QString getScreenName(); - ServerConfig& serverConfig() { return m_ServerConfig; } - void showConfigureServer(const QString& message); - void showConfigureServer() { showConfigureServer(""); } - void autoAddScreen(const QString name); - void updateZeroconfService(); - void serverDetected(const QString name); - void updateLocalFingerprint(); - SubscriptionManager& subscriptionManager() const; - - public slots: - void setEdition(Edition edition); - void beginTrial(bool isExpiring); - void endTrial(bool isExpired); - void appendLogRaw(const QString& text); - void appendLogInfo(const QString& text); - void appendLogDebug(const QString& text); - void appendLogError(const QString& text); - void startSynergy(); - - protected slots: - void sslToggled(bool enabled); - void on_m_pGroupClient_toggled(bool on); - void on_m_pGroupServer_toggled(bool on); - bool on_m_pButtonBrowseConfigFile_clicked(); - void on_m_pButtonConfigureServer_clicked(); - bool on_m_pActionSave_triggered(); - void on_m_pActionAbout_triggered(); - void on_m_pActionSettings_triggered(); - void on_m_pActivate_triggered(); - void synergyFinished(int exitCode, QProcess::ExitStatus); - void trayActivated(QSystemTrayIcon::ActivationReason reason); - void stopSynergy(); - void logOutput(); - void logError(); - void updateFound(const QString& version); - void bonjourInstallFinished(); - - protected: - QSettings& settings() { return m_Settings; } - AppConfig& appConfig() { return *m_AppConfig; } - QProcess* synergyProcess() { return m_pSynergy; } - void setSynergyProcess(QProcess* p) { m_pSynergy = p; } - void initConnections(); - void createMenuBar(); - void createStatusBar(); - void createTrayIcon(); - void loadSettings(); - void saveSettings(); - void setIcon(qSynergyState state); - void setSynergyState(qSynergyState state); - bool checkForApp(int which, QString& app); - bool clientArgs(QStringList& args, QString& app); - bool serverArgs(QStringList& args, QString& app); - void setStatus(const QString& status); - void sendIpcMessage(qIpcMessageType type, const char* buffer, bool showErrors); - void onModeChanged(bool startDesktop, bool applyService); - void updateStateFromLogLine(const QString& line); - QString getIPAddresses(); - void stopService(); - void stopDesktop(); - void changeEvent(QEvent* event); - void retranslateMenuBar(); + Q_OBJECT + + friend class QSynergyApplication; + friend class SetupWizard; + friend class ActivationDialog; + friend class SettingsDialog; + + public: + enum qSynergyState + { + synergyDisconnected, + synergyConnecting, + synergyConnected, + synergyTransfering + }; + + enum qSynergyType + { + synergyClient, + synergyServer + }; + + enum qLevel { + Error, + Info + }; + + enum qRuningState { + kStarted, + kStopped + }; + + public: + MainWindow(QSettings& settings, AppConfig& appConfig, + SubscriptionManager& subscriptionManager); + ~MainWindow(); + + public: + void setVisible(bool visible); + int synergyType() const { return m_pGroupClient->isChecked() ? synergyClient : synergyServer; } + int synergyState() const { return m_SynergyState; } + QString hostname() const { return m_pLineEditHostname->text(); } + QString configFilename(); + QString address(); + QString appPath(const QString& name); + void open(); + void clearLog(); + VersionChecker& versionChecker() { return m_VersionChecker; } + QString getScreenName(); + ServerConfig& serverConfig() { return m_ServerConfig; } + void showConfigureServer(const QString& message); + void showConfigureServer() { showConfigureServer(""); } + void autoAddScreen(const QString name); + void updateZeroconfService(); + void serverDetected(const QString name); + void updateLocalFingerprint(); + SubscriptionManager& subscriptionManager() const; + + public slots: + void setEdition(Edition edition); + void beginTrial(bool isExpiring); + void endTrial(bool isExpired); + void appendLogRaw(const QString& text); + void appendLogInfo(const QString& text); + void appendLogDebug(const QString& text); + void appendLogError(const QString& text); + void startSynergy(); + + protected slots: + void sslToggled(bool enabled); + void on_m_pGroupClient_toggled(bool on); + void on_m_pGroupServer_toggled(bool on); + bool on_m_pButtonBrowseConfigFile_clicked(); + void on_m_pButtonConfigureServer_clicked(); + bool on_m_pActionSave_triggered(); + void on_m_pActionAbout_triggered(); + void on_m_pActionSettings_triggered(); + void on_m_pActivate_triggered(); + void synergyFinished(int exitCode, QProcess::ExitStatus); + void trayActivated(QSystemTrayIcon::ActivationReason reason); + void stopSynergy(); + void logOutput(); + void logError(); + void updateFound(const QString& version); + void bonjourInstallFinished(); + + protected: + QSettings& settings() { return m_Settings; } + AppConfig& appConfig() { return *m_AppConfig; } + QProcess* synergyProcess() { return m_pSynergy; } + void setSynergyProcess(QProcess* p) { m_pSynergy = p; } + void initConnections(); + void createMenuBar(); + void createStatusBar(); + void createTrayIcon(); + void loadSettings(); + void saveSettings(); + void setIcon(qSynergyState state); + void setSynergyState(qSynergyState state); + bool checkForApp(int which, QString& app); + bool clientArgs(QStringList& args, QString& app); + bool serverArgs(QStringList& args, QString& app); + void setStatus(const QString& status); + void sendIpcMessage(qIpcMessageType type, const char* buffer, bool showErrors); + void onModeChanged(bool startDesktop, bool applyService); + void updateFromLogLine(const QString& line); + QString getIPAddresses(); + void stopService(); + void stopDesktop(); + void changeEvent(QEvent* event); + void retranslateMenuBar(); #if defined(Q_OS_WIN) - bool isServiceRunning(QString name); + bool isServiceRunning(QString name); #else - bool isServiceRunning(); + bool isServiceRunning(); #endif - bool isBonjourRunning(); - void downloadBonjour(); - void promptAutoConfig(); - QString getProfileRootForArg(); - void checkConnected(const QString& line); - void checkFingerprint(const QString& line); - bool autoHide(); - QString getTimeStamp(); - void restartSynergy(); - void proofreadInfo(); - - void showEvent (QShowEvent*); - - private: - QSettings& m_Settings; - AppConfig* m_AppConfig; - SubscriptionManager* m_SubscriptionManager; - QProcess* m_pSynergy; - int m_SynergyState; - ServerConfig m_ServerConfig; - QTemporaryFile* m_pTempConfigFile; - QSystemTrayIcon* m_pTrayIcon; - QMenu* m_pTrayIconMenu; - bool m_AlreadyHidden; - VersionChecker m_VersionChecker; - IpcClient m_IpcClient; - QMenuBar* m_pMenuBar; - QMenu* m_pMenuFile; - QMenu* m_pMenuEdit; - QMenu* m_pMenuWindow; - QMenu* m_pMenuHelp; - ZeroconfService* m_pZeroconfService; - DataDownloader* m_pDataDownloader; - QMessageBox* m_DownloadMessageBox; - QAbstractButton* m_pCancelButton; - QMutex m_UpdateZeroconfMutex; - bool m_SuppressAutoConfigWarning; - CommandProcess* m_BonjourInstall; - bool m_SuppressEmptyServerWarning; - qRuningState m_ExpectedRunningState; - QMutex m_StopDesktopMutex; - SslCertificate* m_pSslCertificate; + bool isBonjourRunning(); + void downloadBonjour(); + void promptAutoConfig(); + QString getProfileRootForArg(); + void checkConnected(const QString& line); + void checkLicense(const QString& line); + void checkFingerprint(const QString& line); + bool autoHide(); + QString getTimeStamp(); + void restartSynergy(); + void proofreadInfo(); + + void showEvent (QShowEvent*); + + private: + QSettings& m_Settings; + AppConfig* m_AppConfig; + SubscriptionManager* m_SubscriptionManager; + QProcess* m_pSynergy; + int m_SynergyState; + ServerConfig m_ServerConfig; + QTemporaryFile* m_pTempConfigFile; + QSystemTrayIcon* m_pTrayIcon; + QMenu* m_pTrayIconMenu; + bool m_AlreadyHidden; + VersionChecker m_VersionChecker; + IpcClient m_IpcClient; + QMenuBar* m_pMenuBar; + QMenu* m_pMenuFile; + QMenu* m_pMenuEdit; + QMenu* m_pMenuWindow; + QMenu* m_pMenuHelp; + ZeroconfService* m_pZeroconfService; + DataDownloader* m_pDataDownloader; + QMessageBox* m_DownloadMessageBox; + QAbstractButton* m_pCancelButton; + QMutex m_UpdateZeroconfMutex; + bool m_SuppressAutoConfigWarning; + CommandProcess* m_BonjourInstall; + bool m_SuppressEmptyServerWarning; + qRuningState m_ExpectedRunningState; + QMutex m_StopDesktopMutex; + SslCertificate* m_pSslCertificate; private slots: - void on_m_pCheckBoxAutoConfig_toggled(bool checked); - void on_m_pComboServerList_currentIndexChanged(QString ); - void on_m_pButtonApply_clicked(); - void installBonjour(); - void on_windowShown(); + void on_m_pCheckBoxAutoConfig_toggled(bool checked); + void on_m_pComboServerList_currentIndexChanged(QString ); + void on_m_pButtonApply_clicked(); + void installBonjour(); + void on_windowShown(); signals: - void windowShown(); + void windowShown(); }; #endif diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index b99e3cf1c..0b1660ea3 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -456,7 +456,7 @@ Server::switchScreen(BaseClientProxy* dst, // if trial is expired, exit the process if (!m_args.m_serial.isExpired(std::time(0))) { - LOG((CLOG_ERR "trial is expired, aborting server")); + LOG((CLOG_ERR "trial has expired, aborting server")); exit(kExitSuccess); } From fc67cdf56ea9d627598b8a343e2624396b9f8a12 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 17 Oct 2016 17:57:14 +0100 Subject: [PATCH 150/241] #5657 Rename SubscriptionManager to LicenseManager --- src/gui/gui.pro | 8 +- src/gui/src/ActivationDialog.cpp | 20 +-- src/gui/src/ActivationDialog.h | 16 +-- src/gui/src/LicenseManager.cpp | 144 +++++++++++++++++++++ .../{SubscriptionManager.h => LicenseManager.h} | 28 ++-- src/gui/src/MainWindow.cpp | 34 ++--- src/gui/src/MainWindow.h | 8 +- src/gui/src/SetupWizard.cpp | 2 +- src/gui/src/SubscriptionManager.cpp | 144 --------------------- src/gui/src/main.cpp | 12 +- 10 files changed, 208 insertions(+), 208 deletions(-) create mode 100644 src/gui/src/LicenseManager.cpp rename src/gui/src/{SubscriptionManager.h => LicenseManager.h} (65%) delete mode 100644 src/gui/src/SubscriptionManager.cpp diff --git a/src/gui/gui.pro b/src/gui/gui.pro index e56926bcb..b60c280d7 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -61,12 +61,12 @@ SOURCES += src/main.cpp \ src/Fingerprint.cpp \ src/SslCertificate.cpp \ src/WebClient.cpp \ - src/SubscriptionManager.cpp \ src/ActivationNotifier.cpp \ src/ActivationDialog.cpp \ src/CancelActivationDialog.cpp \ src/FailedLoginDialog.cpp \ - ../lib/shared/SerialKey.cpp + ../lib/shared/SerialKey.cpp \ + src/LicenseManager.cpp HEADERS += src/MainWindow.h \ src/AboutDialog.h \ src/ServerConfig.h \ @@ -108,14 +108,14 @@ HEADERS += src/MainWindow.h \ src/Fingerprint.h \ src/SslCertificate.h \ src/WebClient.h \ - src/SubscriptionManager.h \ src/ActivationNotifier.h \ src/ElevateMode.h \ src/ActivationDialog.h \ src/CancelActivationDialog.h \ src/FailedLoginDialog.h \ ../lib/shared/EditionType.h \ - ../lib/shared/SerialKey.h + ../lib/shared/SerialKey.h \ + src/LicenseManager.h RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc macx { diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 9e7f5e827..a964fca62 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -7,7 +7,7 @@ #include "ActivationNotifier.h" #include "MainWindow.h" #include "QUtility.h" -#include "SubscriptionManager.h" +#include "LicenseManager.h" #include "FailedLoginDialog.h" #include @@ -15,11 +15,11 @@ #include ActivationDialog::ActivationDialog(QWidget* parent, AppConfig& appConfig, - SubscriptionManager& subscriptionManager) : + LicenseManager& licenseManager) : QDialog(parent), ui(new Ui::ActivationDialog), m_appConfig(&appConfig), - m_subscriptionManager (&subscriptionManager) + m_LicenseManager (&licenseManager) { ui->setupUi(this); refreshSerialKey(); @@ -39,10 +39,10 @@ ActivationDialog::~ActivationDialog() void ActivationDialog::reject() { - if (m_subscriptionManager->activeEdition() == kUnregistered) { + if (m_LicenseManager->activeEdition() == kUnregistered) { CancelActivationDialog cancelActivationDialog(this); if (QDialog::Accepted == cancelActivationDialog.exec()) { - m_subscriptionManager->skipActivation(); + m_LicenseManager->skipActivation(); m_appConfig->activationHasRun(true); m_appConfig->saveSettings(); } @@ -59,7 +59,7 @@ void ActivationDialog::accept() std::pair result; try { QString serialKey = ui->m_pTextEditSerialKey->toPlainText(); - result = m_subscriptionManager->setSerialKey(serialKey); + result = m_LicenseManager->setSerialKey(serialKey); } catch (std::exception& e) { message.critical(this, "Unknown Error", @@ -77,17 +77,17 @@ void ActivationDialog::accept() return; } - Edition edition = m_subscriptionManager->activeEdition(); + Edition edition = m_LicenseManager->activeEdition(); if (edition != kUnregistered) { - if (m_subscriptionManager->serialKey().isTrial()) { + if (m_LicenseManager->serialKey().isTrial()) { message.information(this, "Thanks!", tr("Thanks for trying %1!").arg - (m_subscriptionManager->getEditionName(edition))); + (m_LicenseManager->getEditionName(edition))); } else { message.information(this, "Activated!", tr("Thanks for activating %1!").arg - (m_subscriptionManager->getEditionName(edition))); + (m_LicenseManager->getEditionName(edition))); } } diff --git a/src/gui/src/ActivationDialog.h b/src/gui/src/ActivationDialog.h index b0e9aa942..1cc459330 100644 --- a/src/gui/src/ActivationDialog.h +++ b/src/gui/src/ActivationDialog.h @@ -2,7 +2,7 @@ #define ACTIVATIONDIALOG_H #include -#include +#include namespace Ui { class ActivationDialog; @@ -13,23 +13,23 @@ class AppConfig; class ActivationDialog : public QDialog { Q_OBJECT - + public: - ActivationDialog(QWidget *parent, AppConfig& appConfig, - SubscriptionManager& subscriptionManager); + ActivationDialog(QWidget *parent, AppConfig& appConfig, + LicenseManager& licenseManager); ~ActivationDialog(); public slots: void reject(); void accept(); - + protected: - void refreshSerialKey(); - + void refreshSerialKey(); + private: Ui::ActivationDialog *ui; AppConfig* m_appConfig; - SubscriptionManager* m_subscriptionManager; + LicenseManager* m_LicenseManager; }; #endif // ACTIVATIONDIALOG_H diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp new file mode 100644 index 000000000..53ee76cf8 --- /dev/null +++ b/src/gui/src/LicenseManager.cpp @@ -0,0 +1,144 @@ +/* + * 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 . + */ + +#include "LicenseManager.h" +#include "EditionType.h" +#include "AppConfig.h" +#include +#include +#include +#include + +LicenseManager::LicenseManager(AppConfig* appConfig) : + m_AppConfig(appConfig), + m_serialKey(appConfig->edition()) { + try { + setSerialKey(m_AppConfig->serialKey()); + } catch (...) { + /* Remove garbage serial keys from the registry */ + m_AppConfig->setSerialKey(""); + m_AppConfig->setEdition(kUnregistered); + m_AppConfig->saveSettings(); + } +} + +std::pair +LicenseManager::setSerialKey(QString serialKeyString) +{ + std::pair ret (true, ""); + + SerialKey serialKey (serialKeyString.toStdString()); + if (serialKey.isExpired(::time(0))) { + ret.first = false; + ret.second = "Serial key expired"; + return ret; + } + + if (serialKey != m_serialKey) { + using std::swap; + swap (serialKey, m_serialKey); + + m_AppConfig->setSerialKey (serialKeyString); + notifyActivation ("serial:" + serialKeyString); + emit serialKeyChanged (m_serialKey); + + if (m_serialKey.edition() != serialKey.edition()) { + m_AppConfig->setEdition (m_serialKey.edition()); + emit editionChanged (m_serialKey.edition()); + } + + if (serialKey.isTrial()) { + emit endTrial(false); + } + + if (m_serialKey.isTrial()) { + emit beginTrial(m_serialKey.isExpiring(::time(0))); + } + + m_AppConfig->saveSettings(); + } + + return ret; +} + +Edition +LicenseManager::activeEdition() const +{ + return m_serialKey.edition(); +} + +QString +LicenseManager::activeEditionName() const +{ + return getEditionName(activeEdition(), m_serialKey.isTrial()); +} + +SerialKey +LicenseManager::serialKey() const +{ + return m_serialKey; +} + +void LicenseManager::refresh() const +{ + emit serialKeyChanged (m_serialKey); + emit editionChanged (m_serialKey.edition()); + if (m_serialKey.isTrial()) { + emit beginTrial(m_serialKey.isExpiring(::time(0))); + } +} + +void LicenseManager::skipActivation() +{ + notifyActivation ("skip:unknown"); +} + +QString +LicenseManager::getEditionName(Edition const edition, bool trial) +{ + std::string name ("Synergy "); + switch (edition) { + case kUnregistered: + name += "(UNREGISTERED)"; + return QString::fromUtf8 (name.c_str(), name.size()); + case kBasic: + name += "Basic"; + break; + default: + name += "Pro"; + } + if (trial) { + name += " (Trial)"; + } + return QString::fromUtf8 (name.c_str(), name.size()); +} + +void LicenseManager::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); +} diff --git a/src/gui/src/SubscriptionManager.h b/src/gui/src/LicenseManager.h similarity index 65% rename from src/gui/src/SubscriptionManager.h rename to src/gui/src/LicenseManager.h index 5023ddc2b..edb412af9 100644 --- a/src/gui/src/SubscriptionManager.h +++ b/src/gui/src/LicenseManager.h @@ -24,30 +24,30 @@ class AppConfig; -class SubscriptionManager: public QObject +class LicenseManager: public QObject { - Q_OBJECT + Q_OBJECT public: - SubscriptionManager(AppConfig* appConfig); - std::pair setSerialKey(QString serialKey); - void refresh() const; - Edition activeEdition() const; + LicenseManager(AppConfig* appConfig); + std::pair setSerialKey(QString serialKey); + void refresh() const; + Edition activeEdition() const; QString activeEditionName() const; SerialKey serialKey() const; - void skipActivation(); + void skipActivation(); static QString getEditionName(Edition edition, bool trial = false); private: - void notifyActivation(QString identity); + void notifyActivation(QString identity); private: - AppConfig* m_AppConfig; - SerialKey m_serialKey; + AppConfig* m_AppConfig; + SerialKey m_serialKey; signals: - void serialKeyChanged (SerialKey) const; - void editionChanged (Edition) const; - void beginTrial (bool expiring) const; - void endTrial (bool expired) const; + void serialKeyChanged (SerialKey) const; + void editionChanged (Edition) const; + void beginTrial (bool expiring) const; + void endTrial (bool expired) const; }; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index d88498997..417fcb9f4 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -30,7 +30,7 @@ #include "ZeroconfService.h" #include "DataDownloader.h" #include "CommandProcess.h" -#include "SubscriptionManager.h" +#include "LicenseManager.h" #include "EditionType.h" #include "QUtility.h" #include "ProcessorArch.h" @@ -77,10 +77,10 @@ static const char* synergyIconFiles[] = }; MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig, - SubscriptionManager& subscriptionManager) : + LicenseManager& licenseManager) : m_Settings(settings), m_AppConfig(&appConfig), - m_SubscriptionManager(&subscriptionManager), + m_LicenseManager(&licenseManager), m_pSynergy(NULL), m_SynergyState(synergyDisconnected), m_ServerConfig(&m_Settings, 5, 3, m_AppConfig->screenName(), this), @@ -142,19 +142,19 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig, connect (this, SIGNAL(windowShown()), this, SLOT(on_windowShown()), Qt::QueuedConnection); - connect (m_SubscriptionManager, SIGNAL(editionChanged(Edition)), + connect (m_LicenseManager, SIGNAL(editionChanged(Edition)), this, SLOT(setEdition(Edition)), Qt::QueuedConnection); - connect (m_SubscriptionManager, SIGNAL(beginTrial(bool)), + connect (m_LicenseManager, SIGNAL(beginTrial(bool)), this, SLOT(beginTrial(bool)), Qt::QueuedConnection); - connect (m_SubscriptionManager, SIGNAL(endTrial(bool)), + connect (m_LicenseManager, SIGNAL(endTrial(bool)), this, SLOT(endTrial(bool)), Qt::QueuedConnection); connect (m_AppConfig, SIGNAL(sslToggled(bool)), this, SLOT(sslToggled(bool)), Qt::QueuedConnection); - m_SubscriptionManager->refresh(); + m_LicenseManager->refresh(); } MainWindow::~MainWindow() @@ -447,7 +447,7 @@ void MainWindow::checkConnected(const QString& line) void MainWindow::checkLicense(const QString &line) { if (line.contains("trial has expired")) { - m_SubscriptionManager->refresh(); + m_LicenseManager->refresh(); } } @@ -1049,7 +1049,7 @@ void MainWindow::serverDetected(const QString name) void MainWindow::setEdition(Edition edition) { - setWindowTitle(m_SubscriptionManager->getEditionName (edition)); + setWindowTitle(m_LicenseManager->getEditionName (edition)); if (m_AppConfig->getCryptoEnabled()) { m_pSslCertificate = new SslCertificate(this); m_pSslCertificate->generateCertificate(); @@ -1069,11 +1069,11 @@ void MainWindow::beginTrial(bool isExpiring) " color:#0000ff;\">Buy now!" "

"; expiringNotice = expiringNotice.arg - (m_SubscriptionManager->serialKey().daysLeft(::time(0))); + (m_LicenseManager->serialKey().daysLeft(::time(0))); this->m_trialLabel->setText(expiringNotice); this->m_trialWidget->show(); } - setWindowTitle (m_SubscriptionManager->activeEditionName()); + setWindowTitle (m_LicenseManager->activeEditionName()); } void MainWindow::endTrial(bool isExpired) @@ -1081,7 +1081,7 @@ void MainWindow::endTrial(bool isExpired) if (!isExpired) { this->m_trialWidget->hide(); } - setWindowTitle (m_SubscriptionManager->activeEditionName()); + setWindowTitle (m_LicenseManager->activeEditionName()); } void MainWindow::updateLocalFingerprint() @@ -1097,10 +1097,10 @@ void MainWindow::updateLocalFingerprint() } } -SubscriptionManager& -MainWindow::subscriptionManager() const +LicenseManager& +MainWindow::licenseManager() const { - return *m_SubscriptionManager; + return *m_LicenseManager; } void MainWindow::on_m_pGroupClient_toggled(bool on) @@ -1204,7 +1204,7 @@ void MainWindow::on_m_pButtonConfigureServer_clicked() void MainWindow::on_m_pActivate_triggered() { - ActivationDialog activationDialog(this, appConfig(), subscriptionManager()); + ActivationDialog activationDialog(this, appConfig(), licenseManager()); activationDialog.exec(); } @@ -1423,7 +1423,7 @@ void MainWindow::bonjourInstallFinished() void MainWindow::on_windowShown() { if (!m_AppConfig->activationHasRun() && (m_AppConfig->edition() == kUnregistered)) { - ActivationDialog activationDialog (this, appConfig(), subscriptionManager()); + ActivationDialog activationDialog (this, appConfig(), licenseManager()); activationDialog.exec(); } } diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 27fff56a2..b7fc2ca5b 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -58,7 +58,7 @@ class ZeroconfService; class DataDownloader; class CommandProcess; class SslCertificate; -class SubscriptionManager; +class LicenseManager; class MainWindow : public QMainWindow, public Ui::MainWindowBase { @@ -96,7 +96,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase public: MainWindow(QSettings& settings, AppConfig& appConfig, - SubscriptionManager& subscriptionManager); + LicenseManager& licenseManager); ~MainWindow(); public: @@ -118,7 +118,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase void updateZeroconfService(); void serverDetected(const QString name); void updateLocalFingerprint(); - SubscriptionManager& subscriptionManager() const; + LicenseManager& licenseManager() const; public slots: void setEdition(Edition edition); @@ -195,7 +195,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase private: QSettings& m_Settings; AppConfig* m_AppConfig; - SubscriptionManager* m_SubscriptionManager; + LicenseManager* m_LicenseManager; QProcess* m_pSynergy; int m_SynergyState; ServerConfig m_ServerConfig; diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index b4e6ab3a4..da3ff8c3c 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -19,7 +19,7 @@ #include "MainWindow.h" #include "WebClient.h" #include "ActivationNotifier.h" -#include "SubscriptionManager.h" +#include "LicenseManager.h" #include "EditionType.h" #include "QSynergyApplication.h" #include "QUtility.h" diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp deleted file mode 100644 index b42cc98db..000000000 --- a/src/gui/src/SubscriptionManager.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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 . - */ - -#include "SubscriptionManager.h" -#include "EditionType.h" -#include "AppConfig.h" -#include -#include -#include -#include - -SubscriptionManager::SubscriptionManager(AppConfig* appConfig) : - m_AppConfig(appConfig), - m_serialKey(appConfig->edition()) { - try { - setSerialKey(m_AppConfig->serialKey()); - } catch (...) { - /* Remove garbage serial keys from the registry */ - m_AppConfig->setSerialKey(""); - m_AppConfig->setEdition(kUnregistered); - m_AppConfig->saveSettings(); - } -} - -std::pair -SubscriptionManager::setSerialKey(QString serialKeyString) -{ - std::pair ret (true, ""); - - SerialKey serialKey (serialKeyString.toStdString()); - if (serialKey.isExpired(::time(0))) { - ret.first = false; - ret.second = "Serial key expired"; - return ret; - } - - if (serialKey != m_serialKey) { - using std::swap; - swap (serialKey, m_serialKey); - - m_AppConfig->setSerialKey (serialKeyString); - notifyActivation ("serial:" + serialKeyString); - emit serialKeyChanged (m_serialKey); - - if (m_serialKey.edition() != serialKey.edition()) { - m_AppConfig->setEdition (m_serialKey.edition()); - emit editionChanged (m_serialKey.edition()); - } - - if (serialKey.isTrial()) { - emit endTrial(false); - } - - if (m_serialKey.isTrial()) { - emit beginTrial(m_serialKey.isExpiring(::time(0))); - } - - m_AppConfig->saveSettings(); - } - - return ret; -} - -Edition -SubscriptionManager::activeEdition() const -{ - return m_serialKey.edition(); -} - -QString -SubscriptionManager::activeEditionName() const -{ - return getEditionName(activeEdition(), m_serialKey.isTrial()); -} - -SerialKey -SubscriptionManager::serialKey() const -{ - return m_serialKey; -} - -void SubscriptionManager::refresh() const -{ - emit serialKeyChanged (m_serialKey); - emit editionChanged (m_serialKey.edition()); - if (m_serialKey.isTrial()) { - emit beginTrial(m_serialKey.isExpiring(::time(0))); - } -} - -void SubscriptionManager::skipActivation() -{ - notifyActivation ("skip:unknown"); -} - -QString -SubscriptionManager::getEditionName(Edition const edition, bool trial) -{ - std::string name ("Synergy "); - switch (edition) { - case kUnregistered: - name += "(UNREGISTERED)"; - return QString::fromUtf8 (name.c_str(), name.size()); - case kBasic: - name += "Basic"; - break; - default: - name += "Pro"; - } - if (trial) { - name += " (Trial)"; - } - return QString::fromUtf8 (name.c_str(), name.size()); -} - -void SubscriptionManager::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); -} diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index 18febc8e8..d36db7557 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -2,11 +2,11 @@ * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * 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 @@ -20,7 +20,7 @@ #define TRAY_RETRY_WAIT 2000 #include "QSynergyApplication.h" -#include "SubscriptionManager.h" +#include "LicenseManager.h" #include "MainWindow.h" #include "AppConfig.h" #include "SetupWizard.h" @@ -66,7 +66,7 @@ int main(int argc, char* argv[]) "Please drag Synergy to the Applications folder, and open it from there."); return 1; } - + if (!checkMacAssistiveDevices()) { return 1; @@ -85,11 +85,11 @@ int main(int argc, char* argv[]) QSettings settings; AppConfig appConfig (&settings); qRegisterMetaType("Edition"); - SubscriptionManager subscriptionManager (&appConfig); + LicenseManager licenseManager (&appConfig); app.switchTranslator(appConfig.language()); - MainWindow mainWindow(settings, appConfig, subscriptionManager); + MainWindow mainWindow(settings, appConfig, licenseManager); SetupWizard setupWizard(mainWindow, true); if (appConfig.wizardShouldRun()) From 98610fabde2264767c76f52b310d2e3f7f22056e Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 17 Oct 2016 17:59:01 +0100 Subject: [PATCH 151/241] #5657 Remove unused CoreInterfaces --- src/gui/src/CoreInterface.cpp | 19 ------------------- src/gui/src/CoreInterface.h | 3 --- 2 files changed, 22 deletions(-) diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index b06a5d901..f7e987d70 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -38,12 +38,6 @@ CoreInterface::CoreInterface() { } -QString CoreInterface::getPluginDir() -{ - QStringList args("--get-plugin-dir"); - return run(args); -} - QString CoreInterface::getProfileDir() { QStringList args("--get-profile-dir"); @@ -68,19 +62,6 @@ QString CoreInterface::getSerialKeyFilePath() return filename; } -QString CoreInterface::activateSerial(const QString& serial) -{ - QStringList args("--subscription-serial"); - args << serial; - - return run(args); -} - -QString CoreInterface::checkSubscription() -{ - QStringList args("--check-subscription"); - return run(args); -} QString CoreInterface::notifyActivation(const QString& identity) { diff --git a/src/gui/src/CoreInterface.h b/src/gui/src/CoreInterface.h index cd61ae252..c8a291e21 100644 --- a/src/gui/src/CoreInterface.h +++ b/src/gui/src/CoreInterface.h @@ -24,13 +24,10 @@ class CoreInterface public: CoreInterface(); - QString getPluginDir(); QString getProfileDir(); QString getInstalledDir(); QString getArch(); QString getSerialKeyFilePath(); - QString activateSerial(const QString& serial); - QString checkSubscription(); QString notifyActivation(const QString& identity); QString run(const QStringList& args, const QString& input = ""); }; From 859608424d13972a4536f9e4db83bee7e20a5cb6 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 17 Oct 2016 18:06:29 +0100 Subject: [PATCH 152/241] #5657 Link synergyd against Synergy shared library --- src/cmd/synergyd/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cmd/synergyd/CMakeLists.txt b/src/cmd/synergyd/CMakeLists.txt index 4ee859fca..c8b578a90 100644 --- a/src/cmd/synergyd/CMakeLists.txt +++ b/src/cmd/synergyd/CMakeLists.txt @@ -1,11 +1,11 @@ # synergy -- mouse and keyboard sharing utility # Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 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 @@ -35,7 +35,7 @@ else() endif() target_link_libraries(synergyd - arch base common io ipc mt net platform synergy ${libs} ${OPENSSL_LIBS}) + arch base common io ipc mt net platform synergy shared ${libs} ${OPENSSL_LIBS}) if (WIN32) ADD_CUSTOM_COMMAND( From e65631c45142000769dc51ac05c83ff06bed17f5 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 18 Oct 2016 13:02:36 +0100 Subject: [PATCH 153/241] #5657 Fix wrong logic about checking if serial key expired --- src/lib/server/Server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 0b1660ea3..d3aece30a 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -455,7 +455,7 @@ Server::switchScreen(BaseClientProxy* dst, assert(dst != NULL); // if trial is expired, exit the process - if (!m_args.m_serial.isExpired(std::time(0))) { + if (m_args.m_serial.isExpired(std::time(0))) { LOG((CLOG_ERR "trial has expired, aborting server")); exit(kExitSuccess); } From e48be9099de1cd80568cf15aaf47241f6c4fe743 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 18 Oct 2016 13:05:29 +0100 Subject: [PATCH 154/241] #5657 Remove whitespace for serial key input --- src/gui/src/ActivationDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index a964fca62..2d4d20562 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -58,7 +58,7 @@ void ActivationDialog::accept() std::pair result; try { - QString serialKey = ui->m_pTextEditSerialKey->toPlainText(); + QString serialKey = ui->m_pTextEditSerialKey->toPlainText().trimmed(); result = m_LicenseManager->setSerialKey(serialKey); } catch (std::exception& e) { From 45f37c508ccde13f278805546a991dca02a162ec Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 18 Oct 2016 14:56:48 +0100 Subject: [PATCH 155/241] #5657 Make serial key argument for server only --- src/gui/src/MainWindow.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 417fcb9f4..4866ec26e 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -554,10 +554,6 @@ void MainWindow::startSynergy() args << "--name" << getScreenName(); - if (!appConfig().serialKey().isEmpty()) { - args << "--serial-key" << appConfig().serialKey(); - } - if (desktopMode) { setSynergyProcess(new QProcess(this)); @@ -787,6 +783,10 @@ bool MainWindow::serverArgs(QStringList& args, QString& app) #endif args << "-c" << configFilename << "--address" << address(); + if (!appConfig().serialKey().isEmpty()) { + args << "--serial-key" << appConfig().serialKey(); + } + #if defined(Q_OS_WIN) // pass in physical resolution and primary screen center // TODO: get this information in the core binary even when From 880864a249d1b482f6880d0b35c49ecb71636f27 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 18 Oct 2016 14:57:34 +0100 Subject: [PATCH 156/241] Version to 1.8.5-rc1 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fc5a33114..6d3bf1963 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,8 +17,8 @@ # Version number for Synergy set(VERSION_MAJOR 1) set(VERSION_MINOR 8) -set(VERSION_REV 4) -set(VERSION_STAGE stable) +set(VERSION_REV 5) +set(VERSION_STAGE rc1) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From e5aae66ff70733b42aaaae0a7c77d667a7fb2ced Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 18 Oct 2016 15:13:19 +0100 Subject: [PATCH 157/241] #5657 Don't clear edition when appconfig contains an invalid serial key --- src/gui/src/LicenseManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp index 53ee76cf8..ea9c61f51 100644 --- a/src/gui/src/LicenseManager.cpp +++ b/src/gui/src/LicenseManager.cpp @@ -31,7 +31,6 @@ LicenseManager::LicenseManager(AppConfig* appConfig) : } catch (...) { /* Remove garbage serial keys from the registry */ m_AppConfig->setSerialKey(""); - m_AppConfig->setEdition(kUnregistered); m_AppConfig->saveSettings(); } } From 17961501f640d8af9b36903ae6d64e3f949a4269 Mon Sep 17 00:00:00 2001 From: Josh Harris Date: Tue, 18 Oct 2016 15:26:56 +0100 Subject: [PATCH 158/241] Update ISSUE_TEMPLATE.md Moved the 'Server' and 'Client' fields around --- .github/ISSUE_TEMPLATE.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index fa15485af..590f59a59 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,8 +1,7 @@ ### Operating Systems ### -Client: Applesoft Windy OS 10 - Server: microOS Tiara +Client: Applesoft Windy OS 10 **READ ME, DELETE ME**: On Windows, hold the Windows key and press 'r', type 'winver' and hit return to get your OS version. On Mac, hit the Apple menu (top left of the screen) and check 'About this Mac'. Linux users... you know what you're using ;) From 020b7974dfc7778fb70a4bb63a56f30a06dc4d4c Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 18 Oct 2016 15:32:59 +0100 Subject: [PATCH 159/241] #5657 Fix trial warning label and serial key serialisation --- src/gui/src/MainWindow.cpp | 19 +++++++++++-------- src/lib/shared/SerialKey.cpp | 13 +++++++++---- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 417fcb9f4..7b88d6200 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -1061,15 +1061,18 @@ void MainWindow::setEdition(Edition edition) void MainWindow::beginTrial(bool isExpiring) { if (isExpiring) { - QString expiringNotice = "

%1 days of " - "your Synergy Pro trial remain. " - "Buy now!" - "

"; + QString expiringNotice ("

%1 days of " + "your %2 trial remain. " + "Buy now!" + "

"); expiringNotice = expiringNotice.arg - (m_LicenseManager->serialKey().daysLeft(::time(0))); + (m_LicenseManager->serialKey().daysLeft(::time(0))).arg + (LicenseManager::getEditionName(m_LicenseManager->activeEdition())).arg + (QString::fromStdString(m_LicenseManager->serialKey().toString())); + this->m_trialLabel->setText(expiringNotice); this->m_trialWidget->show(); } diff --git a/src/lib/shared/SerialKey.cpp b/src/lib/shared/SerialKey.cpp index cdc7695f8..60a039cbe 100644 --- a/src/lib/shared/SerialKey.cpp +++ b/src/lib/shared/SerialKey.cpp @@ -126,15 +126,20 @@ std::string SerialKey::toString() const { std::ostringstream oss; - oss << "v2;"; - oss << (isTrial() ? "trial" : "lifetime") << ";"; + oss << "{"; + if (isTrial()) { + oss << "v2;trial;"; + } else { + oss << "v1;"; + } oss << editionString() << ";"; oss << m_name << ";"; oss << m_userLimit << ";"; oss << m_email << ";"; oss << m_company << ";"; - oss << m_warnTime << ";"; - oss << m_expireTime; + oss << (isTrial() ? m_warnTime : 0) << ";"; + oss << (isTrial() ? m_expireTime : 0); + oss << "}"; return hexEncode(oss.str()); } From dfc7c31d67abfd2217c266a6ef693b41f29982e2 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 18 Oct 2016 15:43:15 +0100 Subject: [PATCH 160/241] #5657 Delay auto client adding while activation dialog is shown --- src/gui/src/MainWindow.cpp | 29 ++++++++++++++++++++++++++++- src/gui/src/MainWindow.h | 3 +++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 4866ec26e..b5a445699 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -101,7 +101,8 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig, m_BonjourInstall(NULL), m_SuppressEmptyServerWarning(false), m_ExpectedRunningState(kStopped), - m_pSslCertificate(NULL) + m_pSslCertificate(NULL), + m_ActivationDialogRunning(false) { setupUi(this); @@ -1167,6 +1168,14 @@ void MainWindow::on_m_pActionSettings_triggered() void MainWindow::autoAddScreen(const QString name) { if (!m_ServerConfig.ignoreAutoConfigClient()) { + if (m_ActivationDialogRunning) { + // TODO: refactor this code + // add this screen to the pending list and check this list until + // users finish activation dialog + m_PendingClientNames.append(name); + return; + } + int r = m_ServerConfig.autoAddScreen(name); if (r != kAutoAddScreenOk) { switch (r) { @@ -1205,6 +1214,9 @@ void MainWindow::on_m_pButtonConfigureServer_clicked() void MainWindow::on_m_pActivate_triggered() { ActivationDialog activationDialog(this, appConfig(), licenseManager()); + m_ActivationDialogRunning = true; + connect (&activationDialog, SIGNAL(finished(int)), + this, SLOT(on_activationDialogFinish()), Qt::QueuedConnection); activationDialog.exec(); } @@ -1424,10 +1436,25 @@ void MainWindow::on_windowShown() { if (!m_AppConfig->activationHasRun() && (m_AppConfig->edition() == kUnregistered)) { ActivationDialog activationDialog (this, appConfig(), licenseManager()); + m_ActivationDialogRunning = true; + connect (&activationDialog, SIGNAL(finished(int)), + this, SLOT(on_activationDialogFinish()), Qt::QueuedConnection); activationDialog.exec(); } } +void MainWindow::on_activationDialogFinish() +{ + m_ActivationDialogRunning = false; + if (!m_PendingClientNames.empty()) { + foreach (const QString& name, m_PendingClientNames) { + autoAddScreen(name); + } + + m_PendingClientNames.clear(); + } +} + QString MainWindow::getProfileRootForArg() { CoreInterface coreInterface; diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index b7fc2ca5b..632abec2e 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -221,6 +221,8 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase qRuningState m_ExpectedRunningState; QMutex m_StopDesktopMutex; SslCertificate* m_pSslCertificate; + bool m_ActivationDialogRunning; + QStringList m_PendingClientNames; private slots: void on_m_pCheckBoxAutoConfig_toggled(bool checked); @@ -228,6 +230,7 @@ private slots: void on_m_pButtonApply_clicked(); void installBonjour(); void on_windowShown(); + void on_activationDialogFinish(); signals: void windowShown(); From dc4beba9e915440bbfc2e01f05da60348dcba605 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 18 Oct 2016 16:48:32 +0100 Subject: [PATCH 161/241] #5680 Stop deleting socket twice if it's been adopted by PacketStream --- src/lib/net/IListenSocket.h | 9 +-------- src/lib/net/SecureListenSocket.cpp | 11 ----------- src/lib/net/SecureListenSocket.h | 1 - src/lib/net/TCPListenSocket.h | 1 - src/lib/server/ClientListener.cpp | 10 ---------- src/lib/server/ClientListener.h | 2 -- src/lib/server/Server.cpp | 7 +------ 7 files changed, 2 insertions(+), 39 deletions(-) diff --git a/src/lib/net/IListenSocket.h b/src/lib/net/IListenSocket.h index 905ec831f..2cd6fb1bb 100644 --- a/src/lib/net/IListenSocket.h +++ b/src/lib/net/IListenSocket.h @@ -41,14 +41,7 @@ class IListenSocket : public ISocket { */ virtual IDataSocket* accept() = 0; - - //! Delete connection socket - /*! - This is used when the socket was created but not adopted by a client - proxy. - */ - virtual void deleteSocket(void*) = 0; - + //@} // ISocket overrides diff --git a/src/lib/net/SecureListenSocket.cpp b/src/lib/net/SecureListenSocket.cpp index b4c08646e..533ae41a4 100644 --- a/src/lib/net/SecureListenSocket.cpp +++ b/src/lib/net/SecureListenSocket.cpp @@ -93,14 +93,3 @@ SecureListenSocket::accept() throw ex; } } - -void -SecureListenSocket::deleteSocket(void* socket) -{ - SecureSocketSet::iterator it; - it = m_secureSocketSet.find((IDataSocket*)socket); - if (it != m_secureSocketSet.end()) { - delete *it; - m_secureSocketSet.erase(it); - } -} diff --git a/src/lib/net/SecureListenSocket.h b/src/lib/net/SecureListenSocket.h index ff19602c4..960a8a263 100644 --- a/src/lib/net/SecureListenSocket.h +++ b/src/lib/net/SecureListenSocket.h @@ -33,7 +33,6 @@ class SecureListenSocket : public TCPListenSocket{ // IListenSocket overrides virtual IDataSocket* accept(); - void deleteSocket(void*); private: typedef std::set SecureSocketSet; diff --git a/src/lib/net/TCPListenSocket.h b/src/lib/net/TCPListenSocket.h index cf4469a0d..413086228 100644 --- a/src/lib/net/TCPListenSocket.h +++ b/src/lib/net/TCPListenSocket.h @@ -43,7 +43,6 @@ class TCPListenSocket : public IListenSocket { // IListenSocket overrides virtual IDataSocket* accept(); - virtual void deleteSocket(void*) { } protected: void setListeningJob(); diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index 619ba2a0a..a8bef7cfa 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -106,12 +106,6 @@ ClientListener::setServer(Server* server) m_server = server; } -void -ClientListener::deleteSocket(void* socket) -{ - m_listen->deleteSocket(socket); -} - ClientProxy* ClientListener::getNextClient() { @@ -213,10 +207,6 @@ ClientListener::handleUnknownClient(const Event&, void* vclient) } delete unknownClient; - - if (m_useSecureNetwork && !handshakeOk) { - deleteSocket(socket); - } } void diff --git a/src/lib/server/ClientListener.h b/src/lib/server/ClientListener.h index 7674386ce..545e163a3 100644 --- a/src/lib/server/ClientListener.h +++ b/src/lib/server/ClientListener.h @@ -48,8 +48,6 @@ class ClientListener { //@} - void deleteSocket(void* socket); - //! @name accessors //@{ diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index d9394ed57..30249f1e7 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -1378,10 +1378,7 @@ Server::handleClientDisconnected(const Event&, void* vclient) removeActiveClient(client); removeOldClient(client); - PacketStreamFilter* streamFileter = dynamic_cast(client->getStream()); - TCPSocket* socket = dynamic_cast(streamFileter->getStream()); delete client; - m_clientListener->deleteSocket(socket); } void @@ -1391,10 +1388,8 @@ Server::handleClientCloseTimeout(const Event&, void* vclient) BaseClientProxy* client = static_cast(vclient); LOG((CLOG_NOTE "forced disconnection of client \"%s\"", getName(client).c_str())); removeOldClient(client); - PacketStreamFilter* streamFileter = dynamic_cast(client->getStream()); - TCPSocket* socket = dynamic_cast(streamFileter->getStream()); + delete client; - m_clientListener->deleteSocket(socket); } void From 02c23905d6bc32a264b176ef40a3d4cf353bca25 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 18 Oct 2016 17:01:44 +0100 Subject: [PATCH 162/241] #5657 Handle expired keys properly --- src/gui/src/ActivationDialog.cpp | 5 +++-- src/gui/src/LicenseManager.cpp | 45 +++++++++++++++++----------------------- src/gui/src/LicenseManager.h | 4 ++-- src/gui/src/MainWindow.cpp | 31 ++++++++++++++++++++------- 4 files changed, 47 insertions(+), 38 deletions(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 2d4d20562..4ad3d7b4b 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -81,8 +81,9 @@ void ActivationDialog::accept() if (edition != kUnregistered) { if (m_LicenseManager->serialKey().isTrial()) { message.information(this, "Thanks!", - tr("Thanks for trying %1!").arg - (m_LicenseManager->getEditionName(edition))); + tr("Thanks for trying %1!\n\n%2 days of your trial remain").arg + (m_LicenseManager->getEditionName(edition)).arg + (m_LicenseManager->serialKey().daysLeft(::time(0)))); } else { message.information(this, "Activated!", diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp index ea9c61f51..b82a26487 100644 --- a/src/gui/src/LicenseManager.cpp +++ b/src/gui/src/LicenseManager.cpp @@ -26,22 +26,16 @@ LicenseManager::LicenseManager(AppConfig* appConfig) : m_AppConfig(appConfig), m_serialKey(appConfig->edition()) { - try { - setSerialKey(m_AppConfig->serialKey()); - } catch (...) { - /* Remove garbage serial keys from the registry */ - m_AppConfig->setSerialKey(""); - m_AppConfig->saveSettings(); - } } std::pair -LicenseManager::setSerialKey(QString serialKeyString) +LicenseManager::setSerialKey(QString serialKeyString, bool acceptExpired) { std::pair ret (true, ""); - + time_t currentTime = ::time(0); SerialKey serialKey (serialKeyString.toStdString()); - if (serialKey.isExpired(::time(0))) { + + if (!acceptExpired && serialKey.isExpired(currentTime)) { ret.first = false; ret.second = "Serial key expired"; return ret; @@ -50,22 +44,25 @@ LicenseManager::setSerialKey(QString serialKeyString) if (serialKey != m_serialKey) { using std::swap; swap (serialKey, m_serialKey); - m_AppConfig->setSerialKey (serialKeyString); notifyActivation ("serial:" + serialKeyString); emit serialKeyChanged (m_serialKey); + if (serialKey.isTrial()) { + emit endTrial(false); + } + if (m_serialKey.edition() != serialKey.edition()) { m_AppConfig->setEdition (m_serialKey.edition()); emit editionChanged (m_serialKey.edition()); } - if (serialKey.isTrial()) { - emit endTrial(false); - } - if (m_serialKey.isTrial()) { - emit beginTrial(m_serialKey.isExpiring(::time(0))); + if (m_serialKey.isExpired(currentTime)) { + emit endTrial(true); + } else { + emit beginTrial(m_serialKey.isExpiring(currentTime)); + } } m_AppConfig->saveSettings(); @@ -92,13 +89,9 @@ LicenseManager::serialKey() const return m_serialKey; } -void LicenseManager::refresh() const +void LicenseManager::refresh(bool acceptExpired) { - emit serialKeyChanged (m_serialKey); - emit editionChanged (m_serialKey.edition()); - if (m_serialKey.isTrial()) { - emit beginTrial(m_serialKey.isExpiring(::time(0))); - } + setSerialKey (m_AppConfig->serialKey(), acceptExpired); } void LicenseManager::skipActivation() @@ -109,16 +102,16 @@ void LicenseManager::skipActivation() QString LicenseManager::getEditionName(Edition const edition, bool trial) { - std::string name ("Synergy "); + std::string name ("Synergy"); switch (edition) { case kUnregistered: - name += "(UNREGISTERED)"; + name += " (UNREGISTERED)"; return QString::fromUtf8 (name.c_str(), name.size()); case kBasic: - name += "Basic"; + name += " Basic"; break; default: - name += "Pro"; + name += " Pro"; } if (trial) { name += " (Trial)"; diff --git a/src/gui/src/LicenseManager.h b/src/gui/src/LicenseManager.h index edb412af9..deac8b4fa 100644 --- a/src/gui/src/LicenseManager.h +++ b/src/gui/src/LicenseManager.h @@ -30,8 +30,8 @@ class LicenseManager: public QObject public: LicenseManager(AppConfig* appConfig); - std::pair setSerialKey(QString serialKey); - void refresh() const; + std::pair setSerialKey(QString serialKey, bool acceptExpired = false); + void refresh(bool acceptExpired = false); Edition activeEdition() const; QString activeEditionName() const; SerialKey serialKey() const; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 7b88d6200..e1861ca70 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -154,7 +154,8 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig, connect (m_AppConfig, SIGNAL(sslToggled(bool)), this, SLOT(sslToggled(bool)), Qt::QueuedConnection); - m_LicenseManager->refresh(); + setWindowTitle (m_LicenseManager->activeEditionName()); + m_LicenseManager->refresh(true); } MainWindow::~MainWindow() @@ -1064,15 +1065,14 @@ void MainWindow::beginTrial(bool isExpiring) QString expiringNotice ("

%1 days of " "your %2 trial remain. " + "\"http://symless.com/pricing\">" "Buy now!" "

"); - expiringNotice = expiringNotice.arg - (m_LicenseManager->serialKey().daysLeft(::time(0))).arg - (LicenseManager::getEditionName(m_LicenseManager->activeEdition())).arg - (QString::fromStdString(m_LicenseManager->serialKey().toString())); - + expiringNotice = expiringNotice + .arg (m_LicenseManager->serialKey().daysLeft(::time(0))) + .arg (LicenseManager::getEditionName + (m_LicenseManager->activeEdition())); this->m_trialLabel->setText(expiringNotice); this->m_trialWidget->show(); } @@ -1081,7 +1081,22 @@ void MainWindow::beginTrial(bool isExpiring) void MainWindow::endTrial(bool isExpired) { - if (!isExpired) { + if (isExpired) { + QString expiredNotice ( + "

Your %1 trial has expired. " + "" + "Buy now!

" + ); + expiredNotice = expiredNotice + .arg(LicenseManager::getEditionName + (m_LicenseManager->activeEdition())) + .arg(QString::fromStdString + (m_LicenseManager->serialKey().toString())); + + this->m_trialLabel->setText(expiredNotice); + this->m_trialWidget->show(); + } else { this->m_trialWidget->hide(); } setWindowTitle (m_LicenseManager->activeEditionName()); From 47913e57b8d0f48d2fd84834dcd4f8971789a187 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 18 Oct 2016 18:45:15 +0100 Subject: [PATCH 163/241] #5657 Raise activation dialog when trial expires --- src/gui/src/AppConfig.cpp | 12 ++++++------ src/gui/src/AppConfig.h | 9 ++++----- src/gui/src/MainWindow.cpp | 9 +++++++-- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index cdb26e14b..7fd37f505 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -2,11 +2,11 @@ * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * 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 @@ -153,7 +153,7 @@ void AppConfig::loadSettings() QVariant elevateMode = settings().value("elevateModeEnum"); if (!elevateMode.isValid()) { elevateMode = settings().value ("elevateMode", - QVariant(static_cast(defaultElevateMode))); + QVariant(static_cast(defaultElevateMode))); } m_ElevateMode = static_cast(elevateMode.toInt()); m_AutoConfigPrompted = settings().value("autoConfigPrompted", false).toBool(); @@ -178,8 +178,8 @@ void AppConfig::saveSettings() settings().setValue("language", m_Language); settings().setValue("startedBefore", m_StartedBefore); settings().setValue("autoConfig", m_AutoConfig); - // Refer to enum ElevateMode declaration for insight in to why this - // flag is mapped this way + // Refer to enum ElevateMode declaration for insight in to why this + // flag is mapped this way settings().setValue("elevateMode", m_ElevateMode == ElevateAlways); settings().setValue("elevateModeEnum", static_cast(m_ElevateMode)); settings().setValue("autoConfigPrompted", m_AutoConfigPrompted); @@ -274,7 +274,7 @@ void AppConfig::setCryptoEnabled(bool e) { emit sslToggled(e); } -bool AppConfig::getCryptoEnabled() const { +bool AppConfig::getCryptoEnabled() const { return (edition() == kPro) && m_CryptoEnabled; } diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index b7eacf61d..7aaeeb41e 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -2,11 +2,11 @@ * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) - * + * * 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 @@ -104,9 +104,8 @@ class AppConfig: public QObject bool activationHasRun() const; AppConfig& activationHasRun(bool value); - void saveSettings(); - - protected: + void saveSettings();; +protected: QSettings& settings(); void setScreenName(const QString& s); void setPort(int i); diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index e1861ca70..5becfbb78 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -448,7 +448,7 @@ void MainWindow::checkConnected(const QString& line) void MainWindow::checkLicense(const QString &line) { if (line.contains("trial has expired")) { - m_LicenseManager->refresh(); + m_LicenseManager->refresh(true); } } @@ -1096,6 +1096,8 @@ void MainWindow::endTrial(bool isExpired) this->m_trialLabel->setText(expiredNotice); this->m_trialWidget->show(); + stopSynergy(); + m_AppConfig->activationHasRun(false); } else { this->m_trialWidget->hide(); } @@ -1440,7 +1442,10 @@ void MainWindow::bonjourInstallFinished() void MainWindow::on_windowShown() { - if (!m_AppConfig->activationHasRun() && (m_AppConfig->edition() == kUnregistered)) { + time_t currentTime = ::time(0); + if (!m_AppConfig->activationHasRun() + && ((m_AppConfig->edition() == kUnregistered) || + (m_LicenseManager->serialKey().isExpired(currentTime)))) { ActivationDialog activationDialog (this, appConfig(), licenseManager()); activationDialog.exec(); } From f441c24a23ef7b447851d70ed480dd568e0a8cea Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 19 Oct 2016 11:36:48 +0100 Subject: [PATCH 164/241] #5657 Minor activation UI tweaks --- src/gui/res/ActivationDialog.ui | 17 ++--------------- src/gui/res/CancelActivationDialog.ui | 2 +- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/gui/res/ActivationDialog.ui b/src/gui/res/ActivationDialog.ui index 0a980eb2c..1d66c4543 100644 --- a/src/gui/res/ActivationDialog.ui +++ b/src/gui/res/ActivationDialog.ui @@ -6,8 +6,8 @@ 0 0 - 440 - 214 + 410 + 211
@@ -54,19 +54,6 @@ p, li { white-space: pre-wrap; }
- - - - Qt::Vertical - - - - 20 - 40 - - - - diff --git a/src/gui/res/CancelActivationDialog.ui b/src/gui/res/CancelActivationDialog.ui index b98733f6e..054b3fe6f 100644 --- a/src/gui/res/CancelActivationDialog.ui +++ b/src/gui/res/CancelActivationDialog.ui @@ -19,7 +19,7 @@ Are you sure? -If you don't activate Synergy you'll be missing out on some great features +If you don't activate Synergy you'll be missing out on some great features.
true From e01d0ce4c70cc6c577333fc0ecdc59ae71f8e6e5 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 19 Oct 2016 16:01:15 +0100 Subject: [PATCH 165/241] #5657 Raise activation dialog when key expires --- src/gui/res/ActivationDialog.ui | 55 +++++++++++++++++++++++++++++++++++++++- src/gui/src/ActivationDialog.cpp | 4 +++ src/gui/src/LicenseManager.cpp | 14 +++++----- src/gui/src/MainWindow.cpp | 54 ++++++++++++++++++++++++--------------- src/gui/src/MainWindow.h | 5 ++-- 5 files changed, 103 insertions(+), 29 deletions(-) diff --git a/src/gui/res/ActivationDialog.ui b/src/gui/res/ActivationDialog.ui index 1d66c4543..1425ee0c7 100644 --- a/src/gui/res/ActivationDialog.ui +++ b/src/gui/res/ActivationDialog.ui @@ -54,6 +54,57 @@ p, li { white-space: pre-wrap; } + + + + + 2 + + + 0 + + + 0 + + + 8 + + + + + + + + :/res/icons/16x16/money.png + + + + + + + <html><head/><body><p>Your trial has expired. <a href="http://symless.com/pricing?src=gui"><span style=" text-decoration: underline; color:#0000ff;">Buy now!</span></a></p></body></html> + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + @@ -66,7 +117,9 @@ p, li { white-space: pre-wrap; } - + + + buttonBox diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 4ad3d7b4b..3f1cab9fe 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -23,6 +23,10 @@ ActivationDialog::ActivationDialog(QWidget* parent, AppConfig& appConfig, { ui->setupUi(this); refreshSerialKey(); + time_t currentTime = ::time(0); + if (!m_LicenseManager->serialKey().isExpired(currentTime)) { + ui->m_trialWidget->hide(); + } } void ActivationDialog::refreshSerialKey() diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp index b82a26487..d3a865e17 100644 --- a/src/gui/src/LicenseManager.cpp +++ b/src/gui/src/LicenseManager.cpp @@ -44,17 +44,17 @@ LicenseManager::setSerialKey(QString serialKeyString, bool acceptExpired) if (serialKey != m_serialKey) { using std::swap; swap (serialKey, m_serialKey); - m_AppConfig->setSerialKey (serialKeyString); - notifyActivation ("serial:" + serialKeyString); - emit serialKeyChanged (m_serialKey); + m_AppConfig->setSerialKey(serialKeyString); + notifyActivation("serial:" + serialKeyString); + emit serialKeyChanged(m_serialKey); if (serialKey.isTrial()) { emit endTrial(false); } if (m_serialKey.edition() != serialKey.edition()) { - m_AppConfig->setEdition (m_serialKey.edition()); - emit editionChanged (m_serialKey.edition()); + m_AppConfig->setEdition(m_serialKey.edition()); + emit editionChanged(m_serialKey.edition()); } if (m_serialKey.isTrial()) { @@ -91,7 +91,9 @@ LicenseManager::serialKey() const void LicenseManager::refresh(bool acceptExpired) { - setSerialKey (m_AppConfig->serialKey(), acceptExpired); + if (!m_AppConfig->serialKey().isEmpty()) { + setSerialKey(m_AppConfig->serialKey(), acceptExpired); + } } void LicenseManager::skipActivation() diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 4efc88ce3..10308d2ba 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -420,8 +420,10 @@ void MainWindow::appendLogRaw(const QString& text) void MainWindow::updateFromLogLine(const QString &line) { + // TODO: this code makes Andrew cry checkConnected(line); checkFingerprint(line); + checkLicense(line); } void MainWindow::checkConnected(const QString& line) @@ -449,7 +451,7 @@ void MainWindow::checkConnected(const QString& line) void MainWindow::checkLicense(const QString &line) { if (line.contains("trial has expired")) { - m_LicenseManager->refresh(true); + raiseActivationDialog(); } } @@ -541,6 +543,14 @@ void MainWindow::clearLog() void MainWindow::startSynergy() { + SerialKey serialKey = m_LicenseManager->serialKey(); + time_t currentTime = ::time(0); + if (serialKey.isExpired(currentTime)) { + if (QDialog::Rejected == raiseActivationDialog()) { + return; + } + } + bool desktopMode = appConfig().processMode() == Desktop; bool serviceMode = appConfig().processMode() == Service; @@ -1233,11 +1243,7 @@ void MainWindow::on_m_pButtonConfigureServer_clicked() void MainWindow::on_m_pActivate_triggered() { - ActivationDialog activationDialog(this, appConfig(), licenseManager()); - m_ActivationDialogRunning = true; - connect (&activationDialog, SIGNAL(finished(int)), - this, SLOT(on_activationDialogFinish()), Qt::QueuedConnection); - activationDialog.exec(); + raiseActivationDialog(); } void MainWindow::on_m_pButtonApply_clicked() @@ -1452,22 +1458,16 @@ void MainWindow::bonjourInstallFinished() m_pCheckBoxAutoConfig->setChecked(true); } -void MainWindow::on_windowShown() +int MainWindow::raiseActivationDialog() { - time_t currentTime = ::time(0); - if (!m_AppConfig->activationHasRun() - && ((m_AppConfig->edition() == kUnregistered) || - (m_LicenseManager->serialKey().isExpired(currentTime)))) { - ActivationDialog activationDialog (this, appConfig(), licenseManager()); - m_ActivationDialogRunning = true; - connect (&activationDialog, SIGNAL(finished(int)), - this, SLOT(on_activationDialogFinish()), Qt::QueuedConnection); - activationDialog.exec(); + if (m_ActivationDialogRunning) { + return QDialog::Rejected; } -} - -void MainWindow::on_activationDialogFinish() -{ + ActivationDialog activationDialog (this, appConfig(), licenseManager()); + m_ActivationDialogRunning = true; + connect (&activationDialog, SIGNAL(finished(int)), + this, SLOT(on_activationDialogFinish()), Qt::QueuedConnection); + int result = activationDialog.exec(); m_ActivationDialogRunning = false; if (!m_PendingClientNames.empty()) { foreach (const QString& name, m_PendingClientNames) { @@ -1476,6 +1476,20 @@ void MainWindow::on_activationDialogFinish() m_PendingClientNames.clear(); } + if (result == QDialog::Accepted) { + restartSynergy(); + } + return result; +} + +void MainWindow::on_windowShown() +{ + time_t currentTime = ::time(0); + if (!m_AppConfig->activationHasRun() + && ((m_AppConfig->edition() == kUnregistered) || + (m_LicenseManager->serialKey().isExpired(currentTime)))) { + raiseActivationDialog(); + } } QString MainWindow::getProfileRootForArg() diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 632abec2e..2ca3e711b 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -120,7 +120,9 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase void updateLocalFingerprint(); LicenseManager& licenseManager() const; - public slots: + int raiseActivationDialog(); + +public slots: void setEdition(Edition edition); void beginTrial(bool isExpiring); void endTrial(bool isExpired); @@ -230,7 +232,6 @@ private slots: void on_m_pButtonApply_clicked(); void installBonjour(); void on_windowShown(); - void on_activationDialogFinish(); signals: void windowShown(); From f2a1d962bc40ddd5b33104ad0baa7b3ecde11ddb Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 19 Oct 2016 17:40:34 +0100 Subject: [PATCH 166/241] #5657 Fix skip activation loop --- src/gui/src/ActivationDialog.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 3f1cab9fe..94fbe1a02 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -49,6 +49,8 @@ void ActivationDialog::reject() m_LicenseManager->skipActivation(); m_appConfig->activationHasRun(true); m_appConfig->saveSettings(); + } else { + return; } } QDialog::reject(); From ae590907a8d18a6536822faa8d3d7bc83b3da2f3 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 19 Oct 2016 17:50:44 +0100 Subject: [PATCH 167/241] #5657 Remind users to activate all devices if they might be using SSL --- src/gui/src/ActivationDialog.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 94fbe1a02..4f167cd48 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -85,11 +85,20 @@ void ActivationDialog::accept() Edition edition = m_LicenseManager->activeEdition(); if (edition != kUnregistered) { + QString thanksMessage = tr("Thanks for trying %1! %3\n\n%2 days of " + "your trial remain"). + arg (m_LicenseManager->getEditionName(edition)). + arg (m_LicenseManager->serialKey().daysLeft(::time(0))); + + if (edition == kPro) { + thanksMessage = thanksMessage.arg("If you're using SSL, " + "remember to activate all of your devices."); + } else { + thanksMessage = thanksMessage.arg(""); + } + if (m_LicenseManager->serialKey().isTrial()) { - message.information(this, "Thanks!", - tr("Thanks for trying %1!\n\n%2 days of your trial remain").arg - (m_LicenseManager->getEditionName(edition)).arg - (m_LicenseManager->serialKey().daysLeft(::time(0)))); + message.information(this, "Thanks!", thanksMessage); } else { message.information(this, "Activated!", From 868887155dea2ec479f0304cd608ce305e7a4a0c Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 20 Oct 2016 11:30:02 +0100 Subject: [PATCH 168/241] #5657 Update buy now links --- src/gui/src/ActivationDialog.cpp | 7 +++++++ src/gui/src/MainWindow.cpp | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 4f167cd48..26392d987 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -34,6 +34,13 @@ 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("

Your trial has " + "expired. Buy now!" + "

") + .arg (m_appConfig->serialKey())); } ActivationDialog::~ActivationDialog() diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 10308d2ba..1178edd93 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -1095,7 +1095,7 @@ void MainWindow::endTrial(bool isExpired) if (isExpired) { QString expiredNotice ( "

Your %1 trial has expired. " + "\"https://symless.com/synergy/trial/thanks?id=%2\">" "" "Buy now!

" ); From bdf55460587f9ab77113cfc0a2c8d628b8bf843e Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 20 Oct 2016 14:02:01 +0100 Subject: [PATCH 169/241] Revert "#5640 About dialog tweaks" This reverts commit 9837c982cd2ff0ec89b9683eaabd6657868f858a. --- src/gui/res/AboutDialogBase.ui | 87 +++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/src/gui/res/AboutDialogBase.ui b/src/gui/res/AboutDialogBase.ui index 01df3dd95..52d8330fe 100644 --- a/src/gui/res/AboutDialogBase.ui +++ b/src/gui/res/AboutDialogBase.ui @@ -13,19 +13,25 @@ 0 0 450 - 378 + 350 - + 0 0 + + + 450 + 350 + + 450 - 378 + 350 @@ -35,20 +41,48 @@ true - - + + - + 0 0 - + + <p> +Keyboard and mouse sharing application. Cross platform, open source, and totally awesome.<br /><br /> +Copyright © 2012-2016 Symless Ltd.<br /> +Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br /> +Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> +The Synergy GUI is based on QSynergy by Volker Lanz.<br /><br /> +Visit our website for help and info (symless.com). +</p> +Synergy is released under the GNU General Public License (GPLv2).<br /><br /> + + + 1 + + + + + + + Qt::Vertical + + + QSizePolicy::Preferred + + - 450 - 16777215 + 20 + 100 + + + + @@ -163,41 +197,6 @@ - - - - - 0 - 0 - - - - <html><head/><body><p>Keyboard and mouse sharing application. <br/><br/>Copyright © 2012-2016 Symless Ltd.<br/>Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.</p><p>Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br/>The Synergy GUI is based on QSynergy by Volker Lanz. </p><p>Synergy is released under the GNU General Public License (GPLv2).</p></body></html> - - - false - - - 1 - - - - - - - Qt::Vertical - - - QSizePolicy::Preferred - - - - 20 - 100 - - - - From 3048ca5fc6505099b7e234a235d6f296bd877614 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 20 Oct 2016 14:02:34 +0100 Subject: [PATCH 170/241] Revert "#5640 About dialog tweaks" This reverts commit 03b8788660af08290f74cc4812ac32f29932010c. --- src/gui/res/AboutDialogBase.ui | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gui/res/AboutDialogBase.ui b/src/gui/res/AboutDialogBase.ui index 52d8330fe..ba18e3091 100644 --- a/src/gui/res/AboutDialogBase.ui +++ b/src/gui/res/AboutDialogBase.ui @@ -13,7 +13,7 @@ 0 0 450 - 350 + 300 @@ -25,13 +25,13 @@ 450 - 350 + 300 450 - 350 + 300 @@ -51,14 +51,14 @@ <p> -Keyboard and mouse sharing application. Cross platform, open source, and totally awesome.<br /><br /> +Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> Copyright © 2012-2016 Symless Ltd.<br /> -Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br /> +Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br /><br /> +Synergy is released under the GNU General Public License (GPLv2).<br /><br /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> The Synergy GUI is based on QSynergy by Volker Lanz.<br /><br /> Visit our website for help and info (symless.com). -</p> -Synergy is released under the GNU General Public License (GPLv2).<br /><br /> +</p>
1 From e17130f0603eb799738c525be6c8c05f835506c0 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 20 Oct 2016 14:02:47 +0100 Subject: [PATCH 171/241] Revert "#5640 Update icon to the new hotness" This reverts commit 833c73f1bdb4ade0b89b9ca93da61db26654a548. --- res/synergy.ico | Bin 24277 -> 287934 bytes src/gui/res/icons/256x256/synergy.ico | Bin 24277 -> 287934 bytes src/gui/res/image/about.png | Bin 5701 -> 4941 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/res/synergy.ico b/res/synergy.ico index 9e3d57100883a631db42ac5c9ef323228f84b43f..fc2e41468ec60a88e0da4194f288a18572f3f36f 100644 GIT binary patch literal 287934 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 literal 24277 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 z zH?5NIoBip&L_&|b*DxVFf@S$eVPE(C7V9q_(|+h#nxWBdIBB()R5@vxNtmXmDuiCM z`@?%R+1kY6vgSZ?Ocd^;d0#8^iU*sz>Kuu*bMrFIv)p7*FF;_cJoHEokhhiQL;Vy- z#CBh@Ol@*}+FD3+0!z_gm$zNnuH>RO>N{u^ku6iwxeCFC)W1${>cG`&S-E`?3c*Wx zchxFd#>jxg`j2WAiq(#@JRI+aeRwI4L$*je7CcJPVv8&<(=!q79(L@Y$=5<2TP+FP z#A$azOw0|pY|~J2pRr5|L-y!agV`iv_bp9Nia5NWINU#vG0C5Dxo8Yz<#~2yN2Rtt znfq0ccVKe9L+D*R!iiX~UhsuI)pf%VSDy2lLDQ~hiokUvmOk9_b(2iZMm`q4=LYQlgPe6@g z1Ke|1*fvF}gCA63aQWPb7;Z*#(VtyLet&&BH$d%)nLc(uS$)Nmm|>lCR$OMQ;r-V$eDFM`7v^4KTirTjb6_K{@rF? z17tI9+x^C$NWSqhRx_@tS;Ksnw&px9V<~qW8Z0K5pg7{RT?L{gq*M>F{UE`;&pDXI z1uDU-iTmnQ`X*#l5XEzJowgThUIb+kaz~QmOy@y_Y2^)sn@f0sw?R)nYOJ?mr+}9? z>zPcgiR?#*vj=z6B0yNr>HFbAKEi+YR0}II)CK+-&CMr=Tr((7OqNTz&xmuuWx&K3 zSTraVI`b$wjkcE(>flT5K*Av@UUqDIRWu0p+zsK{fNbhub0z)vREx24O*koB)62R@ zFY0=qL(W)#5bOxW=M)KY#{8yIx9_1tsES0(He75&*5~>xtO^0+ z5cF9A!VJlBhBlj0-3NTFoe=fK^)=?4kFnmE0_A)Z&137Dljxer`!~%ZZ4`;wkH3?| zt{70sd)0VgN54L!d#Ep^9jzfb+=OaTLOtdRG_tiXX7$>w%61v zUp`Ut3VNwL)jkL4-UKQNLCtFo8j?Q&Mi#?#SwY(GYj`e)^?w9 z_XJF3-oBhKRSnw)BaLlf(U6nvKBD@%SUUyT{g(@cl=z)|Rnq3H7I_?zy7%CFPI@N; zY=7cVUJeuWuEjLS5yoy6=4fyQ=!top8`;r~TeH3Rsc9N{GC zolf0&Pt`q``QrzWLXX<4R-_8sVtO(7N?%pT3cZD~w&9XweYHX>vY#BKaNp*wz?CSE zzhtGBlv6~wgZxgoDrt-W&g-5iml<)C_SW8+dBKLPaVNlyj>YuPk)wqF1F9nFxMZL5 z8Xx&fvm%qbAxq1u?&5TA#tBcux%PzzSZY!{lry6@(|{6eO93XWoX}c z&M%>4gP%nthdzDmOY>K3Bceab9+(Bnt7fs2 z7pShrytk|btKj5z5Lk7xpo!TXE1!BjO(!!St$SQHi4D#a?oGLupDA&wUCSn+s$YI; zS|_k7j3MfwSqwUG4w37H6=UB%+TKW0Pt3qU_LAHDz>;?wKFAfL#Wu3yWh zWYz0Iop|x;iN8KW`G{9S()Paa^at}VeQHlH(Ll)8O#jG%=^km};R)FbYjhoBnkuFotjd$Q!o&0HtMOtjZgd^;rSCQV zyuYxjZCS<_N4b_;3ccuT{nN~xGDPx3OlAmuQvN8}OB1^}L9J?Z-aZojwH1AHQ%FpQ zONG<<>HP(-tC=}p*B7X83J|{g99?w2&UruuoV#+Oc|S*XHh+sz`1k9>zu%~MyDPFS zmy#8``z$HZ&J#{p-1#Orm^B6H>iW2e9KbKKkUJ-@8=CM)Zr`#*PM}Jarlo4#T_mLD z1`U+w3ip`LjdytQW3P{fcsy9q?A>gBOl4Y)1SZbTZbYlZ8Ix(ECKr1*hYu|S4Rg9Z z+NvW0cPW#55nr7~bA!UyS7-RdzC%aBNJ5>WHtZItxscxthh>nc@{o;9r*ychj?f@p zVFVY_+8B4(gl4ibxxeCox17{TcDO6lEl4zFeu&o3@9H!y()mWo)yqkidZTL3Oq<0M zHbZyWl_{p`C;me3Nwc(UM~anMh4*&B_;lkz^NB=f(Lag3;mQS_2&G}SKSsdjXB8QZ zqx;10Erhg@jS3=dZ-a|6GlQd64Fe)mymHwF%MbjhXNF*O(oO-ZiPydIqaRw|dkIBU zrs$3xXRcS#&a8CaR*%Tc)2uq+Zd{b*p-OU*(*9L0Be6KIhwtW;vNn~n!iwVQ$Hu=k zXd~$8T4g46c!%qsAm%lA;Gr1dN7m)hSDgeS#p_zdpPB9WkD>r90`6Yp-qAj^n@6bd zxQfj0=eX@Qgf_(;#%c3niL*0A0v!x!ittVU10?bBnp%P%H;Tw2BlW(z z91$-~S|lo~Jph)ZQ1jvCEp&{d#pj6cVhlU!cGnod1stBock(Nq!L)AY{ClI)^wfpj zn5Jt&5rjFuBkN`6J2LNweygyAR*im$Axd)>OBajg;?mU!lcfWw^hHI_O%cRI@f#?L zgpeFV3tPI)`&HM>Y_hNU^y;E>U~!2?h9~3XVsYTDFlbeU*Zm(tGd8qHy7UxFx5ojT zswbDdA=Y0TZj5{6l#c^bUXa(^11|Nhr@*R|*<)52|MJ|`q`rIKF0A%vVj`n;NWzJK z|6ELkmR?-+s>9wewxo1CRSq-bVLF>obJBgxamZ~s-qOpS49<7DJdBnOrfqD0myX{8 z!OA3V!%Sfj=m$qB2Dv;mtycUTgk#PJ*+Ow5GpKO<=H*ywa`=ikr?FnN4eqcOIVJp! zqr%ZX0p$2S^6=+8+JLAAx$d2*Ji(0?k&xATnS{#j;BaMvx*}6k!CC1MoqAiMN}u9# zBqRrQH)CaRru~H#(i<#Q;AXRyPt-6`{0B^O^dq8-GmHzT;ug_8E6}shHZnS?-ZnJ& z1kur$HAur+M98yxs1|0J_9$@Tg;}wE-%+kR26HQBg zphEHntF9roA)CO`H|DIMf92}`seCG8$C1&y^gmA3{|VL~eZJ#a{1UFvL;U}D34=bw zAmD#o!a9y6A3qzA0{_P)y!M| z5e^=r(#0B^5V}!IJxLTT4sFWHNE=+{K* zF-8d}$N>v5=*u#axH88eXP~YhLCJZbnN~DBM(h;)!~wwELo1TNJb|Z@@jBrTo+6`A zYCuSU30Qu?bI!(P8VP6FJ8J&P_#h@I6BrT27{_>-nb zQ=*5dDHZz-EFgt)@pGt0V=|!8FQQIYI!y3)X*<@yuQK2}%evyc{_FrUumKqGfbk?1 zkw~5C&NCsLuf=24kFk<{3@%{X(2(w z3f?b7u(ZiO2rx=hmLkrH#ZM%Cshr;Pu-?eqZ8Rg~PAi2# z`?fS(k7PoRkqTVr_Fv5l4Beiu30!Iq0i3m6eTA8tm=~db*R) zE1++o^sLda7hWqiHs(b7Hx;2tzn|GB=;q-kOhib+y(&Kgm#3piRS?=>gDpS*B?%)Pomm>NeOOv?ccrVIlX+lUUpAe;*s< z*NSFYrmob2oB%B+)ZV$C&P~?|i&`N9m_&&P`&8?YpLo*~K#Iv6!bOtP_{z7m&CY`a&cu;2f7Zq~D*hZ~ORm zXHyh=EAI__oCX>7I9q(9H8uPCVS8SOsgUvTxj@_F{O@p{+_PK?)a>cMu)i)|8hA~* zUk}b_w_fbe77Y*!c>Lr86FV9O{!Nxi(QGa_KrWdNvMpr*xRsQp8`N;!CP<5h@K&W= zvkXW#t7ShL<8i$yh6lv9M^FLC5XBjGh8>ZhO9NeDhrkn|E%9L#G}{>xX6Y!RDR;~i z9CkEx8bJWuGPY&}Q!`B2Fd}Z?bsWUQ6S5j~B2pTY63F zNrDr21Z}n#eKZEA8!d73iqc@TiQeh8!qmyeZkP05f#<7t=_e8EohUcH8&q(XpCpl< z3F+pi=Fw25>7R6>6Xx68o~EV=&hpa7)O-<)ikg6uPjjxzQ^l2FEt)Fni;`=Wf^p3X z1kfu+EdkGmRDjTaH!zYFJxz0jAoMg=M^?E^^d5kZN-FjCj`VB)4f4yzKiaJ_Od&*1 z6VomiTXFG%E_4S5dN~02?vl`7FiHQ8>7N03lkr8TLfQAOIl{QH@JaJfT*z|yPPUnw zt&{SFYv$zv5k4g^9rNP*Wy(Icm5cKNq+`7{6nMR*!u*NsnG7KtXQ`=|y_3bV;SbsJ z{kY%;&D~NiU-`ZkJMV+obcY`KFJ@LK$E0XxgaWP)uX_0S=&u=+Mu+wZxIw&*JJ!-OH87ZkkAGQL59Pm$*wbKnKH!}{ z)D5xOTvs2!4x*?%tm|}lmjJxgzY&%nBl*^4rKVz-I^~uiPkH7a!uv8l(9Nv5HeSCr~Fntyk|ON zv7RP=Hk*BEf(km=xA+Jlj8PMg(H%NwtK?uGqY0rTegl`4S$l`bHmQE%Mq+kW6b*v- zAT==-HMq}lKYkZN{^!p%K$klMV2;Tg@T*f+mQL#&UDwrkKoMdvg$g58{`jW#po069 z`c5>Oyr z?5iaIdB_nU**fHT4s2yw8UygNmxaO5)xjlm?u77VVc22H8D zCc#ae8w{nuu6ikGYqX?=nh7E25|PTCx9+t6(%2;PT!iiDQ=FDK&_S*t=pg^8V`89j zxkDl7d~aA?5n+kc0f+eF9l)KYt*yW#;u=;Df6M^p7&@~3#{K8-2#GTn4sF_r#`NG? za63ZS9w-B0vt*=|97+B>Hj!2Nzw9f)*ricnu3Wf@zJH$&(RkI(p9{MWyhRS@D+|Y& zAEST09PXdO>OQ&Ew7>}2!9S8ifI|0mya%HCvVUELo-m#T>|zU9S%uC^EhPKf7oIT2 zM);;{As@tT%1z&g+{o?p(GCGA6|kn;rx zekk@ve*=-@*+04A=UIgo1j?#atyeDX7{3mBROLEYGtumEtO5+GQ5)n zI!KIlKu3&#K41e^gIEB=hjy$WSFU9p^>gip4mH=9+1?{*>2y_Z3OqY<89UitAps%m zF&#Q6pOWyP@oJvIH%dO0jf~4of=@Wz)8ct`{ zIRS=BK;2ojuuZgi#cIz1@%;1O|1`HQu!$E(n+Ne}E34sH}t#cZGtBAGw>pPnT8`#=p zKQ7 ze?Z8J-gHP+m`B3Sb$0x{aVIFi0>iT}L{eNFvqdopL&Eudb!fTWIqJXHiyj-3hQgO; zrIj)}DvY+axwEddP|Pot;Jh{f?A!D6%4NBmfCIC;TB;H@jtQ@U*bY@!wDP0W)KrM= z)(}6swbaMhw%)?%RCk%#&$U08v!aIuACK`k9F|VdC6E50%Gelg9ce%&vtD2UqfNmV zFz9A?V}IKI{54K#vVXAb{4837u(%7NA$OgT+m`inP`%9f$891J>ii$4S2NIh z#E>ch9GU+p$g@*$9YNfY>wktZzEChEe% zsc20MpkI`H(r^u?x?`L>Lex6nSPLiVH+p3JMSK{q(d^*@R>Da>l~VtWDOvb8Vza2) zjhC}xY7Wl@vC&a(aJZ5yARbaUsgpdgNtOFGDiv;#io?MxmV%CP$UR(Kpq^R|rUoFD z9v|EJ-E6bc)J>US3>$ERLwf7mNQC!dLK|(!W%gDeJX@_sYj`Nz>f+eHhAGudFCdXjXURTn?Rtr60KkM^ZUR6Hu8OBVVXW_VwAZF5jDVh?I)Db zio)rJj!w-FxtRhfiMTZGNeEXQY_DFKP^f_M&2-S8)2zffK`cHLNf6%qMH(U~U?9cZ zcV`>1Ax#n}eY|2UsH(9qXTC86x)L*~;EX~dgTK}8#9#r)O%lMONUa?^x`N|&IHV>@ za9>QO*yY;Uz-SXbT0U@+lu4!F7c+0O-Jiqli{b&oJEuKH-PZQ+)W4oCLxDY>t>h;y zhB#rSeZcXnhJ6S*Ve~cVVdj8(r6)ar?kSoj0W@B9PnM%#$Ro+ z-T|6!*a;yD@3Rt!|JqIqGXZWq^iM1?#A9Ak7l}oo4oCl@8C{?U7(fVzd@h(4LCj~! zdno=|KtR<7tTpO|y9W5JcsSNs!4-|_!3r@<59XS1i5DWl`@;;2HaKOpfHrvXpO6DI zA4j@{$99q@hz*}weuTMOAtp~iFbYtMRp>(5ZLj8F1LJDh(1_jl<`-kzyd&qs6S)C| zLj)Pn2I(iHZ$_le@>W&~W*P`mAbTD{2vOeOi^-IQqvp{OC0`MxMw@W4iLhACNPGT* zc>E7^I=#{x3{Gfip*OYFS2cR-`Z!MiaN_tt793cbRm5cWkwCqad7AAt`kv8jLi}fGz+M~(-BXP8nnp)zF!2SD zBWHWqcS^XT`M_<1C6*9cfP=Oy;)vIj=%%G(0s>O-rtTaG3&eNOm&g(>>aKUUtKlB%PYAv7 zmMJLmv%5b%>jFwdRIErZ)!i%7ef-4_Bmai+*`(mpxNKGgQ8uNQcz=n0^WW&Sdu0YH zBbg~c&NptJycXI+BFIX6hU&c=Bhzk?f*&1YFnbW7Y}kdEL-k58Le*IXcm8!VHQJt{19s1q>`|`fL@8@@8 zpR@N}YwdOSn&`5BvO?kc3!rCyBMw4jlB+6O6pE@Uxa2D-E_k)A0zQp$Xf(JYh?M=R zQUWHOJEh}4MqX}2$Q1b8pMZ^Q-ydbZ9JV}ChogwT6VaL(n_rqG@L}uf)2=C#S!Ur8!@OM0|0qHm))U8rDwS5mR7JEWWlLtM2dX zLx%bt)M4MY>h`ulFj>U|A!0b>@w(g1Y;C~9v48IGq#qBPM`W-lH}J#{s3}Hb=;e2o zLWWZ{!xoUkI7n5+M$g5^FPbs=S#KLvcxZ=CMS$@MGHc}HWBagD62*?USIR~^XKc=9 zJhIU??wus0y^cd6jXr^|(s#ypMuA#?=o6?B1 zHb5PzLYW`uoR1;Wu=}`&@}xn?EJ%0Fej1*1w~!?L`w5^N;loEHepUs;tSrS$kWeFP z*TqqL?Ej*U6lph{BmoGX@TZR^V58TEADQf&y@KsSRl2xjqft;!EI!)Ews71!`-wt+ z^@%-i&UBA?XA2-898(0~_X6o+sG9puYMV&dOuLA z8F+Rdxrc*PWV)Tni%%T4bJV*#Gr7Kg4fKFX$8^stLvru@kTG3AI4h@I{}l$R@yn*l zmr6pa-4GZw+lydq^7>PimUgA%Pr~(*>WHvWG%%KDqz#B_yJQ&>vwJ1h1-a133yAdZg>{o3xo$!|jxtHq0H3U$%!Iz}pVcOn?(a28lMl z`C(}mh_iT3!!ZW1b+EP;S~VGm^EVJyDOjD~=f?@@EoL3ET$EjQ$9N4JFXlcb_S;h<6j+&tkA zc-Ls6z7SHcpCXUOSb>OB{-->YCC2iOF>@8Z1_ExDN`2OVwZfN&(Xv4%!*w^;pyXkc zC(#rNRg}DJ3Zo30JsQ4PcrD8FG_eix>j-w4a+U@(m*h*Pm$?HGAAUby=sR&|d9bKh7e=+xNt!yX(r6neaG^A(rvKP6F}K-f^Y5l^a5zW?Jf) zpJadSPYcL8L-|(XppTqw;Z$Bk@YhL>X^lT0?rX&CfRvkrXu}kX)xZC6W%I*ZPj}u2 z?nFO@4>UUS$h_Gg&Op$~aEPb7($lu48uSqZf@5cQzd(L`*|ARMbuWG=3<%v&%d`3< zDwd+h-Fugsid@2c2wj)x72>`cZaDvdmyc`<)K~tOvl!NkjUyxwk*J3bcsAfZH2_3N zf_}D>DwulvW#~dtmbvq?G736p=>Zgun(ZuG#kf_x%u; zV&-5GvkOq_YxBcbYZFarP#mwj$8abmkT*O|Bq2qBM;3M4Q_sgC7!r7H7)nL=rP;s_j|NW-O{^!t_2FW52;!7ta!1Y`ZI~u-H9|yxQvJg zn)U_r9sSdhA}LMEYa!yrAH~UZw`k|xG;=ii_rV0FGw0^|L2fh36zpMC8gpx`s~hD9cDcKJx$ve>;-mYF6j#={P` zodtl96@3ZYX@I!@N!HaLdRuAM(X zh*}dwTVfC1cNIc~J;n}qUDA#q@1*&{M>{eeqxpdjN<~lLkM}X{uSb-B`^q9jkGSm; z#;7g-T(>v-77a5P(;0A?xe$fZA*W(AWhG@ly)2asy2CXKJDy)sMSq~`Lj5{}PEa$S zIBm|p<9tv9hQXE**GKoZv$gCoT3{I~X9;;eyp2fT-r}TFq|iyM3Qrwb^2W(iIu|GY zjCUi7mf%A&uO>%x+zT7ENur$dhiQM+pmXR;twQ->*PXDEdWfwm(1+;^h43h%q_s zq}W?%qKaXDeCFm#{5Cqn{J7|7$|deE7m;mYO<6K|xlHSuz}I=bA{mKQz^Uk(7b7x_ zV#*Y}2H)ZLXWien#;gKaMc2FFE435d5zS-;;VoG0KW`?I|jCKoGgdcdwx4Mc?JJ6L;g@6ccdz{ zw}j|M>s41au_vXE8h`Ge>xp+`)GKb!8||$ode}NG_@SJMi5_@A#uLfUhl;R_ds=JN zsxdM6RT1hjUxCT&+^BHNY}pIx-qzFRe$Bmj3hRL%=&8vmFC1hpo||ny$Uy4i{h~gS z=3NN;C{Pmgm&U24EEVB7h9jhDzPKi)k}#Dn>XG$Rjy`3~&oGh`|FV5DYpbakZB96n z=B-lTgpU>>5X8lueYX$Cl{$}HP1xyD#NkV-Yd78SRlEQzk^kL0T(kyPl_H{}Rs4Tu z9+oT!sxTiaY%UxaB`S|l%i{(k&4R2`|e zz%XfJ)!)^U8{F9#UQHqjC{jGVL?pS{eTBIIocnVxdIs#NpU$-6T@pVgDfkS;@f zE0<@RR7P9Uh=kl?r9?A4R`Agz$(yHLTY2l4&Z*yiQIZ}Y;ZXC(;}QYKn;eiK_by%Y zP1u~mCJN6&e{VL;O$j(?7F^!6xVQmaJ5Uui5H;nd1`5B118gJ!Zs|qo@N?l-_0A3w zy{Od0n&XhOmNx!ibHLX+^p_>=3Du7J0z=lw%iZ#yejwh9Z9px)=oSPn<7#f1k-Fjd zBCBV+eq}JNN49R9R26@23pqR_LJT##1Of6W2$GNbrT>1#Z;Q6LXLocH&S^pPz`gP> zSliNHGln9Jf-|07CmVb#D6Xc44HC-+VnQ`RlsXvKPXN3LC7YABZ`AIrSI^D+;*DP0 z_4X1`Su%#%;-cr}g;AOhP05x%q~0}{29d54#w-B!6-#Yl;FgWI3A-3g^r-3I8;=07 ztru$g6(P}SzGu+80s&csjr+h>&2Ke~!`%X}FCS0s{Yv96?bk^x+cM(dc3|FCdLq^( z3*i;)htB)j1B^9iApRu<8e+jgXyvW}W0Pe@Gi9+LnQNRrpa?URvv1aw3kVYg$o>xT z0V`T`w5)jFpU+r*Vf&L3)&?P32+i$NM~hgt9eoD0*sl6?fy%ID9Y%G+khft482Y3y zaO%AMT$TYkf+I$7kL#oDU>OUj*S*krlUFj&VV*(Zi?T>I@&?wdUxzYHPM zu}3)yi=+LD!v_B5=J-4-*7qsDX0Z%Bn%fNEKKSRy*-L_49*_tnJnX$%FnF`NExjQ# zN?sn*8*??+h8x-fG7Rghxzev!C1;rk$?IhU5_LkHigA#ewMBAq3F8H8fmBHN_r>Ck z+sff?KaVGx@-NK!CggrEDKZOh+rY>%of6w+ls6H2PqZ5WVM}HMfQUK=1as3V`1i{K!ypPc{w~jv*7HM$DB^ zGfbbMu9l_hajX&yturQ-9NAyJ3L=#xYt%Z?;W}MEF}O)Wm@P1O{0E*Y1VO)G96I!zd1MPhgUB5Ikc+z<55+4ud-);? zCHj#kBe_$25)b~^pIqU}chG=MudoO~irD+m8i8LgG=2SedWb79BO3RXfoGmZA!05Y zFx~HC0}zK7TNtKp#@k*l$nCK%TW4cQBtlT-p2q%GN`#(2ky`5BRu3J&7++Lu$7RVA zI4wa}niU>|A=xVOGww1+|65Ym^>w>#y@`M&hmbodn)cg=6sYIFD%{9@g_L-K&)$2z zF>K~k12JE%g6)suIIQJ1n_y;7P7IeP(3c?Gr=#A&w^iutytNOOaJm$+w9K{UcOV!g ze-mTzY-J1*SN5K0>!A0uOIlKG1jSHl)L+~@*nydCAGz{vU&@|k z>Bai7du$$*>21=Nl|xn-ekXhck=s!f09>7FiW7r>)0!V;3~;3U7y_;9%KljZ^BnYn zf{`rgE|=a$hJOS^k7**Tz?Z;J834$I|MLQ9!B38J=PB6k$|k@<2Rg}0w9$L^+eCUL z3fDKLuvr{<7-lROe!~pPG1CQpwqHUWi?VMuc(kFG^)hs23S*O0Wx=^RLU=y$N)bfg zHBiTppA4Amf~09R+?okXh2}yGj|Lh>TdnaZy`(@>&8RW_=Z<;blKnKPqQkRIVq`*$ zVB59zkCkVfB&MeQQFCpur;zH)!P{nnD5}${z4R+Rlh1C+0Cri0H}udd#CD?NoGqn! ztjlYR0Y+ENs1oxY5L+eZ?&1ZZvV)FVJ zRRRB22-tuUT+{$rS$dH!N2kEg8do9edM;3ZqmCA8qc+_3r2lT}Py3~5uuGg8DzTz& zS9Aqn^U0)nLzEk~(`kT5&)z5ZQO$mf6O6L18ub}E%Pm%QHtvrS7p3pIV}MyaSg|m4 zyFMxZ88lMZvn9JR4x~WOytPhoqdiAo)e*OI#=#zb;^O+bAI7px9hig>=o?hkuk=wT zT-vMS;)oE@K(FFXtAsIApK(lITiP{k1y}gd+pMzD{A3eLNTutv|Q%8?d;3TPp9pm1oL13QR$1B<`^q^Rw zZPXHiMhZ(N`cI61l*RJ@yth?j;Rf%9u*0~+%)pfGeB))P6M8Odi5rH%^&6btHSFM0HIJ#W!bZ)rz;B0V+ra4RCtcy*5 zIG@COG@28caV%(3xfWEl-nqYbNO5;U>0ap-@@a81uS4+_Vic@jXsd*M@uNKeDtY+> z7CMyj`!n$*--1##kKjSE4&x#V`x4n+z}8WUaEptJMoE5rmI%Sh_zU9gPP_<$vf`AD z!Zc01wH{1+0ZU`1QQv=`l0T={`G5ksp5a1}R-ch+OTY%b6mx}63%5Jv#8p9z8@%`4 z9u_uB&DjeHvB-UXjS;W zfbippzx&oqJ((=(afAIy215-O-m0hUh-CZijL7^gLtPd*?fIkkY0RI}%Cp!3&Q1xvp? z?c<1&g(4ZU4pMGOsXRdCS&JUvLk6-G+!##OlW{OW@6N_O1Uv9rm^C}fRlI3!V3xg# z`}c%cYw+%lcw=;o8U#SZ780}w>ok9YaIVYv<2@z}#LWm~a;yHDT6Hc^Kas9b zO2PBuM79C{HMrV2`q}t=o`JnHLP~Xf0HB>8JQL58kGjzGmIwi|M4h^ax*7~n@;>)o zbiBJ~T`{QUNdGN_d=Dk{U2^E|CcXOK`FDZVKsk;b-Mq^luo6bkgbyv(c@Xc5juSj4Prui z*Hka5m<||%&Hn58)q>+=EVTPoZ3I>Gwn%-*fE$t_QfcBz3b~r1tlnKo0)0BbYW;72 z*fDlHaK_1#_b9j=DyadvF4@|PAi_R(*%Of3tP=IF z0zb4SX~}db8Bi~_%nW&D6J)KI2P$37(>fWhBp0LwYH5&Dz5zpZPR<2Qm7jR|i2`Ra zR!_^QQoPrKk8!On0I>ed={jBD0h^6YMy%G=-NLO8rx>>XmYZ*uywZi2bvz(UgH$BG z(APH@A%7`h9R^BCpCoYj`yPXJ3dc>t%w&D`-595~Lc%!zUY*h{H$npTQVmAn<3Bem zEHqBO{=~=!Ar}Q+g008cII=Zul zw}AiHq)ku^H(0?7&bo!hS^bERO)!{oabHX)pU==lF3TWY4j+L0ba(m!OHW`hI%(rw zBlhOQa+p<*M|w7Dhi?h38W} zId0VO+NzQ6+Aj|C2B24GA`81qm+15ma8}_r&g=eW-}&ixe2KdTsgDxKawB> zZeOY@n3_ZX`PS6m3<3Is&%Pa9e3JI>F+2_lNjP3VK}5~eF7?U8zKHE(Y$9Soz-(rC2;%&cbsfi5`YXp}nJb zklW1eo!tx1=>CbyB{Elyytg!w^V4d;usbAo;CPvMglhAOq3QsmR+a$D>-3VwEvK1S z=&lfFO5$!_kj)E^F+VWe>a%7kD@i}W>r1Oh?oT8JtUN{+DuN1enyR=*F3i&EC3zD) z`>&^A6JNez4R5ucngcq8Fv}+8W*G^}l!b);8$8w7{<12u*`<5s!$?Acqx8z6tWVj! znlfN--R;}IEL3#tUc7}>{a*ok$?-{>*93C+D^Sa*oLdy!Y9Oa;ZpC2LMd7Z!I(t8b z)kN*jCU0S8^0pIBRZej#Rq6$hazE z%!X4xGK~%LTMyAShhTYoW#sW=LLezI_z|h9*o`cy?M&_5w40ON)-(5`2M7P{HAMmP z+sx92418K<8gmT^N6Ytd@PC<`H!owWJ|9?y=xC`-sNOGuP7B#@J2TvB7CF)6%2iey zFWyp}tjg)qwLSm}$jD*$!*LE7g*QQ?`E?=&o}oyCpv@9 zT18oBYoa|&fA&vb?7q-9MzRD6fq#U#W^cfE>U@UaA^FA|?5o^n23txLiKs zut&bV`^~?$cmsd$ErbytO%n6YlAzm@s6gJ&=tZ(9ANOVrs~xZj6i^V;1`|R|2R zVBMV@-1ccP(5Koo2zY({-G(Y_Np;{MNs2Wfs0^%<;fh;5ETldNP5!v~H-SxkB+6CZwfNv(2{^J)L{R6pxDIN0sR<<%+=!!0qm2$4pI`l&Z zww7xBlC6^!%kllu0!Gvy-y` diff --git a/src/gui/res/icons/256x256/synergy.ico b/src/gui/res/icons/256x256/synergy.ico index 9e3d57100883a631db42ac5c9ef323228f84b43f..fc2e41468ec60a88e0da4194f288a18572f3f36f 100644 GIT binary patch literal 287934 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 literal 24277 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 z zH?5NIoBip&L_&|b*DxVFf@S$eVPE(C7V9q_(|+h#nxWBdIBB()R5@vxNtmXmDuiCM z`@?%R+1kY6vgSZ?Ocd^;d0#8^iU*sz>Kuu*bMrFIv)p7*FF;_cJoHEokhhiQL;Vy- z#CBh@Ol@*}+FD3+0!z_gm$zNnuH>RO>N{u^ku6iwxeCFC)W1${>cG`&S-E`?3c*Wx zchxFd#>jxg`j2WAiq(#@JRI+aeRwI4L$*je7CcJPVv8&<(=!q79(L@Y$=5<2TP+FP z#A$azOw0|pY|~J2pRr5|L-y!agV`iv_bp9Nia5NWINU#vG0C5Dxo8Yz<#~2yN2Rtt znfq0ccVKe9L+D*R!iiX~UhsuI)pf%VSDy2lLDQ~hiokUvmOk9_b(2iZMm`q4=LYQlgPe6@g z1Ke|1*fvF}gCA63aQWPb7;Z*#(VtyLet&&BH$d%)nLc(uS$)Nmm|>lCR$OMQ;r-V$eDFM`7v^4KTirTjb6_K{@rF? z17tI9+x^C$NWSqhRx_@tS;Ksnw&px9V<~qW8Z0K5pg7{RT?L{gq*M>F{UE`;&pDXI z1uDU-iTmnQ`X*#l5XEzJowgThUIb+kaz~QmOy@y_Y2^)sn@f0sw?R)nYOJ?mr+}9? z>zPcgiR?#*vj=z6B0yNr>HFbAKEi+YR0}II)CK+-&CMr=Tr((7OqNTz&xmuuWx&K3 zSTraVI`b$wjkcE(>flT5K*Av@UUqDIRWu0p+zsK{fNbhub0z)vREx24O*koB)62R@ zFY0=qL(W)#5bOxW=M)KY#{8yIx9_1tsES0(He75&*5~>xtO^0+ z5cF9A!VJlBhBlj0-3NTFoe=fK^)=?4kFnmE0_A)Z&137Dljxer`!~%ZZ4`;wkH3?| zt{70sd)0VgN54L!d#Ep^9jzfb+=OaTLOtdRG_tiXX7$>w%61v zUp`Ut3VNwL)jkL4-UKQNLCtFo8j?Q&Mi#?#SwY(GYj`e)^?w9 z_XJF3-oBhKRSnw)BaLlf(U6nvKBD@%SUUyT{g(@cl=z)|Rnq3H7I_?zy7%CFPI@N; zY=7cVUJeuWuEjLS5yoy6=4fyQ=!top8`;r~TeH3Rsc9N{GC zolf0&Pt`q``QrzWLXX<4R-_8sVtO(7N?%pT3cZD~w&9XweYHX>vY#BKaNp*wz?CSE zzhtGBlv6~wgZxgoDrt-W&g-5iml<)C_SW8+dBKLPaVNlyj>YuPk)wqF1F9nFxMZL5 z8Xx&fvm%qbAxq1u?&5TA#tBcux%PzzSZY!{lry6@(|{6eO93XWoX}c z&M%>4gP%nthdzDmOY>K3Bceab9+(Bnt7fs2 z7pShrytk|btKj5z5Lk7xpo!TXE1!BjO(!!St$SQHi4D#a?oGLupDA&wUCSn+s$YI; zS|_k7j3MfwSqwUG4w37H6=UB%+TKW0Pt3qU_LAHDz>;?wKFAfL#Wu3yWh zWYz0Iop|x;iN8KW`G{9S()Paa^at}VeQHlH(Ll)8O#jG%=^km};R)FbYjhoBnkuFotjd$Q!o&0HtMOtjZgd^;rSCQV zyuYxjZCS<_N4b_;3ccuT{nN~xGDPx3OlAmuQvN8}OB1^}L9J?Z-aZojwH1AHQ%FpQ zONG<<>HP(-tC=}p*B7X83J|{g99?w2&UruuoV#+Oc|S*XHh+sz`1k9>zu%~MyDPFS zmy#8``z$HZ&J#{p-1#Orm^B6H>iW2e9KbKKkUJ-@8=CM)Zr`#*PM}Jarlo4#T_mLD z1`U+w3ip`LjdytQW3P{fcsy9q?A>gBOl4Y)1SZbTZbYlZ8Ix(ECKr1*hYu|S4Rg9Z z+NvW0cPW#55nr7~bA!UyS7-RdzC%aBNJ5>WHtZItxscxthh>nc@{o;9r*ychj?f@p zVFVY_+8B4(gl4ibxxeCox17{TcDO6lEl4zFeu&o3@9H!y()mWo)yqkidZTL3Oq<0M zHbZyWl_{p`C;me3Nwc(UM~anMh4*&B_;lkz^NB=f(Lag3;mQS_2&G}SKSsdjXB8QZ zqx;10Erhg@jS3=dZ-a|6GlQd64Fe)mymHwF%MbjhXNF*O(oO-ZiPydIqaRw|dkIBU zrs$3xXRcS#&a8CaR*%Tc)2uq+Zd{b*p-OU*(*9L0Be6KIhwtW;vNn~n!iwVQ$Hu=k zXd~$8T4g46c!%qsAm%lA;Gr1dN7m)hSDgeS#p_zdpPB9WkD>r90`6Yp-qAj^n@6bd zxQfj0=eX@Qgf_(;#%c3niL*0A0v!x!ittVU10?bBnp%P%H;Tw2BlW(z z91$-~S|lo~Jph)ZQ1jvCEp&{d#pj6cVhlU!cGnod1stBock(Nq!L)AY{ClI)^wfpj zn5Jt&5rjFuBkN`6J2LNweygyAR*im$Axd)>OBajg;?mU!lcfWw^hHI_O%cRI@f#?L zgpeFV3tPI)`&HM>Y_hNU^y;E>U~!2?h9~3XVsYTDFlbeU*Zm(tGd8qHy7UxFx5ojT zswbDdA=Y0TZj5{6l#c^bUXa(^11|Nhr@*R|*<)52|MJ|`q`rIKF0A%vVj`n;NWzJK z|6ELkmR?-+s>9wewxo1CRSq-bVLF>obJBgxamZ~s-qOpS49<7DJdBnOrfqD0myX{8 z!OA3V!%Sfj=m$qB2Dv;mtycUTgk#PJ*+Ow5GpKO<=H*ywa`=ikr?FnN4eqcOIVJp! zqr%ZX0p$2S^6=+8+JLAAx$d2*Ji(0?k&xATnS{#j;BaMvx*}6k!CC1MoqAiMN}u9# zBqRrQH)CaRru~H#(i<#Q;AXRyPt-6`{0B^O^dq8-GmHzT;ug_8E6}shHZnS?-ZnJ& z1kur$HAur+M98yxs1|0J_9$@Tg;}wE-%+kR26HQBg zphEHntF9roA)CO`H|DIMf92}`seCG8$C1&y^gmA3{|VL~eZJ#a{1UFvL;U}D34=bw zAmD#o!a9y6A3qzA0{_P)y!M| z5e^=r(#0B^5V}!IJxLTT4sFWHNE=+{K* zF-8d}$N>v5=*u#axH88eXP~YhLCJZbnN~DBM(h;)!~wwELo1TNJb|Z@@jBrTo+6`A zYCuSU30Qu?bI!(P8VP6FJ8J&P_#h@I6BrT27{_>-nb zQ=*5dDHZz-EFgt)@pGt0V=|!8FQQIYI!y3)X*<@yuQK2}%evyc{_FrUumKqGfbk?1 zkw~5C&NCsLuf=24kFk<{3@%{X(2(w z3f?b7u(ZiO2rx=hmLkrH#ZM%Cshr;Pu-?eqZ8Rg~PAi2# z`?fS(k7PoRkqTVr_Fv5l4Beiu30!Iq0i3m6eTA8tm=~db*R) zE1++o^sLda7hWqiHs(b7Hx;2tzn|GB=;q-kOhib+y(&Kgm#3piRS?=>gDpS*B?%)Pomm>NeOOv?ccrVIlX+lUUpAe;*s< z*NSFYrmob2oB%B+)ZV$C&P~?|i&`N9m_&&P`&8?YpLo*~K#Iv6!bOtP_{z7m&CY`a&cu;2f7Zq~D*hZ~ORm zXHyh=EAI__oCX>7I9q(9H8uPCVS8SOsgUvTxj@_F{O@p{+_PK?)a>cMu)i)|8hA~* zUk}b_w_fbe77Y*!c>Lr86FV9O{!Nxi(QGa_KrWdNvMpr*xRsQp8`N;!CP<5h@K&W= zvkXW#t7ShL<8i$yh6lv9M^FLC5XBjGh8>ZhO9NeDhrkn|E%9L#G}{>xX6Y!RDR;~i z9CkEx8bJWuGPY&}Q!`B2Fd}Z?bsWUQ6S5j~B2pTY63F zNrDr21Z}n#eKZEA8!d73iqc@TiQeh8!qmyeZkP05f#<7t=_e8EohUcH8&q(XpCpl< z3F+pi=Fw25>7R6>6Xx68o~EV=&hpa7)O-<)ikg6uPjjxzQ^l2FEt)Fni;`=Wf^p3X z1kfu+EdkGmRDjTaH!zYFJxz0jAoMg=M^?E^^d5kZN-FjCj`VB)4f4yzKiaJ_Od&*1 z6VomiTXFG%E_4S5dN~02?vl`7FiHQ8>7N03lkr8TLfQAOIl{QH@JaJfT*z|yPPUnw zt&{SFYv$zv5k4g^9rNP*Wy(Icm5cKNq+`7{6nMR*!u*NsnG7KtXQ`=|y_3bV;SbsJ z{kY%;&D~NiU-`ZkJMV+obcY`KFJ@LK$E0XxgaWP)uX_0S=&u=+Mu+wZxIw&*JJ!-OH87ZkkAGQL59Pm$*wbKnKH!}{ z)D5xOTvs2!4x*?%tm|}lmjJxgzY&%nBl*^4rKVz-I^~uiPkH7a!uv8l(9Nv5HeSCr~Fntyk|ON zv7RP=Hk*BEf(km=xA+Jlj8PMg(H%NwtK?uGqY0rTegl`4S$l`bHmQE%Mq+kW6b*v- zAT==-HMq}lKYkZN{^!p%K$klMV2;Tg@T*f+mQL#&UDwrkKoMdvg$g58{`jW#po069 z`c5>Oyr z?5iaIdB_nU**fHT4s2yw8UygNmxaO5)xjlm?u77VVc22H8D zCc#ae8w{nuu6ikGYqX?=nh7E25|PTCx9+t6(%2;PT!iiDQ=FDK&_S*t=pg^8V`89j zxkDl7d~aA?5n+kc0f+eF9l)KYt*yW#;u=;Df6M^p7&@~3#{K8-2#GTn4sF_r#`NG? za63ZS9w-B0vt*=|97+B>Hj!2Nzw9f)*ricnu3Wf@zJH$&(RkI(p9{MWyhRS@D+|Y& zAEST09PXdO>OQ&Ew7>}2!9S8ifI|0mya%HCvVUELo-m#T>|zU9S%uC^EhPKf7oIT2 zM);;{As@tT%1z&g+{o?p(GCGA6|kn;rx zekk@ve*=-@*+04A=UIgo1j?#atyeDX7{3mBROLEYGtumEtO5+GQ5)n zI!KIlKu3&#K41e^gIEB=hjy$WSFU9p^>gip4mH=9+1?{*>2y_Z3OqY<89UitAps%m zF&#Q6pOWyP@oJvIH%dO0jf~4of=@Wz)8ct`{ zIRS=BK;2ojuuZgi#cIz1@%;1O|1`HQu!$E(n+Ne}E34sH}t#cZGtBAGw>pPnT8`#=p zKQ7 ze?Z8J-gHP+m`B3Sb$0x{aVIFi0>iT}L{eNFvqdopL&Eudb!fTWIqJXHiyj-3hQgO; zrIj)}DvY+axwEddP|Pot;Jh{f?A!D6%4NBmfCIC;TB;H@jtQ@U*bY@!wDP0W)KrM= z)(}6swbaMhw%)?%RCk%#&$U08v!aIuACK`k9F|VdC6E50%Gelg9ce%&vtD2UqfNmV zFz9A?V}IKI{54K#vVXAb{4837u(%7NA$OgT+m`inP`%9f$891J>ii$4S2NIh z#E>ch9GU+p$g@*$9YNfY>wktZzEChEe% zsc20MpkI`H(r^u?x?`L>Lex6nSPLiVH+p3JMSK{q(d^*@R>Da>l~VtWDOvb8Vza2) zjhC}xY7Wl@vC&a(aJZ5yARbaUsgpdgNtOFGDiv;#io?MxmV%CP$UR(Kpq^R|rUoFD z9v|EJ-E6bc)J>US3>$ERLwf7mNQC!dLK|(!W%gDeJX@_sYj`Nz>f+eHhAGudFCdXjXURTn?Rtr60KkM^ZUR6Hu8OBVVXW_VwAZF5jDVh?I)Db zio)rJj!w-FxtRhfiMTZGNeEXQY_DFKP^f_M&2-S8)2zffK`cHLNf6%qMH(U~U?9cZ zcV`>1Ax#n}eY|2UsH(9qXTC86x)L*~;EX~dgTK}8#9#r)O%lMONUa?^x`N|&IHV>@ za9>QO*yY;Uz-SXbT0U@+lu4!F7c+0O-Jiqli{b&oJEuKH-PZQ+)W4oCLxDY>t>h;y zhB#rSeZcXnhJ6S*Ve~cVVdj8(r6)ar?kSoj0W@B9PnM%#$Ro+ z-T|6!*a;yD@3Rt!|JqIqGXZWq^iM1?#A9Ak7l}oo4oCl@8C{?U7(fVzd@h(4LCj~! zdno=|KtR<7tTpO|y9W5JcsSNs!4-|_!3r@<59XS1i5DWl`@;;2HaKOpfHrvXpO6DI zA4j@{$99q@hz*}weuTMOAtp~iFbYtMRp>(5ZLj8F1LJDh(1_jl<`-kzyd&qs6S)C| zLj)Pn2I(iHZ$_le@>W&~W*P`mAbTD{2vOeOi^-IQqvp{OC0`MxMw@W4iLhACNPGT* zc>E7^I=#{x3{Gfip*OYFS2cR-`Z!MiaN_tt793cbRm5cWkwCqad7AAt`kv8jLi}fGz+M~(-BXP8nnp)zF!2SD zBWHWqcS^XT`M_<1C6*9cfP=Oy;)vIj=%%G(0s>O-rtTaG3&eNOm&g(>>aKUUtKlB%PYAv7 zmMJLmv%5b%>jFwdRIErZ)!i%7ef-4_Bmai+*`(mpxNKGgQ8uNQcz=n0^WW&Sdu0YH zBbg~c&NptJycXI+BFIX6hU&c=Bhzk?f*&1YFnbW7Y}kdEL-k58Le*IXcm8!VHQJt{19s1q>`|`fL@8@@8 zpR@N}YwdOSn&`5BvO?kc3!rCyBMw4jlB+6O6pE@Uxa2D-E_k)A0zQp$Xf(JYh?M=R zQUWHOJEh}4MqX}2$Q1b8pMZ^Q-ydbZ9JV}ChogwT6VaL(n_rqG@L}uf)2=C#S!Ur8!@OM0|0qHm))U8rDwS5mR7JEWWlLtM2dX zLx%bt)M4MY>h`ulFj>U|A!0b>@w(g1Y;C~9v48IGq#qBPM`W-lH}J#{s3}Hb=;e2o zLWWZ{!xoUkI7n5+M$g5^FPbs=S#KLvcxZ=CMS$@MGHc}HWBagD62*?USIR~^XKc=9 zJhIU??wus0y^cd6jXr^|(s#ypMuA#?=o6?B1 zHb5PzLYW`uoR1;Wu=}`&@}xn?EJ%0Fej1*1w~!?L`w5^N;loEHepUs;tSrS$kWeFP z*TqqL?Ej*U6lph{BmoGX@TZR^V58TEADQf&y@KsSRl2xjqft;!EI!)Ews71!`-wt+ z^@%-i&UBA?XA2-898(0~_X6o+sG9puYMV&dOuLA z8F+Rdxrc*PWV)Tni%%T4bJV*#Gr7Kg4fKFX$8^stLvru@kTG3AI4h@I{}l$R@yn*l zmr6pa-4GZw+lydq^7>PimUgA%Pr~(*>WHvWG%%KDqz#B_yJQ&>vwJ1h1-a133yAdZg>{o3xo$!|jxtHq0H3U$%!Iz}pVcOn?(a28lMl z`C(}mh_iT3!!ZW1b+EP;S~VGm^EVJyDOjD~=f?@@EoL3ET$EjQ$9N4JFXlcb_S;h<6j+&tkA zc-Ls6z7SHcpCXUOSb>OB{-->YCC2iOF>@8Z1_ExDN`2OVwZfN&(Xv4%!*w^;pyXkc zC(#rNRg}DJ3Zo30JsQ4PcrD8FG_eix>j-w4a+U@(m*h*Pm$?HGAAUby=sR&|d9bKh7e=+xNt!yX(r6neaG^A(rvKP6F}K-f^Y5l^a5zW?Jf) zpJadSPYcL8L-|(XppTqw;Z$Bk@YhL>X^lT0?rX&CfRvkrXu}kX)xZC6W%I*ZPj}u2 z?nFO@4>UUS$h_Gg&Op$~aEPb7($lu48uSqZf@5cQzd(L`*|ARMbuWG=3<%v&%d`3< zDwd+h-Fugsid@2c2wj)x72>`cZaDvdmyc`<)K~tOvl!NkjUyxwk*J3bcsAfZH2_3N zf_}D>DwulvW#~dtmbvq?G736p=>Zgun(ZuG#kf_x%u; zV&-5GvkOq_YxBcbYZFarP#mwj$8abmkT*O|Bq2qBM;3M4Q_sgC7!r7H7)nL=rP;s_j|NW-O{^!t_2FW52;!7ta!1Y`ZI~u-H9|yxQvJg zn)U_r9sSdhA}LMEYa!yrAH~UZw`k|xG;=ii_rV0FGw0^|L2fh36zpMC8gpx`s~hD9cDcKJx$ve>;-mYF6j#={P` zodtl96@3ZYX@I!@N!HaLdRuAM(X zh*}dwTVfC1cNIc~J;n}qUDA#q@1*&{M>{eeqxpdjN<~lLkM}X{uSb-B`^q9jkGSm; z#;7g-T(>v-77a5P(;0A?xe$fZA*W(AWhG@ly)2asy2CXKJDy)sMSq~`Lj5{}PEa$S zIBm|p<9tv9hQXE**GKoZv$gCoT3{I~X9;;eyp2fT-r}TFq|iyM3Qrwb^2W(iIu|GY zjCUi7mf%A&uO>%x+zT7ENur$dhiQM+pmXR;twQ->*PXDEdWfwm(1+;^h43h%q_s zq}W?%qKaXDeCFm#{5Cqn{J7|7$|deE7m;mYO<6K|xlHSuz}I=bA{mKQz^Uk(7b7x_ zV#*Y}2H)ZLXWien#;gKaMc2FFE435d5zS-;;VoG0KW`?I|jCKoGgdcdwx4Mc?JJ6L;g@6ccdz{ zw}j|M>s41au_vXE8h`Ge>xp+`)GKb!8||$ode}NG_@SJMi5_@A#uLfUhl;R_ds=JN zsxdM6RT1hjUxCT&+^BHNY}pIx-qzFRe$Bmj3hRL%=&8vmFC1hpo||ny$Uy4i{h~gS z=3NN;C{Pmgm&U24EEVB7h9jhDzPKi)k}#Dn>XG$Rjy`3~&oGh`|FV5DYpbakZB96n z=B-lTgpU>>5X8lueYX$Cl{$}HP1xyD#NkV-Yd78SRlEQzk^kL0T(kyPl_H{}Rs4Tu z9+oT!sxTiaY%UxaB`S|l%i{(k&4R2`|e zz%XfJ)!)^U8{F9#UQHqjC{jGVL?pS{eTBIIocnVxdIs#NpU$-6T@pVgDfkS;@f zE0<@RR7P9Uh=kl?r9?A4R`Agz$(yHLTY2l4&Z*yiQIZ}Y;ZXC(;}QYKn;eiK_by%Y zP1u~mCJN6&e{VL;O$j(?7F^!6xVQmaJ5Uui5H;nd1`5B118gJ!Zs|qo@N?l-_0A3w zy{Od0n&XhOmNx!ibHLX+^p_>=3Du7J0z=lw%iZ#yejwh9Z9px)=oSPn<7#f1k-Fjd zBCBV+eq}JNN49R9R26@23pqR_LJT##1Of6W2$GNbrT>1#Z;Q6LXLocH&S^pPz`gP> zSliNHGln9Jf-|07CmVb#D6Xc44HC-+VnQ`RlsXvKPXN3LC7YABZ`AIrSI^D+;*DP0 z_4X1`Su%#%;-cr}g;AOhP05x%q~0}{29d54#w-B!6-#Yl;FgWI3A-3g^r-3I8;=07 ztru$g6(P}SzGu+80s&csjr+h>&2Ke~!`%X}FCS0s{Yv96?bk^x+cM(dc3|FCdLq^( z3*i;)htB)j1B^9iApRu<8e+jgXyvW}W0Pe@Gi9+LnQNRrpa?URvv1aw3kVYg$o>xT z0V`T`w5)jFpU+r*Vf&L3)&?P32+i$NM~hgt9eoD0*sl6?fy%ID9Y%G+khft482Y3y zaO%AMT$TYkf+I$7kL#oDU>OUj*S*krlUFj&VV*(Zi?T>I@&?wdUxzYHPM zu}3)yi=+LD!v_B5=J-4-*7qsDX0Z%Bn%fNEKKSRy*-L_49*_tnJnX$%FnF`NExjQ# zN?sn*8*??+h8x-fG7Rghxzev!C1;rk$?IhU5_LkHigA#ewMBAq3F8H8fmBHN_r>Ck z+sff?KaVGx@-NK!CggrEDKZOh+rY>%of6w+ls6H2PqZ5WVM}HMfQUK=1as3V`1i{K!ypPc{w~jv*7HM$DB^ zGfbbMu9l_hajX&yturQ-9NAyJ3L=#xYt%Z?;W}MEF}O)Wm@P1O{0E*Y1VO)G96I!zd1MPhgUB5Ikc+z<55+4ud-);? zCHj#kBe_$25)b~^pIqU}chG=MudoO~irD+m8i8LgG=2SedWb79BO3RXfoGmZA!05Y zFx~HC0}zK7TNtKp#@k*l$nCK%TW4cQBtlT-p2q%GN`#(2ky`5BRu3J&7++Lu$7RVA zI4wa}niU>|A=xVOGww1+|65Ym^>w>#y@`M&hmbodn)cg=6sYIFD%{9@g_L-K&)$2z zF>K~k12JE%g6)suIIQJ1n_y;7P7IeP(3c?Gr=#A&w^iutytNOOaJm$+w9K{UcOV!g ze-mTzY-J1*SN5K0>!A0uOIlKG1jSHl)L+~@*nydCAGz{vU&@|k z>Bai7du$$*>21=Nl|xn-ekXhck=s!f09>7FiW7r>)0!V;3~;3U7y_;9%KljZ^BnYn zf{`rgE|=a$hJOS^k7**Tz?Z;J834$I|MLQ9!B38J=PB6k$|k@<2Rg}0w9$L^+eCUL z3fDKLuvr{<7-lROe!~pPG1CQpwqHUWi?VMuc(kFG^)hs23S*O0Wx=^RLU=y$N)bfg zHBiTppA4Amf~09R+?okXh2}yGj|Lh>TdnaZy`(@>&8RW_=Z<;blKnKPqQkRIVq`*$ zVB59zkCkVfB&MeQQFCpur;zH)!P{nnD5}${z4R+Rlh1C+0Cri0H}udd#CD?NoGqn! ztjlYR0Y+ENs1oxY5L+eZ?&1ZZvV)FVJ zRRRB22-tuUT+{$rS$dH!N2kEg8do9edM;3ZqmCA8qc+_3r2lT}Py3~5uuGg8DzTz& zS9Aqn^U0)nLzEk~(`kT5&)z5ZQO$mf6O6L18ub}E%Pm%QHtvrS7p3pIV}MyaSg|m4 zyFMxZ88lMZvn9JR4x~WOytPhoqdiAo)e*OI#=#zb;^O+bAI7px9hig>=o?hkuk=wT zT-vMS;)oE@K(FFXtAsIApK(lITiP{k1y}gd+pMzD{A3eLNTutv|Q%8?d;3TPp9pm1oL13QR$1B<`^q^Rw zZPXHiMhZ(N`cI61l*RJ@yth?j;Rf%9u*0~+%)pfGeB))P6M8Odi5rH%^&6btHSFM0HIJ#W!bZ)rz;B0V+ra4RCtcy*5 zIG@COG@28caV%(3xfWEl-nqYbNO5;U>0ap-@@a81uS4+_Vic@jXsd*M@uNKeDtY+> z7CMyj`!n$*--1##kKjSE4&x#V`x4n+z}8WUaEptJMoE5rmI%Sh_zU9gPP_<$vf`AD z!Zc01wH{1+0ZU`1QQv=`l0T={`G5ksp5a1}R-ch+OTY%b6mx}63%5Jv#8p9z8@%`4 z9u_uB&DjeHvB-UXjS;W zfbippzx&oqJ((=(afAIy215-O-m0hUh-CZijL7^gLtPd*?fIkkY0RI}%Cp!3&Q1xvp? z?c<1&g(4ZU4pMGOsXRdCS&JUvLk6-G+!##OlW{OW@6N_O1Uv9rm^C}fRlI3!V3xg# z`}c%cYw+%lcw=;o8U#SZ780}w>ok9YaIVYv<2@z}#LWm~a;yHDT6Hc^Kas9b zO2PBuM79C{HMrV2`q}t=o`JnHLP~Xf0HB>8JQL58kGjzGmIwi|M4h^ax*7~n@;>)o zbiBJ~T`{QUNdGN_d=Dk{U2^E|CcXOK`FDZVKsk;b-Mq^luo6bkgbyv(c@Xc5juSj4Prui z*Hka5m<||%&Hn58)q>+=EVTPoZ3I>Gwn%-*fE$t_QfcBz3b~r1tlnKo0)0BbYW;72 z*fDlHaK_1#_b9j=DyadvF4@|PAi_R(*%Of3tP=IF z0zb4SX~}db8Bi~_%nW&D6J)KI2P$37(>fWhBp0LwYH5&Dz5zpZPR<2Qm7jR|i2`Ra zR!_^QQoPrKk8!On0I>ed={jBD0h^6YMy%G=-NLO8rx>>XmYZ*uywZi2bvz(UgH$BG z(APH@A%7`h9R^BCpCoYj`yPXJ3dc>t%w&D`-595~Lc%!zUY*h{H$npTQVmAn<3Bem zEHqBO{=~=!Ar}Q+g008cII=Zul zw}AiHq)ku^H(0?7&bo!hS^bERO)!{oabHX)pU==lF3TWY4j+L0ba(m!OHW`hI%(rw zBlhOQa+p<*M|w7Dhi?h38W} zId0VO+NzQ6+Aj|C2B24GA`81qm+15ma8}_r&g=eW-}&ixe2KdTsgDxKawB> zZeOY@n3_ZX`PS6m3<3Is&%Pa9e3JI>F+2_lNjP3VK}5~eF7?U8zKHE(Y$9Soz-(rC2;%&cbsfi5`YXp}nJb zklW1eo!tx1=>CbyB{Elyytg!w^V4d;usbAo;CPvMglhAOq3QsmR+a$D>-3VwEvK1S z=&lfFO5$!_kj)E^F+VWe>a%7kD@i}W>r1Oh?oT8JtUN{+DuN1enyR=*F3i&EC3zD) z`>&^A6JNez4R5ucngcq8Fv}+8W*G^}l!b);8$8w7{<12u*`<5s!$?Acqx8z6tWVj! znlfN--R;}IEL3#tUc7}>{a*ok$?-{>*93C+D^Sa*oLdy!Y9Oa;ZpC2LMd7Z!I(t8b z)kN*jCU0S8^0pIBRZej#Rq6$hazE z%!X4xGK~%LTMyAShhTYoW#sW=LLezI_z|h9*o`cy?M&_5w40ON)-(5`2M7P{HAMmP z+sx92418K<8gmT^N6Ytd@PC<`H!owWJ|9?y=xC`-sNOGuP7B#@J2TvB7CF)6%2iey zFWyp}tjg)qwLSm}$jD*$!*LE7g*QQ?`E?=&o}oyCpv@9 zT18oBYoa|&fA&vb?7q-9MzRD6fq#U#W^cfE>U@UaA^FA|?5o^n23txLiKs zut&bV`^~?$cmsd$ErbytO%n6YlAzm@s6gJ&=tZ(9ANOVrs~xZj6i^V;1`|R|2R zVBMV@-1ccP(5Koo2zY({-G(Y_Np;{MNs2Wfs0^%<;fh;5ETldNP5!v~H-SxkB+6CZwfNv(2{^J)L{R6pxDIN0sR<<%+=!!0qm2$4pI`l&Z zww7xBlC6^!%kllu0!Gvy-y` diff --git a/src/gui/res/image/about.png b/src/gui/res/image/about.png index 66307bb077761806aace467307b7b7ae93e58a95..4630bd32a5d36f7588a3d8b4bbf9b8706e80adec 100644 GIT binary patch literal 4941 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 LNkvXXu0mjfO~zre literal 5701 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 From 1842a68a0ed58e207ef3ddeed0e5ce0e2ff1e157 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 20 Oct 2016 14:55:21 +0100 Subject: [PATCH 172/241] #5657 Always show trial counter, fix plurality --- src/gui/src/MainWindow.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 1178edd93..77a9a841b 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -1072,21 +1072,26 @@ void MainWindow::setEdition(Edition edition) void MainWindow::beginTrial(bool isExpiring) { - if (isExpiring) { + //Hack + //if (isExpiring) { + time_t daysLeft = m_LicenseManager->serialKey().daysLeft(::time(0)); QString expiringNotice ("

%1 days of " + "font-weight:600;\">%1 day%3 of " "your %2 trial remain. " + "\"https://symless.com/synergy/trial/thanks?id=%4\">" "Buy now!" "

"); expiringNotice = expiringNotice - .arg (m_LicenseManager->serialKey().daysLeft(::time(0))) + .arg (daysLeft) .arg (LicenseManager::getEditionName - (m_LicenseManager->activeEdition())); + (m_LicenseManager->activeEdition())) + .arg ((daysLeft == 1) ? "" : "s") + .arg(QString::fromStdString + (m_LicenseManager->serialKey().toString())); this->m_trialLabel->setText(expiringNotice); this->m_trialWidget->show(); - } + //} setWindowTitle (m_LicenseManager->activeEditionName()); } From b66043e0008379054a179411046afc211a502ec5 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 20 Oct 2016 17:59:03 +0100 Subject: [PATCH 173/241] #5657 Tweak plurality of trial countdown --- src/gui/src/ActivationDialog.cpp | 9 ++++++--- src/gui/src/MainWindow.cpp | 7 ++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 26392d987..6abbc938e 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -91,11 +91,14 @@ void ActivationDialog::accept() } Edition edition = m_LicenseManager->activeEdition(); + time_t daysLeft = m_LicenseManager->serialKey().daysLeft(::time(0)); if (edition != kUnregistered) { - QString thanksMessage = tr("Thanks for trying %1! %3\n\n%2 days of " - "your trial remain"). + QString thanksMessage = tr("Thanks for trying %1! %5\n\n%2 day%3 of " + "your trial remain%4"). arg (m_LicenseManager->getEditionName(edition)). - arg (m_LicenseManager->serialKey().daysLeft(::time(0))); + arg (daysLeft). + arg ((daysLeft == 1) ? "" : "s"). + arg ((daysLeft == 1) ? "s" : ""); if (edition == kPro) { thanksMessage = thanksMessage.arg("If you're using SSL, " diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 77a9a841b..8a2888fe9 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -1077,7 +1077,7 @@ void MainWindow::beginTrial(bool isExpiring) time_t daysLeft = m_LicenseManager->serialKey().daysLeft(::time(0)); QString expiringNotice ("

%1 day%3 of " - "your %2 trial remain. " "Buy now!" @@ -1087,8 +1087,9 @@ void MainWindow::beginTrial(bool isExpiring) .arg (LicenseManager::getEditionName (m_LicenseManager->activeEdition())) .arg ((daysLeft == 1) ? "" : "s") - .arg(QString::fromStdString - (m_LicenseManager->serialKey().toString())); + .arg (QString::fromStdString + (m_LicenseManager->serialKey().toString())) + .arg ((daysLeft == 1) ? "s" : ""); this->m_trialLabel->setText(expiringNotice); this->m_trialWidget->show(); //} From 492df1f3fd66de2b0020e8444585ff33c3cc9fe2 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 25 Oct 2016 16:27:12 +0100 Subject: [PATCH 174/241] #5657 Stop service and refresh license when trial expires --- src/gui/src/LicenseManager.cpp | 7 +++++-- src/gui/src/LicenseManager.h | 2 +- src/gui/src/MainWindow.cpp | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp index d3a865e17..aa623c88b 100644 --- a/src/gui/src/LicenseManager.cpp +++ b/src/gui/src/LicenseManager.cpp @@ -89,10 +89,13 @@ LicenseManager::serialKey() const return m_serialKey; } -void LicenseManager::refresh(bool acceptExpired) +void LicenseManager::refresh() { if (!m_AppConfig->serialKey().isEmpty()) { - setSerialKey(m_AppConfig->serialKey(), acceptExpired); + setSerialKey(m_AppConfig->serialKey(), true); + } + if (m_serialKey.isExpired(::time(0))) { + emit endTrial(true); } } diff --git a/src/gui/src/LicenseManager.h b/src/gui/src/LicenseManager.h index deac8b4fa..3d1d03b85 100644 --- a/src/gui/src/LicenseManager.h +++ b/src/gui/src/LicenseManager.h @@ -31,7 +31,7 @@ class LicenseManager: public QObject public: LicenseManager(AppConfig* appConfig); std::pair setSerialKey(QString serialKey, bool acceptExpired = false); - void refresh(bool acceptExpired = false); + void refresh(); Edition activeEdition() const; QString activeEditionName() const; SerialKey serialKey() const; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 8a2888fe9..a431d27ea 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -156,7 +156,7 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig, this, SLOT(sslToggled(bool)), Qt::QueuedConnection); setWindowTitle (m_LicenseManager->activeEditionName()); - m_LicenseManager->refresh(true); + m_LicenseManager->refresh(); } MainWindow::~MainWindow() @@ -451,6 +451,7 @@ void MainWindow::checkConnected(const QString& line) void MainWindow::checkLicense(const QString &line) { if (line.contains("trial has expired")) { + licenseManager().refresh(); raiseActivationDialog(); } } From f08f0b3f37f9212ff93ab3c959d3f63e3224c922 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 25 Oct 2016 16:40:33 +0100 Subject: [PATCH 175/241] #5657 Fix activation dialog tab order --- src/gui/res/ActivationDialog.ui | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gui/res/ActivationDialog.ui b/src/gui/res/ActivationDialog.ui index 1425ee0c7..86fea30d0 100644 --- a/src/gui/res/ActivationDialog.ui +++ b/src/gui/res/ActivationDialog.ui @@ -42,6 +42,9 @@ true + + true + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> @@ -117,6 +120,9 @@ p, li { white-space: pre-wrap; } + + m_pTextEditSerialKey + From b7e0473cb4e582015aca422a3b83f0a0023d9297 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 26 Oct 2016 15:34:36 +0100 Subject: [PATCH 176/241] Update buildbot to Qt 4.8.7 --- src/gui/src/MainWindow.cpp | 1 - src/gui/src/ZeroconfService.h | 1 + src/setup/win32/Include.wxi | 2 +- src/setup/win32/Product.wxs | 3 ++- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index a431d27ea..8e53f2d65 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -51,7 +51,6 @@ #endif #if defined(Q_OS_WIN) -#define _WIN32_WINNT 0x0501 #define WIN32_LEAN_AND_MEAN #include #endif diff --git a/src/gui/src/ZeroconfService.h b/src/gui/src/ZeroconfService.h index 8f9aa6db0..fde78201c 100644 --- a/src/gui/src/ZeroconfService.h +++ b/src/gui/src/ZeroconfService.h @@ -21,6 +21,7 @@ #include "ZeroconfRecord.h" #include +#include typedef int32_t DNSServiceErrorType; diff --git a/src/setup/win32/Include.wxi b/src/setup/win32/Include.wxi index 62f92ac7c..0961390bb 100644 --- a/src/setup/win32/Include.wxi +++ b/src/setup/win32/Include.wxi @@ -7,7 +7,7 @@ - + diff --git a/src/setup/win32/Product.wxs b/src/setup/win32/Product.wxs index 03a309e81..51a9976bb 100644 --- a/src/setup/win32/Product.wxs +++ b/src/setup/win32/Product.wxs @@ -118,7 +118,8 @@ - + + From 6b0cd355276eb754edce8906ee029af81393fc36 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 26 Oct 2016 08:10:09 -0700 Subject: [PATCH 177/241] #5186 Update registry keys to enable Windows 7 compatibility and disable DPI scaling --- src/setup/win32/Product.wxs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/setup/win32/Product.wxs b/src/setup/win32/Product.wxs index 03a309e81..cd794d172 100644 --- a/src/setup/win32/Product.wxs +++ b/src/setup/win32/Product.wxs @@ -28,8 +28,19 @@ + + + + + + + + + From 640262dfff6fad74ab40f4c8aa7d3ed8debb104a Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 26 Oct 2016 08:43:55 -0700 Subject: [PATCH 178/241] #5186 Only add DPI related registry key on Windows 8 or above --- src/setup/win32/Product.wxs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/setup/win32/Product.wxs b/src/setup/win32/Product.wxs index cd794d172..20de813af 100644 --- a/src/setup/win32/Product.wxs +++ b/src/setup/win32/Product.wxs @@ -38,6 +38,10 @@ Action="createAndRemoveOnUninstall"> + + + = 602)]]> + From c62c4d503dbbc2fedb4a9f247274684b2a1cfae4 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 28 Oct 2016 03:14:44 -0700 Subject: [PATCH 179/241] #5186 Remove dpiaware manifest --- res/dpiaware.manifest | 15 --------------- src/cmd/synergyc/CMakeLists.txt | 9 --------- src/cmd/synergyd/CMakeLists.txt | 9 --------- src/cmd/synergys/CMakeLists.txt | 9 --------- 4 files changed, 42 deletions(-) delete mode 100644 res/dpiaware.manifest diff --git a/res/dpiaware.manifest b/res/dpiaware.manifest deleted file mode 100644 index 743e3369b..000000000 --- a/res/dpiaware.manifest +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - true - - - - diff --git a/src/cmd/synergyc/CMakeLists.txt b/src/cmd/synergyc/CMakeLists.txt index 653cf374f..22bbe2573 100644 --- a/src/cmd/synergyc/CMakeLists.txt +++ b/src/cmd/synergyc/CMakeLists.txt @@ -60,15 +60,6 @@ add_executable(synergyc ${sources}) target_link_libraries(synergyc arch base client common io mt net ipc platform server synergy ${libs} ${OPENSSL_LIBS}) -if (WIN32) - ADD_CUSTOM_COMMAND( - TARGET synergyc - POST_BUILD - COMMAND "mt.exe" -manifest \"${CMAKE_SOURCE_DIR}\\res\\dpiaware.manifest\" -inputresource:\"$\"\;\#1 -outputresource:\"$\"\;\#1 - COMMENT "Adding display aware manifest..." - ) -endif() - if (CONF_CPACK) install(TARGETS synergyc diff --git a/src/cmd/synergyd/CMakeLists.txt b/src/cmd/synergyd/CMakeLists.txt index c8b578a90..a63bee676 100644 --- a/src/cmd/synergyd/CMakeLists.txt +++ b/src/cmd/synergyd/CMakeLists.txt @@ -37,15 +37,6 @@ endif() target_link_libraries(synergyd arch base common io ipc mt net platform synergy shared ${libs} ${OPENSSL_LIBS}) -if (WIN32) - ADD_CUSTOM_COMMAND( - TARGET synergyd - POST_BUILD - COMMAND "mt.exe" -manifest \"${CMAKE_SOURCE_DIR}\\res\\dpiaware.manifest\" -inputresource:\"$\"\;\#1 -outputresource:\"$\"\;\#1 - COMMENT "Adding display aware manifest..." - ) -endif() - if (CONF_CPACK) install(TARGETS synergyd diff --git a/src/cmd/synergys/CMakeLists.txt b/src/cmd/synergys/CMakeLists.txt index 023574b16..2474bcc67 100644 --- a/src/cmd/synergys/CMakeLists.txt +++ b/src/cmd/synergys/CMakeLists.txt @@ -60,15 +60,6 @@ add_executable(synergys ${sources}) target_link_libraries(synergys arch base client common io mt net ipc platform server synergy ${libs} ${OPENSSL_LIBS}) -if (WIN32) - ADD_CUSTOM_COMMAND( - TARGET synergys - POST_BUILD - COMMAND "mt.exe" -manifest \"${CMAKE_SOURCE_DIR}\\res\\dpiaware.manifest\" -inputresource:\"$\"\;\#1 -outputresource:\"$\"\;\#1 - COMMENT "Adding display aware manifest..." - ) -endif() - if (CONF_CPACK) install(TARGETS synergys From cf397a0d6f4cce4d8527730f849208453a22a97d Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 28 Oct 2016 04:50:06 -0700 Subject: [PATCH 180/241] #5186 Remove Dpi calculation code --- src/gui/src/MainWindow.cpp | 19 -------- src/lib/platform/MSWindowsScreen.cpp | 56 ++++++--------------- src/lib/server/Server.cpp | 9 ---- src/lib/synergy/ArgParser.cpp | 13 ----- src/lib/synergy/DpiHelper.cpp | 53 -------------------- src/lib/synergy/DpiHelper.h | 38 --------------- src/test/unittests/synergy/DpiHelperTests.cpp | 70 --------------------------- 7 files changed, 14 insertions(+), 244 deletions(-) delete mode 100644 src/lib/synergy/DpiHelper.cpp delete mode 100644 src/lib/synergy/DpiHelper.h delete mode 100644 src/test/unittests/synergy/DpiHelperTests.cpp diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index a431d27ea..eb37894a4 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -800,25 +800,6 @@ bool MainWindow::serverArgs(QStringList& args, QString& app) args << "--serial-key" << appConfig().serialKey(); } -#if defined(Q_OS_WIN) - // pass in physical resolution and primary screen center - // TODO: get this information in the core binary even when - // high DPI is used - int height = QApplication::desktop()->height(); - int width = QApplication::desktop()->width(); - - QRect rec = QApplication::desktop()->screenGeometry(); - int heightCenter = rec.height() / 2; - int widthCenter = rec.width() / 2; - - appendLogDebug(tr("screen resolution: %1 %2 primary screen center: %3 %4") - .arg(width).arg(height).arg(widthCenter).arg(heightCenter)); - - args << "--res-w" << QString::number(width); - args << "--res-h" << QString::number(height); - args << "--prm-wc" << QString::number(widthCenter); - args << "--prm-hc" << QString::number(heightCenter); - #endif return true; } diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 74abfcd75..1ab3adf08 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -31,7 +31,6 @@ #include "synergy/App.h" #include "synergy/ArgsBase.h" #include "synergy/ClientApp.h" -#include "synergy/DpiHelper.h" #include "mt/Lock.h" #include "mt/Thread.h" #include "arch/win32/ArchMiscWindows.h" @@ -146,10 +145,6 @@ MSWindowsScreen::MSWindowsScreen( stopOnDeskSwitch); m_keyState = new MSWindowsKeyState(m_desks, getEventTarget(), m_events); - DpiHelper::calculateDpi( - GetSystemMetrics(SM_CXVIRTUALSCREEN), - GetSystemMetrics(SM_CYVIRTUALSCREEN)); - updateScreenShape(); m_class = createWindowClass(); m_window = createWindow(m_class, "Synergy"); @@ -348,8 +343,7 @@ MSWindowsScreen::leave() // warp to center LOG((CLOG_DEBUG1 "warping cursor to center: %+d, %+d", m_xCenter, m_yCenter)); - float dpi = DpiHelper::getDpi(); - warpCursor(m_xCenter / dpi, m_yCenter / dpi); + warpCursor(m_xCenter, m_yCenter); // disable special key sequences on win95 family enableSpecialKeys(false); @@ -1369,20 +1363,10 @@ MSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) bool MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) { - SInt32 originalMX = mx; - SInt32 originalMY = my; - float scaledMX = (float)mx; - float scaledMY = (float)my; - - if (DpiHelper::s_dpiScaled) { - scaledMX /= DpiHelper::getDpi(); - scaledMY /= DpiHelper::getDpi(); - } - // compute motion delta (relative to the last known // mouse position) - float x = scaledMX - m_xCursor; - float y = scaledMY - m_yCursor; + float x = (float)mx - m_xCursor; + float y = (float)my - m_yCursor; LOG((CLOG_DEBUG3 "mouse move - motion delta: %+d=(%+d - %+d),%+d=(%+d - %+d)", @@ -1395,14 +1379,14 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) } // save position to compute delta of next motion - saveMousePosition((SInt32)scaledMX, (SInt32)scaledMY); + saveMousePosition(mx, my); if (m_isOnScreen) { // motion on primary screen sendEvent( m_events->forIPrimaryScreen().motionOnPrimary(), - MotionInfo::alloc(originalMX, originalMY)); + MotionInfo::alloc(m_xCursor, m_yCursor)); if (m_buttons[kButtonLeft] == true && m_draggingStarted == false) { m_draggingStarted = true; @@ -1415,8 +1399,7 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) // will always try to return to the original entry point on the // secondary screen. LOG((CLOG_DEBUG5 "warping server cursor to center: %+d,%+d", m_xCenter, m_yCenter)); - float dpi = DpiHelper::getDpi(); - warpCursorNoFlush(m_xCenter / dpi, m_yCenter / dpi); + warpCursorNoFlush(m_xCenter, m_yCenter); // examine the motion. if it's about the distance // from the center of the screen to an edge then @@ -1424,10 +1407,10 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) // ignore (see warpCursorNoFlush() for a further // description). static SInt32 bogusZoneSize = 10; - if (-x + bogusZoneSize > (m_xCenter - m_x) / dpi || - x + bogusZoneSize > (m_x + m_w - m_xCenter) / dpi || - -y + bogusZoneSize > (m_yCenter - m_y) / dpi || - y + bogusZoneSize > (m_y + m_h - m_yCenter) / dpi) { + if (-x + bogusZoneSize > m_xCenter - m_x || + x + bogusZoneSize > m_x + m_w - m_xCenter || + -y + bogusZoneSize > m_yCenter - m_y || + y + bogusZoneSize > m_y + m_h - m_yCenter) { LOG((CLOG_DEBUG "dropped bogus delta motion: %+d,%+d", x, y)); } @@ -1623,22 +1606,11 @@ void MSWindowsScreen::updateScreenShape() { // get shape and center - if (DpiHelper::s_dpiScaled) { - // use the original resolution size for width and height - m_w = (SInt32)DpiHelper::s_resolutionWidth; - m_h = (SInt32)DpiHelper::s_resolutionHeight; + m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); + m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); - // calculate center position according to the original size - m_xCenter = (SInt32)DpiHelper::s_primaryWidthCenter; - m_yCenter = (SInt32)DpiHelper::s_primaryHeightCenter; - } - else { - m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); - m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); - - m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; - m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; - } + m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; + m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; // get position m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index ec730bcd4..c980d03b3 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -33,7 +33,6 @@ #include "synergy/KeyState.h" #include "synergy/Screen.h" #include "synergy/PacketStreamFilter.h" -#include "synergy/DpiHelper.h" #include "net/TCPSocket.h" #include "net/IDataSocket.h" #include "net/IListenSocket.h" @@ -2004,14 +2003,6 @@ Server::onMouseMoveSecondary(SInt32 dx, SInt32 dy) SInt32 newX = m_x; SInt32 newY = m_y; - if (DpiHelper::s_dpiScaled) { - // only scale if it's going back to server - if (newScreen->isPrimary()) { - newX = (SInt32)(newX / DpiHelper::getDpi()); - newY = (SInt32)(newY / DpiHelper::getDpi()); - } - } - // switch screens switchScreen(newScreen, newX, newY, false); } diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 1af32843b..a21cf9501 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -23,7 +23,6 @@ #include "synergy/ClientArgs.h" #include "synergy/ToolArgs.h" #include "synergy/ArgsBase.h" -#include "synergy/DpiHelper.h" #include "base/Log.h" #include "base/String.h" @@ -58,18 +57,6 @@ ArgParser::parseServerArgs(ServerArgs& args, int argc, const char* const* argv) // save configuration file path args.m_configFile = argv[++i]; } - else if (isArg(i, argc, argv, "", "--res-w", 1)) { - DpiHelper::s_resolutionWidth = synergy::string::stringToSizeType(argv[++i]); - } - else if (isArg(i, argc, argv, "", "--res-h", 1)) { - DpiHelper::s_resolutionHeight = synergy::string::stringToSizeType(argv[++i]); - } - else if (isArg(i, argc, argv, "", "--prm-wc", 1)) { - DpiHelper::s_primaryWidthCenter = synergy::string::stringToSizeType(argv[++i]); - } - else if (isArg(i, argc, argv, "", "--prm-hc", 1)) { - DpiHelper::s_primaryHeightCenter = synergy::string::stringToSizeType(argv[++i]); - } else if (isArg(i, argc, argv, "", "--serial-key", 1)) { args.m_serial = SerialKey(argv[++i]); } diff --git a/src/lib/synergy/DpiHelper.cpp b/src/lib/synergy/DpiHelper.cpp deleted file mode 100644 index 2f2ffcb70..000000000 --- a/src/lib/synergy/DpiHelper.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 . - */ - -#include "synergy/DpiHelper.h" -#include "base/Log.h" - -#include - -size_t DpiHelper::s_dpi = kDefaultDpi; -bool DpiHelper::s_dpiScaled = false; -size_t DpiHelper::s_resolutionWidth = 0; -size_t DpiHelper::s_resolutionHeight = 0; -size_t DpiHelper::s_primaryWidthCenter = 0; -size_t DpiHelper::s_primaryHeightCenter = 0; - -void DpiHelper::calculateDpi(size_t width, size_t height) -{ - if (s_resolutionWidth == 0 || - s_resolutionHeight == 0 || - s_primaryWidthCenter == 0 || - s_primaryHeightCenter == 0) { - return; - } - - size_t dpiTest1 = s_resolutionWidth * 100 / width; - size_t dpiTest2 = s_resolutionHeight * 100 / height; - - if (dpiTest1 == dpiTest2) { - s_dpi = dpiTest1; - - if (s_dpi != kDefaultDpi) { - s_dpiScaled = true; - - LOG((CLOG_DEBUG "DPI: %d%%", s_dpi)); - LOG((CLOG_DEBUG "physical resolution: %d, %d scaled resolution: %d, %d", - s_resolutionWidth, s_resolutionHeight, width, height)); - } - } -} diff --git a/src/lib/synergy/DpiHelper.h b/src/lib/synergy/DpiHelper.h deleted file mode 100644 index 0488a4652..000000000 --- a/src/lib/synergy/DpiHelper.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 . - */ - -#pragma once - -#include "common/common.h" - -class DpiHelper { -public: - enum EDpi { - kDefaultDpi = 100 - }; - - static void calculateDpi(size_t width, size_t height); - static float getDpi() { return (float)(s_dpi / 100.0f); } - -public: - static size_t s_dpi; - static bool s_dpiScaled; - static size_t s_resolutionWidth; - static size_t s_resolutionHeight; - static size_t s_primaryWidthCenter; - static size_t s_primaryHeightCenter; -}; diff --git a/src/test/unittests/synergy/DpiHelperTests.cpp b/src/test/unittests/synergy/DpiHelperTests.cpp deleted file mode 100644 index 9dee828ed..000000000 --- a/src/test/unittests/synergy/DpiHelperTests.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 . - */ - -#include "synergy/DpiHelper.h" - -#include "test/global/gtest.h" - -void resetStaticVariables() -{ - DpiHelper::s_resolutionWidth = 0; - DpiHelper::s_resolutionHeight = 0; - DpiHelper::s_primaryWidthCenter = 0; - DpiHelper::s_primaryHeightCenter = 0; - DpiHelper::s_dpi = DpiHelper::kDefaultDpi; - DpiHelper::s_dpiScaled = false; -} - -TEST(DpiHelperTests, calculateDpi_samePhysicalAndVirtualResolutions_defaultDpi) -{ - resetStaticVariables(); - - DpiHelper::s_resolutionWidth = 1920; - DpiHelper::s_resolutionHeight = 1080; - DpiHelper::s_primaryWidthCenter = 960; - DpiHelper::s_primaryHeightCenter = 540; - - DpiHelper::calculateDpi(1920, 1080); - - EXPECT_FALSE(DpiHelper::s_dpiScaled); - EXPECT_EQ(DpiHelper::kDefaultDpi, DpiHelper::s_dpi); -} - -TEST(DpiHelperTests, calculateDpi_differentPhysicalAndVirtualResolutions_scaledDpi) -{ - resetStaticVariables(); - - DpiHelper::s_resolutionWidth = 1920; - DpiHelper::s_resolutionHeight = 1080; - DpiHelper::s_primaryWidthCenter = 960; - DpiHelper::s_primaryHeightCenter = 540; - - DpiHelper::calculateDpi(960, 540); - - EXPECT_TRUE(DpiHelper::s_dpiScaled); - EXPECT_EQ(200, DpiHelper::s_dpi); -} - -TEST(DpiHelperTests, calculateDpi_defaultStaticValues_defaultDpi) -{ - resetStaticVariables(); - - DpiHelper::calculateDpi(1920, 1080); - - EXPECT_FALSE(DpiHelper::s_dpiScaled); - EXPECT_EQ(DpiHelper::kDefaultDpi, DpiHelper::s_dpi); -} From c2372bc9a80c4515db70a272512c0bb4f49f1d2f Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 28 Oct 2016 05:24:07 -0700 Subject: [PATCH 181/241] #5186 Remove accumulate fraction DPI calculation --- src/lib/platform/MSWindowsScreen.cpp | 24 +++--------------------- src/lib/platform/MSWindowsScreen.h | 7 ------- 2 files changed, 3 insertions(+), 28 deletions(-) diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 1ab3adf08..d6466b9bb 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -104,7 +104,6 @@ MSWindowsScreen::MSWindowsScreen( m_xCenter(0), m_yCenter(0), m_multimon(false), m_xCursor(0), m_yCursor(0), - m_xFractionalMove(0.0f), m_yFractionalMove(0.0f), m_sequenceNumber(0), m_mark(0), m_markReceived(0), @@ -570,21 +569,6 @@ void MSWindowsScreen::saveMousePosition(SInt32 x, SInt32 y) { LOG((CLOG_DEBUG5 "saved mouse position for next delta: %+d,%+d", x,y)); } -void MSWindowsScreen::accumulateFractionalMove(float x, float y, SInt32& intX, SInt32& intY) -{ - // Accumulate together the move into the running total - m_xFractionalMove += x; - m_yFractionalMove += y; - - // Return the integer part - intX = (SInt32)m_xFractionalMove; - intY = (SInt32)m_yFractionalMove; - - // And keep only the fractional part - m_xFractionalMove -= intX; - m_yFractionalMove -= intY; -} - UInt32 MSWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask) { @@ -1365,8 +1349,8 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) { // compute motion delta (relative to the last known // mouse position) - float x = (float)mx - m_xCursor; - float y = (float)my - m_yCursor; + SInt32 x = mx - m_xCursor; + SInt32 y = my - m_yCursor; LOG((CLOG_DEBUG3 "mouse move - motion delta: %+d=(%+d - %+d),%+d=(%+d - %+d)", @@ -1416,9 +1400,7 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) } else { // send motion - SInt32 ix, iy; - accumulateFractionalMove(x, y, ix, iy); - sendEvent(m_events->forIPrimaryScreen().motionOnSecondary(), MotionInfo::alloc(ix, iy)); + sendEvent(m_events->forIPrimaryScreen().motionOnSecondary(), MotionInfo::alloc(x, y)); } } diff --git a/src/lib/platform/MSWindowsScreen.h b/src/lib/platform/MSWindowsScreen.h index 4d947be7f..b1ae0f944 100644 --- a/src/lib/platform/MSWindowsScreen.h +++ b/src/lib/platform/MSWindowsScreen.h @@ -216,10 +216,6 @@ class MSWindowsScreen : public PlatformScreen { // save last position of mouse to compute next delta movement void saveMousePosition(SInt32 x, SInt32 y); - // accumulates together a series of fractional pixel moves, each time - // taking away and returning just the integer part of the running total. - void accumulateFractionalMove(float x, float y, SInt32& intX, SInt32& intY); - // check if it is a modifier key repeating message bool isModifierRepeat(KeyModifierMask oldState, KeyModifierMask state, WPARAM wParam) const; @@ -270,9 +266,6 @@ class MSWindowsScreen : public PlatformScreen { // last mouse position SInt32 m_xCursor, m_yCursor; - // accumulated fractional pixel moves - float m_xFractionalMove, m_yFractionalMove; - // last clipboard UInt32 m_sequenceNumber; From 0d4fd6dcef7e941fa6d916511066cf0653ca11f3 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 28 Oct 2016 06:00:11 -0700 Subject: [PATCH 182/241] #5186 Reorder some functions call to make it clear --- src/lib/platform/MSWindowsScreen.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index d6466b9bb..defe2b821 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -1590,13 +1590,11 @@ MSWindowsScreen::updateScreenShape() // get shape and center m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); - + m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); + m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; - // get position - m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); - m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); // check for multiple monitors m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || m_h != GetSystemMetrics(SM_CYSCREEN)); From 8d193c76b586511586ea33fda3411614631d671b Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 28 Oct 2016 06:00:46 -0700 Subject: [PATCH 183/241] #5186 Apply DPI aware option in local machine rather than current user in registry --- src/setup/win32/Product.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setup/win32/Product.wxs b/src/setup/win32/Product.wxs index 20de813af..3663607a9 100644 --- a/src/setup/win32/Product.wxs +++ b/src/setup/win32/Product.wxs @@ -33,7 +33,7 @@ - From 906fd15b4a2e7421ff2f697cf6a94bec60022f22 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 26 Oct 2016 15:57:38 +0100 Subject: [PATCH 184/241] #5707 Store and update last version uses in settings --- src/gui/src/AppConfig.cpp | 11 +++++++++++ src/gui/src/AppConfig.h | 7 ++++++- src/gui/src/MainWindow.cpp | 5 +++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 7fd37f505..06f9f43da 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -162,6 +162,7 @@ void AppConfig::loadSettings() m_CryptoEnabled = settings().value("cryptoEnabled", true).toBool(); m_AutoHide = settings().value("autoHide", false).toBool(); m_Serialkey = settings().value("serialKey", "").toString(); + m_lastVersion = settings().value("lastVersion", "Unknown").toString(); m_LastExpiringWarningTime = settings().value("lastExpiringWarningTime", 0).toInt(); m_ActivationHasRun = settings().value("activationHasRun", false).toBool(); } @@ -187,6 +188,7 @@ void AppConfig::saveSettings() settings().setValue("cryptoEnabled", m_CryptoEnabled); settings().setValue("autoHide", m_AutoHide); settings().setValue("serialKey", m_Serialkey); + settings().setValue("lastVersion", m_lastVersion); settings().setValue("lastExpiringWarningTime", m_LastExpiringWarningTime); settings().setValue("activationHasRun", m_ActivationHasRun); settings().sync(); @@ -203,6 +205,15 @@ AppConfig& AppConfig::activationHasRun(bool value) return *this; } +QString AppConfig::lastVersion() const +{ + return m_lastVersion; +} + +void AppConfig::setLastVersion(QString version) { + m_lastVersion = version; +} + QSettings &AppConfig::settings() { return *m_pSettings; } void AppConfig::setScreenName(const QString &s) { m_ScreenName = s; } diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 7aaeeb41e..4dcd4572b 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -104,7 +104,11 @@ class AppConfig: public QObject bool activationHasRun() const; AppConfig& activationHasRun(bool value); - void saveSettings();; + QString lastVersion() const; + + void saveSettings(); + void setLastVersion(QString version); + protected: QSettings& settings(); void setScreenName(const QString& s); @@ -139,6 +143,7 @@ class AppConfig: public QObject bool m_CryptoEnabled; bool m_AutoHide; QString m_Serialkey; + QString m_lastVersion; int m_LastExpiringWarningTime; bool m_ActivationHasRun; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 8e53f2d65..59808220a 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -156,6 +156,11 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig, setWindowTitle (m_LicenseManager->activeEditionName()); m_LicenseManager->refresh(); + + QString currentVersion = m_VersionChecker.getVersion(); + if (m_AppConfig->lastVersion() != currentVersion) { + m_AppConfig->setLastVersion (currentVersion); + } } MainWindow::~MainWindow() From 9f1e91cc7688e9b0edbb55188f9f633add5cf804 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 26 Oct 2016 19:50:18 +0100 Subject: [PATCH 185/241] Remove pointless call to curl_free --- src/lib/arch/unix/ArchInternetUnix.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/arch/unix/ArchInternetUnix.cpp b/src/lib/arch/unix/ArchInternetUnix.cpp index 596000c18..fe4a39bac 100644 --- a/src/lib/arch/unix/ArchInternetUnix.cpp +++ b/src/lib/arch/unix/ArchInternetUnix.cpp @@ -116,7 +116,6 @@ CurlFacade::urlEncode(const String& url) char* resultCStr = curl_easy_escape(m_curl, url.c_str(), 0); if (resultCStr == NULL) { - curl_free(resultCStr); throw XArch("CURL escape failed."); } From 2de06b972751dae2086805530d405f02995a3638 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 26 Oct 2016 20:14:02 +0100 Subject: [PATCH 186/241] #5707 Add support for upgrade notifications --- src/gui/src/CoreInterface.cpp | 6 ++++++ src/gui/src/CoreInterface.h | 1 + src/gui/src/LicenseManager.cpp | 5 +++++ src/gui/src/LicenseManager.h | 1 + src/lib/synergy/ArgParser.cpp | 4 ++++ src/lib/synergy/ToolApp.cpp | 31 ++++++++++++++++++++++++++++--- src/lib/synergy/ToolApp.h | 1 + src/lib/synergy/ToolArgs.cpp | 3 ++- src/lib/synergy/ToolArgs.h | 1 + 9 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index f7e987d70..8b3a1ae3a 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -62,6 +62,12 @@ QString CoreInterface::getSerialKeyFilePath() return filename; } +QString CoreInterface::notifyUpgrade (QString const& version, + QString const& serialKey) { + QStringList args("--notify-upgrade"); + QString input(version + ":" + serialKey); + return run(args, input); +} QString CoreInterface::notifyActivation(const QString& identity) { diff --git a/src/gui/src/CoreInterface.h b/src/gui/src/CoreInterface.h index c8a291e21..3576f02a7 100644 --- a/src/gui/src/CoreInterface.h +++ b/src/gui/src/CoreInterface.h @@ -29,5 +29,6 @@ class CoreInterface QString getArch(); QString getSerialKeyFilePath(); QString notifyActivation(const QString& identity); + QString notifyUpgrade (QString const& version, QString const& serialKey); QString run(const QStringList& args, const QString& input = ""); }; diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp index aa623c88b..15f799336 100644 --- a/src/gui/src/LicenseManager.cpp +++ b/src/gui/src/LicenseManager.cpp @@ -71,6 +71,11 @@ LicenseManager::setSerialKey(QString serialKeyString, bool acceptExpired) return ret; } +void +LicenseManager::notifyUpdate(QString version) { + +} + Edition LicenseManager::activeEdition() const { diff --git a/src/gui/src/LicenseManager.h b/src/gui/src/LicenseManager.h index 3d1d03b85..4d235369e 100644 --- a/src/gui/src/LicenseManager.h +++ b/src/gui/src/LicenseManager.h @@ -36,6 +36,7 @@ class LicenseManager: public QObject QString activeEditionName() const; SerialKey serialKey() const; void skipActivation(); + void notifyUpdate(QString version); static QString getEditionName(Edition edition, bool trial = false); private: diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 1af32843b..828e4c06d 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -208,6 +208,10 @@ ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv) args.m_notifyActivation = true; return true; } + else if (isArg(i, argc, argv, NULL, "--notify-upgrade", 0)) { + args.m_notifyUpgrade = true; + return true; + } else { return false; } diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index bf3dfdce2..434ae6845 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -1,11 +1,11 @@ /* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2014-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 @@ -80,6 +80,9 @@ ToolApp::run(int argc, char** argv) else if (m_args.m_getArch) { std::cout << ARCH->getPlatformName() << std::endl; } + else if (m_args.m_notifyUpgrade) { + notifyUpgrade(); + } else if (m_args.m_notifyActivation) { notifyActivation(); } @@ -134,7 +137,29 @@ ToolApp::loginAuth() } } -void +void +ToolApp::notifyUpgrade() +{ + String data; + std::cin >> data; + + std::vector parts = synergy::string::splitString(data, ':'); + size_t count = parts.size(); + + if (count == 2) { + std::stringstream ss; + ss << JSON_URL << "notify/upgraded/"; + ss << "?version=" << parts[0]; + ss << "&serial=" << parts[1]; + + std::cout << ARCH->internet().get(ss.str()) << std::endl; + } + else { + throw XSynergy("Invalid upgrade data."); + } +} + +void ToolApp::notifyActivation() { String info; diff --git a/src/lib/synergy/ToolApp.h b/src/lib/synergy/ToolApp.h index 39c87ca78..0f918827d 100644 --- a/src/lib/synergy/ToolApp.h +++ b/src/lib/synergy/ToolApp.h @@ -30,6 +30,7 @@ class ToolApp : public MinimalApp private: void loginAuth(); void notifyActivation(); + void notifyUpgrade(); private: ToolArgs m_args; diff --git a/src/lib/synergy/ToolArgs.cpp b/src/lib/synergy/ToolArgs.cpp index 5f67c6667..4884696f8 100644 --- a/src/lib/synergy/ToolArgs.cpp +++ b/src/lib/synergy/ToolArgs.cpp @@ -23,6 +23,7 @@ ToolArgs::ToolArgs() : m_getInstalledDir(false), m_getProfileDir(false), m_getArch(false), - m_notifyActivation(false) + m_notifyActivation(false), + m_notifyUpgrade(false) { } diff --git a/src/lib/synergy/ToolArgs.h b/src/lib/synergy/ToolArgs.h index 5febab9e3..4a620a9bf 100644 --- a/src/lib/synergy/ToolArgs.h +++ b/src/lib/synergy/ToolArgs.h @@ -30,4 +30,5 @@ class ToolArgs { bool m_getProfileDir; bool m_getArch; bool m_notifyActivation; + bool m_notifyUpgrade; }; From 4206799ae357ac603be9720bd702edfcf691d588 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 28 Oct 2016 13:17:07 +0100 Subject: [PATCH 187/241] #5707 Add from and to version numbers to version update notification --- src/gui/src/ActivationNotifier.cpp | 21 ++++++++++++++++++++- src/gui/src/ActivationNotifier.h | 8 +++++++- src/gui/src/CoreInterface.cpp | 7 ++++--- src/gui/src/CoreInterface.h | 4 +++- src/gui/src/LicenseManager.cpp | 15 ++++++++++++++- src/gui/src/LicenseManager.h | 2 +- src/gui/src/MainWindow.cpp | 5 ++++- src/lib/synergy/ToolApp.cpp | 7 ++++--- 8 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/gui/src/ActivationNotifier.cpp b/src/gui/src/ActivationNotifier.cpp index fb2fd465e..e8159a34d 100644 --- a/src/gui/src/ActivationNotifier.cpp +++ b/src/gui/src/ActivationNotifier.cpp @@ -20,7 +20,7 @@ #include "CoreInterface.h" ActivationNotifier::ActivationNotifier(QObject *parent) : - QObject(parent) + QObject(parent) { } @@ -29,6 +29,15 @@ void ActivationNotifier::setIdentity(QString identity) m_Identity = identity; } +void ActivationNotifier::setUpgradeInfo(QString const& fromVersion, + QString const& toVersion, + QString const& serialKey) +{ + m_fromVersion = fromVersion; + m_toVersion = toVersion; + m_serialKey = serialKey; +} + void ActivationNotifier::notify() { CoreInterface coreInterface; @@ -39,3 +48,13 @@ void ActivationNotifier::notify() // catch all exceptions and fails silently } } + +void ActivationNotifier::notifyUpgrade() +{ + try { + CoreInterface coreInterface; + coreInterface.notifyUpdate(m_fromVersion, m_toVersion, + m_serialKey); + } catch (...) { + } +} diff --git a/src/gui/src/ActivationNotifier.h b/src/gui/src/ActivationNotifier.h index d245cd278..fda336a4e 100644 --- a/src/gui/src/ActivationNotifier.h +++ b/src/gui/src/ActivationNotifier.h @@ -24,18 +24,24 @@ class ActivationNotifier : public QObject { Q_OBJECT public: - explicit ActivationNotifier(QObject *parent = 0); + explicit ActivationNotifier(QObject *parent = 0); void setIdentity(QString identity); + void setUpgradeInfo(QString const& fromVersion, + QString const& toVersion, QString const& serialKey); public slots: void notify(); + void notifyUpgrade(); signals: void finished(); private: QString m_Identity; + QString m_fromVersion; + QString m_toVersion; + QString m_serialKey; }; #endif // ACTIVATIONNOTIFIER_H diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index 8b3a1ae3a..aa8d14a3c 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -62,10 +62,11 @@ QString CoreInterface::getSerialKeyFilePath() return filename; } -QString CoreInterface::notifyUpgrade (QString const& version, - QString const& serialKey) { +QString CoreInterface::notifyUpdate (QString const& fromVersion, + QString const& toVersion, + QString const& serialKey) { QStringList args("--notify-upgrade"); - QString input(version + ":" + serialKey); + QString input(fromVersion + ":" + toVersion + ":" + serialKey); return run(args, input); } diff --git a/src/gui/src/CoreInterface.h b/src/gui/src/CoreInterface.h index 3576f02a7..98a84f57a 100644 --- a/src/gui/src/CoreInterface.h +++ b/src/gui/src/CoreInterface.h @@ -29,6 +29,8 @@ class CoreInterface QString getArch(); QString getSerialKeyFilePath(); QString notifyActivation(const QString& identity); - QString notifyUpgrade (QString const& version, QString const& serialKey); + QString notifyUpdate (QString const& fromVersion, + QString const& toVersion, + QString const& serialKey); QString run(const QStringList& args, const QString& input = ""); }; diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp index 15f799336..97a1826a7 100644 --- a/src/gui/src/LicenseManager.cpp +++ b/src/gui/src/LicenseManager.cpp @@ -72,8 +72,21 @@ LicenseManager::setSerialKey(QString serialKeyString, bool acceptExpired) } void -LicenseManager::notifyUpdate(QString version) { +LicenseManager::notifyUpdate(QString fromVersion, QString toVersion) { + ActivationNotifier* notifier = new ActivationNotifier(); + notifier->setUpgradeInfo (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, "notifyUpgrade", + Qt::QueuedConnection); } Edition diff --git a/src/gui/src/LicenseManager.h b/src/gui/src/LicenseManager.h index 4d235369e..4592b28eb 100644 --- a/src/gui/src/LicenseManager.h +++ b/src/gui/src/LicenseManager.h @@ -36,7 +36,7 @@ class LicenseManager: public QObject QString activeEditionName() const; SerialKey serialKey() const; void skipActivation(); - void notifyUpdate(QString version); + void notifyUpdate(QString fromVersion, QString toVersion); static QString getEditionName(Edition edition, bool trial = false); private: diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 59808220a..19a1dfa21 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -157,9 +157,12 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig, setWindowTitle (m_LicenseManager->activeEditionName()); m_LicenseManager->refresh(); + QString lastVersion = m_AppConfig->lastVersion(); QString currentVersion = m_VersionChecker.getVersion(); - if (m_AppConfig->lastVersion() != currentVersion) { + if (lastVersion != currentVersion) { m_AppConfig->setLastVersion (currentVersion); + m_AppConfig->saveSettings(); + m_LicenseManager->notifyUpdate (lastVersion, currentVersion); } } diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 434ae6845..22a871b80 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -146,11 +146,12 @@ ToolApp::notifyUpgrade() std::vector parts = synergy::string::splitString(data, ':'); size_t count = parts.size(); - if (count == 2) { + if (count == 3) { std::stringstream ss; ss << JSON_URL << "notify/upgraded/"; - ss << "?version=" << parts[0]; - ss << "&serial=" << parts[1]; + ss << "?from=" << parts[0]; + ss << "&to=" << parts[1]; + ss << "&serial=" << parts[2]; std::cout << ARCH->internet().get(ss.str()) << std::endl; } From 73685c3d923368841b71a154e8ebc68c28922b9d Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 28 Oct 2016 14:48:01 +0100 Subject: [PATCH 188/241] #5707 Tweak notify url for upgrades --- src/lib/synergy/ToolApp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 22a871b80..917894efc 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -148,7 +148,7 @@ ToolApp::notifyUpgrade() if (count == 3) { std::stringstream ss; - ss << JSON_URL << "notify/upgraded/"; + ss << JSON_URL << "notify/upgrade"; ss << "?from=" << parts[0]; ss << "&to=" << parts[1]; ss << "&serial=" << parts[2]; From fa7daa48f7f44820f727584e641d53992dd1ed15 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 28 Oct 2016 14:48:36 +0100 Subject: [PATCH 189/241] Revert "Update buildbot to Qt 4.8.7" This reverts commit b7e0473cb4e582015aca422a3b83f0a0023d9297. --- src/gui/src/MainWindow.cpp | 1 + src/gui/src/ZeroconfService.h | 1 - src/setup/win32/Include.wxi | 2 +- src/setup/win32/Product.wxs | 3 +-- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 19a1dfa21..f32ebdc8a 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -51,6 +51,7 @@ #endif #if defined(Q_OS_WIN) +#define _WIN32_WINNT 0x0501 #define WIN32_LEAN_AND_MEAN #include #endif diff --git a/src/gui/src/ZeroconfService.h b/src/gui/src/ZeroconfService.h index fde78201c..8f9aa6db0 100644 --- a/src/gui/src/ZeroconfService.h +++ b/src/gui/src/ZeroconfService.h @@ -21,7 +21,6 @@ #include "ZeroconfRecord.h" #include -#include typedef int32_t DNSServiceErrorType; diff --git a/src/setup/win32/Include.wxi b/src/setup/win32/Include.wxi index 0961390bb..62f92ac7c 100644 --- a/src/setup/win32/Include.wxi +++ b/src/setup/win32/Include.wxi @@ -7,7 +7,7 @@ - + diff --git a/src/setup/win32/Product.wxs b/src/setup/win32/Product.wxs index 51a9976bb..03a309e81 100644 --- a/src/setup/win32/Product.wxs +++ b/src/setup/win32/Product.wxs @@ -118,8 +118,7 @@ - - + From af9037276c718c6b224fe6695422703f19e87132 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 28 Oct 2016 14:54:38 +0100 Subject: [PATCH 190/241] #5707 Prefer 'update' over 'upgrade' --- src/gui/src/ActivationNotifier.cpp | 4 ++-- src/gui/src/ActivationNotifier.h | 4 ++-- src/gui/src/CoreInterface.cpp | 2 +- src/gui/src/LicenseManager.cpp | 4 ++-- src/lib/synergy/ArgParser.cpp | 4 ++-- src/lib/synergy/ToolApp.cpp | 6 +++--- src/lib/synergy/ToolApp.h | 2 +- src/lib/synergy/ToolArgs.cpp | 2 +- src/lib/synergy/ToolArgs.h | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/gui/src/ActivationNotifier.cpp b/src/gui/src/ActivationNotifier.cpp index e8159a34d..0786d90cb 100644 --- a/src/gui/src/ActivationNotifier.cpp +++ b/src/gui/src/ActivationNotifier.cpp @@ -29,7 +29,7 @@ void ActivationNotifier::setIdentity(QString identity) m_Identity = identity; } -void ActivationNotifier::setUpgradeInfo(QString const& fromVersion, +void ActivationNotifier::setUpdateInfo(QString const& fromVersion, QString const& toVersion, QString const& serialKey) { @@ -49,7 +49,7 @@ void ActivationNotifier::notify() } } -void ActivationNotifier::notifyUpgrade() +void ActivationNotifier::notifyUpdate() { try { CoreInterface coreInterface; diff --git a/src/gui/src/ActivationNotifier.h b/src/gui/src/ActivationNotifier.h index fda336a4e..2ac216c7f 100644 --- a/src/gui/src/ActivationNotifier.h +++ b/src/gui/src/ActivationNotifier.h @@ -27,12 +27,12 @@ Q_OBJECT explicit ActivationNotifier(QObject *parent = 0); void setIdentity(QString identity); - void setUpgradeInfo(QString const& fromVersion, + void setUpdateInfo(QString const& fromVersion, QString const& toVersion, QString const& serialKey); public slots: void notify(); - void notifyUpgrade(); + void notifyUpdate(); signals: void finished(); diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index aa8d14a3c..b3c11f0ae 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -65,7 +65,7 @@ QString CoreInterface::getSerialKeyFilePath() QString CoreInterface::notifyUpdate (QString const& fromVersion, QString const& toVersion, QString const& serialKey) { - QStringList args("--notify-upgrade"); + QStringList args("--notify-update"); QString input(fromVersion + ":" + toVersion + ":" + serialKey); return run(args, input); } diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp index 97a1826a7..f11531a01 100644 --- a/src/gui/src/LicenseManager.cpp +++ b/src/gui/src/LicenseManager.cpp @@ -74,7 +74,7 @@ LicenseManager::setSerialKey(QString serialKeyString, bool acceptExpired) void LicenseManager::notifyUpdate(QString fromVersion, QString toVersion) { ActivationNotifier* notifier = new ActivationNotifier(); - notifier->setUpgradeInfo (fromVersion, toVersion, + notifier->setUpdateInfo (fromVersion, toVersion, QString::fromStdString(m_serialKey.toString())); QThread* thread = new QThread(); @@ -85,7 +85,7 @@ LicenseManager::notifyUpdate(QString fromVersion, QString toVersion) { notifier->moveToThread(thread); thread->start(); - QMetaObject::invokeMethod(notifier, "notifyUpgrade", + QMetaObject::invokeMethod(notifier, "notifyUpdate", Qt::QueuedConnection); } diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 828e4c06d..6693ebf00 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -208,8 +208,8 @@ ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv) args.m_notifyActivation = true; return true; } - else if (isArg(i, argc, argv, NULL, "--notify-upgrade", 0)) { - args.m_notifyUpgrade = true; + else if (isArg(i, argc, argv, NULL, "--notify-update", 0)) { + args.m_notifyUpdate = true; return true; } else { diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 917894efc..653012ff8 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -80,8 +80,8 @@ ToolApp::run(int argc, char** argv) else if (m_args.m_getArch) { std::cout << ARCH->getPlatformName() << std::endl; } - else if (m_args.m_notifyUpgrade) { - notifyUpgrade(); + else if (m_args.m_notifyUpdate) { + notifyUpdate(); } else if (m_args.m_notifyActivation) { notifyActivation(); @@ -138,7 +138,7 @@ ToolApp::loginAuth() } void -ToolApp::notifyUpgrade() +ToolApp::notifyUpdate() { String data; std::cin >> data; diff --git a/src/lib/synergy/ToolApp.h b/src/lib/synergy/ToolApp.h index 0f918827d..771b298fe 100644 --- a/src/lib/synergy/ToolApp.h +++ b/src/lib/synergy/ToolApp.h @@ -30,7 +30,7 @@ class ToolApp : public MinimalApp private: void loginAuth(); void notifyActivation(); - void notifyUpgrade(); + void notifyUpdate(); private: ToolArgs m_args; diff --git a/src/lib/synergy/ToolArgs.cpp b/src/lib/synergy/ToolArgs.cpp index 4884696f8..2685c1df5 100644 --- a/src/lib/synergy/ToolArgs.cpp +++ b/src/lib/synergy/ToolArgs.cpp @@ -24,6 +24,6 @@ ToolArgs::ToolArgs() : m_getProfileDir(false), m_getArch(false), m_notifyActivation(false), - m_notifyUpgrade(false) + m_notifyUpdate(false) { } diff --git a/src/lib/synergy/ToolArgs.h b/src/lib/synergy/ToolArgs.h index 4a620a9bf..4619efc1e 100644 --- a/src/lib/synergy/ToolArgs.h +++ b/src/lib/synergy/ToolArgs.h @@ -30,5 +30,5 @@ class ToolArgs { bool m_getProfileDir; bool m_getArch; bool m_notifyActivation; - bool m_notifyUpgrade; + bool m_notifyUpdate; }; From af62174b5937932d282837ceb724de6846f5ec2d Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 28 Oct 2016 15:58:59 +0100 Subject: [PATCH 191/241] #5707 Only notify activation on user action --- src/gui/src/ActivationDialog.cpp | 1 + src/gui/src/LicenseManager.cpp | 1 - src/gui/src/LicenseManager.h | 2 -- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 6abbc938e..253ec3ee0 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -90,6 +90,7 @@ void ActivationDialog::accept() 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) { diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp index f11531a01..a6af21d89 100644 --- a/src/gui/src/LicenseManager.cpp +++ b/src/gui/src/LicenseManager.cpp @@ -45,7 +45,6 @@ LicenseManager::setSerialKey(QString serialKeyString, bool acceptExpired) using std::swap; swap (serialKey, m_serialKey); m_AppConfig->setSerialKey(serialKeyString); - notifyActivation("serial:" + serialKeyString); emit serialKeyChanged(m_serialKey); if (serialKey.isTrial()) { diff --git a/src/gui/src/LicenseManager.h b/src/gui/src/LicenseManager.h index 4592b28eb..2dc89cd8d 100644 --- a/src/gui/src/LicenseManager.h +++ b/src/gui/src/LicenseManager.h @@ -38,8 +38,6 @@ class LicenseManager: public QObject void skipActivation(); void notifyUpdate(QString fromVersion, QString toVersion); static QString getEditionName(Edition edition, bool trial = false); - -private: void notifyActivation(QString identity); private: From ef9842c81916cc5b5a2ab40f35bfc397823905c4 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 28 Oct 2016 16:01:57 +0100 Subject: [PATCH 192/241] #5707 Change update notification url --- src/lib/synergy/ToolApp.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 653012ff8..3dcfd05d8 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -146,17 +146,17 @@ ToolApp::notifyUpdate() std::vector parts = synergy::string::splitString(data, ':'); size_t count = parts.size(); - if (count == 3) { - std::stringstream ss; - ss << JSON_URL << "notify/upgrade"; - ss << "?from=" << parts[0]; - ss << "&to=" << parts[1]; - ss << "&serial=" << parts[2]; + if (count == 3) { + std::stringstream ss; + ss << JSON_URL << "notify/update"; + ss << "?from=" << parts[0]; + ss << "&to=" << parts[1]; + ss << "&serial=" << parts[2]; std::cout << ARCH->internet().get(ss.str()) << std::endl; } else { - throw XSynergy("Invalid upgrade data."); + throw XSynergy("Invalid update data."); } } From 3e9815dfddc8c83522c6caaaef6d88ec5f6dfe9f Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 28 Oct 2016 16:34:35 +0100 Subject: [PATCH 193/241] #5707 Add newline to update notification string --- src/gui/src/CoreInterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index b3c11f0ae..e560fc455 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -67,6 +67,7 @@ QString CoreInterface::notifyUpdate (QString const& fromVersion, QString const& serialKey) { QStringList args("--notify-update"); QString input(fromVersion + ":" + toVersion + ":" + serialKey); + input.append("\n"); return run(args, input); } From 2f2dd7742f79bb60d943cb4ad6d9bfdf79873df1 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 28 Oct 2016 17:43:08 +0100 Subject: [PATCH 194/241] #5707 Don't send update notifications for new users --- src/gui/src/LicenseManager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp index a6af21d89..221fd16d8 100644 --- a/src/gui/src/LicenseManager.cpp +++ b/src/gui/src/LicenseManager.cpp @@ -72,6 +72,11 @@ LicenseManager::setSerialKey(QString serialKeyString, bool acceptExpired) void LicenseManager::notifyUpdate(QString fromVersion, QString toVersion) { + if ((fromVersion == "Unknown") + && (m_serialKey == SerialKey(kUnregistered))) { + return; + } + ActivationNotifier* notifier = new ActivationNotifier(); notifier->setUpdateInfo (fromVersion, toVersion, QString::fromStdString(m_serialKey.toString())); From c9bb421fb5deb056166e728c820ae547f5fa0c2f Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 28 Oct 2016 17:48:20 +0100 Subject: [PATCH 195/241] Version to v1.8.5-rc2 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d3bf1963..e888229aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 5) -set(VERSION_STAGE rc1) +set(VERSION_STAGE rc2) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From 771d2a419b27e28b3fa3e839eaa1da34ec4c0cb1 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 31 Oct 2016 12:38:16 +0000 Subject: [PATCH 196/241] Fix 1.8.4 changelog --- ChangeLog | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index 19d10d500..a496d276a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,16 +1,15 @@ v1.8.4-stable ============= - -Bug #4041 UHD/4K DPI scaling broken on Windows servers -Bug #4420 When XRandR adds a screen, it is inaccessible -Bug #5603 Activation notification depends on existence of /etc/os-release -Bug #5624 Update notification sometimes requests a downgrade -Bug #5329 Current date is shown for build date in the about dialog -Bug #5640 Synergy branding is inconsistent across platforms -Enhancement #5617 Remove redundant plugin infrastructure -Enhancement #5627 Move SSL certificate generation to main window -Enhancement #5628 Move SSL implementation into core binary -Enhancement #5629 Move activation from wizard into new dialog window +Bug #5183 - Slowly moving the cursor has no effect on high DPI clients +Bug #4041 - UHD/4K DPI scaling broken on Windows servers +Bug #4420 - When XRandR adds a screen, it is inaccessible +Bug #5603 - Activation notification depends on existence of /etc/os-release +Bug #5624 - Update notification sometimes requests a downgrade +Bug #5329 - Current date is shown for build date in the about dialog +Enhancement #5617 - Remove redundant plugin infrastructure +Enhancement #5627 - Move SSL certificate generation to main window +Enhancement #5628 - Move SSL implementation into core binary +Enhancement #5629 - Move activation from wizard into new dialog window v1.8.3-stable ============= From 20a34e5abf6e77692de27024fdf2bca98b29fa0d Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 31 Oct 2016 12:45:20 +0000 Subject: [PATCH 197/241] Update changelog for 1.8.5-stable --- ChangeLog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ChangeLog b/ChangeLog index a496d276a..477426ca4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +v1.8.5-stable +============= +Bug #5680 - Server crashes when disconnecting SSL clients +Bug #5626 - Build fails using Xcode 8 and macOS SDK 10.12 +Feature #5657 - Trial version support +Feature #5707 - User upgrade statistics + v1.8.4-stable ============= Bug #5183 - Slowly moving the cursor has no effect on high DPI clients From a18eba7520f301fac921c178aa0c75d7768ef176 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 31 Oct 2016 12:45:42 +0000 Subject: [PATCH 198/241] Version to 1.8.5-stable --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e888229aa..45c8d9d6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 5) -set(VERSION_STAGE rc2) +set(VERSION_STAGE stable) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From 340ee43d90a87ba35375da106315a69c8baf0cfe Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 31 Oct 2016 17:40:54 +0000 Subject: [PATCH 199/241] #5699 Add Mac deploy argument in configure command If deploy target is not specified, it would use the specified sdk version --- ext/toolchain/commands1.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 9c28ab75f..8e7910f92 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -41,7 +41,7 @@ class Toolchain: cmd_opt_dict = { 'about' : ['', []], 'setup' : ['g:', ['generator=']], - 'configure' : ['g:dr', ['generator=', 'debug', 'release', 'mac-sdk=', 'mac-identity=']], + 'configure' : ['g:dr', ['generator=', 'debug', 'release', 'mac-sdk=', 'mac-deploy=', 'mac-identity=']], 'build' : ['dr', ['debug', 'release']], 'clean' : ['dr', ['debug', 'release']], 'update' : ['', []], @@ -244,6 +244,9 @@ class InternalCommands: # by default, unknown macSdk = None + # by default, unknown + macDeploy = None + # by default, unknown macIdentity = None @@ -365,7 +368,7 @@ def configure(self, target='', extraArgs=''): # ensure latest setup and do not ask config for generator (only fall # back to prompt if not specified as arg) self.ensure_setup_latest() - + if sys.platform == "darwin": config = self.getConfig() @@ -374,6 +377,11 @@ def configure(self, target='', extraArgs=''): elif config.has_option("hm", "macSdk"): self.macSdk = config.get('hm', 'macSdk') + if self.macDeploy: + config.set('hm', 'macDeploy', self.macDeploy) + elif config.has_option("hm", "macDeploy"): + self.macSdk = config.get('hm', 'macDeploy') + if self.macIdentity: config.set('hm', 'macIdentity', self.macIdentity) elif config.has_option("hm", "macIdentity"): @@ -383,7 +391,10 @@ def configure(self, target='', extraArgs=''): if not self.macSdk: raise Exception("Arg missing: --mac-sdk "); - + + if not self.macDeploy: + self.macDeploy = self.macSdk + if not self.macIdentity: raise Exception("Arg missing: --mac-identity "); @@ -438,7 +449,7 @@ def configureCore(self, target="", extraArgs=""): if generator.cmakeName.find('Unix Makefiles') == -1: sdkDir = self.getMacSdkDir() cmake_args += " -DCMAKE_OSX_SYSROOT=" + sdkDir - cmake_args += " -DCMAKE_OSX_DEPLOYMENT_TARGET=" + self.macSdk + cmake_args += " -DCMAKE_OSX_DEPLOYMENT_TARGET=" + self.macDeploy cmake_args += " -DOSX_TARGET_MAJOR=" + macSdkMatch.group(1) cmake_args += " -DOSX_TARGET_MINOR=" + macSdkMatch.group(2) @@ -1898,6 +1909,8 @@ def __init__(self, argv, opts, args, verbose): self.qtDir = a elif o == '--mac-sdk': self.ic.macSdk = a + elif o == '--mac-deploy': + self.ic.macDeploy = a elif o == '--mac-identity': self.ic.macIdentity = a From 7ce69059351a165e192d717741c267809b154dab Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 1 Nov 2016 15:22:33 +0000 Subject: [PATCH 200/241] Version to 1.8.6-rc1 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 45c8d9d6a..5b294eee6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,8 +17,8 @@ # Version number for Synergy set(VERSION_MAJOR 1) set(VERSION_MINOR 8) -set(VERSION_REV 5) -set(VERSION_STAGE stable) +set(VERSION_REV 6) +set(VERSION_STAGE rc1) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From 1a7920f80d86295b7e23017537250fc550632eb5 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 1 Nov 2016 17:29:25 +0000 Subject: [PATCH 201/241] #5722 Make LicenseManager accept SerialKey object instead of string --- src/gui/src/ActivationDialog.cpp | 3 ++- src/gui/src/LicenseManager.cpp | 14 ++++++++++---- src/gui/src/LicenseManager.h | 3 ++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp index 253ec3ee0..6035dfa88 100644 --- a/src/gui/src/ActivationDialog.cpp +++ b/src/gui/src/ActivationDialog.cpp @@ -71,7 +71,8 @@ void ActivationDialog::accept() std::pair result; try { - QString serialKey = ui->m_pTextEditSerialKey->toPlainText().trimmed(); + SerialKey serialKey (ui->m_pTextEditSerialKey->toPlainText(). + trimmed().toStdString()); result = m_LicenseManager->setSerialKey(serialKey); } catch (std::exception& e) { diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp index 221fd16d8..cd0b6eb92 100644 --- a/src/gui/src/LicenseManager.cpp +++ b/src/gui/src/LicenseManager.cpp @@ -29,11 +29,10 @@ LicenseManager::LicenseManager(AppConfig* appConfig) : } std::pair -LicenseManager::setSerialKey(QString serialKeyString, bool acceptExpired) +LicenseManager::setSerialKey(SerialKey serialKey, bool acceptExpired) { std::pair ret (true, ""); time_t currentTime = ::time(0); - SerialKey serialKey (serialKeyString.toStdString()); if (!acceptExpired && serialKey.isExpired(currentTime)) { ret.first = false; @@ -44,7 +43,8 @@ LicenseManager::setSerialKey(QString serialKeyString, bool acceptExpired) if (serialKey != m_serialKey) { using std::swap; swap (serialKey, m_serialKey); - m_AppConfig->setSerialKey(serialKeyString); + m_AppConfig->setSerialKey(QString::fromStdString + (serialKey.toString())); emit serialKeyChanged(m_serialKey); if (serialKey.isTrial()) { @@ -114,7 +114,13 @@ LicenseManager::serialKey() const void LicenseManager::refresh() { if (!m_AppConfig->serialKey().isEmpty()) { - setSerialKey(m_AppConfig->serialKey(), true); + try { + SerialKey serialKey (m_AppConfig->serialKey().toStdString()); + setSerialKey(serialKey, true); + } catch (...) { + m_AppConfig->setSerialKey(""); + m_AppConfig->saveSettings(); + } } if (m_serialKey.isExpired(::time(0))) { emit endTrial(true); diff --git a/src/gui/src/LicenseManager.h b/src/gui/src/LicenseManager.h index 2dc89cd8d..7ece4e23c 100644 --- a/src/gui/src/LicenseManager.h +++ b/src/gui/src/LicenseManager.h @@ -30,7 +30,8 @@ class LicenseManager: public QObject public: LicenseManager(AppConfig* appConfig); - std::pair setSerialKey(QString serialKey, bool acceptExpired = false); + std::pair setSerialKey(SerialKey serialKey, + bool acceptExpired = false); void refresh(); Edition activeEdition() const; QString activeEditionName() const; From 24a548273edbcaceddba1d3e2085049189b65c07 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 2 Nov 2016 11:28:04 +0000 Subject: [PATCH 202/241] #5722 Trim serial keys already stored --- src/gui/src/AppConfig.cpp | 2 +- src/gui/src/LicenseManager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 06f9f43da..e6d15e63d 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -161,7 +161,7 @@ void AppConfig::loadSettings() m_ActivateEmail = settings().value("activateEmail", "").toString(); m_CryptoEnabled = settings().value("cryptoEnabled", true).toBool(); m_AutoHide = settings().value("autoHide", false).toBool(); - m_Serialkey = settings().value("serialKey", "").toString(); + m_Serialkey = settings().value("serialKey", "").toString().trimmed(); m_lastVersion = settings().value("lastVersion", "Unknown").toString(); m_LastExpiringWarningTime = settings().value("lastExpiringWarningTime", 0).toInt(); m_ActivationHasRun = settings().value("activationHasRun", false).toBool(); diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp index cd0b6eb92..335dd821b 100644 --- a/src/gui/src/LicenseManager.cpp +++ b/src/gui/src/LicenseManager.cpp @@ -118,7 +118,7 @@ void LicenseManager::refresh() SerialKey serialKey (m_AppConfig->serialKey().toStdString()); setSerialKey(serialKey, true); } catch (...) { - m_AppConfig->setSerialKey(""); + m_AppConfig->clearSerialKey(); m_AppConfig->saveSettings(); } } From 5cccac360c17b59d7613e3e32c43dca88e4162a6 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 2 Nov 2016 11:55:39 +0000 Subject: [PATCH 203/241] #5699 Add deploy target to GUI configure --- ext/toolchain/commands1.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 8e7910f92..ceadb1fe1 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -509,8 +509,8 @@ def configureGui(self, target="", extraArgs=""): sdkDir = self.getMacSdkDir() shortForm = "macosx" + self.macSdk version = str(major) + "." + str(minor) - - qmake_cmd_string += " QMAKE_MACOSX_DEPLOYMENT_TARGET=" + version + + qmake_cmd_string += " QMAKE_MACOSX_DEPLOYMENT_TARGET=" + self.macDeploy (qMajor, qMinor, qRev) = self.getQmakeVersion() if qMajor <= 4: From b8233fc1465a958058658124809ad17fb0d13939 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 3 Nov 2016 14:31:27 +0000 Subject: [PATCH 204/241] #5186 Add deprecated arguments warnings --- src/lib/synergy/ArgParser.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index b3921e120..83cecdf88 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -319,6 +319,26 @@ ArgParser::parseDeprecatedArgs(int argc, const char* const* argv, int& i) i++; return true; } + else if (isArg(i, argc, argv, NULL, "--res-w")) { + LOG((CLOG_NOTE "--res-w is deprecated")); + i++; + return true; + } + else if (isArg(i, argc, argv, NULL, "--res-h")) { + LOG((CLOG_NOTE "--res-h is deprecated")); + i++; + return true; + } + else if (isArg(i, argc, argv, NULL, "--prm-wc")) { + LOG((CLOG_NOTE "--prm-wc is deprecated")); + i++; + return true; + } + else if (isArg(i, argc, argv, NULL, "--prm-hc")) { + LOG((CLOG_NOTE "--prm-hc is deprecated")); + i++; + return true; + } return false; } From f2f4b05c6f7a45395d04b8c0d5d3e00f20a7f543 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 3 Nov 2016 14:55:36 +0000 Subject: [PATCH 205/241] #5699 Only put MacOSX version number in filename when deploying for exact SDK --- ext/toolchain/commands1.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index ceadb1fe1..b5a47217c 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -1854,7 +1854,10 @@ def getMacPackageName(self): # version is major and minor with no dots (e.g. 106) version = str(major) + str(minor) - return "MacOSX%s-%s" % (version, arch) + if (self.macDeploy == self.macSdk): + return "MacOSX%s-%s" % (version, arch) + else: + return "MacOSX-%s" % arch def reset(self): if os.path.exists('build'): From e6ca9e417dc9b54a042345b03a114f9fac330239 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 3 Nov 2016 16:42:38 +0000 Subject: [PATCH 206/241] #5699 load deploy target on load config so dist command is aware of it --- ext/toolchain/commands1.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index b5a47217c..cbfb31c7d 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -667,6 +667,9 @@ def loadConfig(self): if config.has_option("hm", "macSdk"): self.macSdk = config.get("hm", "macSdk") + + if config.has_option("hm", "macDeploy"): + self.macDeploy = config.get("hm", "macDeploy") if config.has_option("hm", "macIdentity"): self.macIdentity = config.get("hm", "macIdentity") From e8145aa779feae145aaaf3b4ff5b657f71a91a83 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 22 Nov 2016 15:19:58 +0000 Subject: [PATCH 207/241] #5592 Post keyboard events to lower level HID client --- src/lib/platform/OSXKeyState.cpp | 168 ++++++++++++++++++++++++--------------- src/lib/platform/OSXKeyState.h | 6 ++ 2 files changed, 109 insertions(+), 65 deletions(-) diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 2071621bf..8ab0bc89a 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -23,6 +23,7 @@ #include "base/Log.h" #include +#include // Note that some virtual keys codes appear more than once. The // first instance of a virtual key code maps to the KeyID that we @@ -469,6 +470,105 @@ OSXKeyState::getKeyMap(synergy::KeyMap& keyMap) } } +static io_connect_t getEventDriver(void) +{ + static mach_port_t sEventDrvrRef = 0; + mach_port_t masterPort, service, iter; + kern_return_t kr; + + if (!sEventDrvrRef) { + // Get master device port + kr = IOMasterPort(bootstrap_port, &masterPort); + assert(KERN_SUCCESS == kr); + + kr = IOServiceGetMatchingServices(masterPort, + IOServiceMatching(kIOHIDSystemClass), &iter); + assert(KERN_SUCCESS == kr); + + service = IOIteratorNext(iter); + assert(service); + + kr = IOServiceOpen(service, mach_task_self(), + kIOHIDParamConnectType, &sEventDrvrRef); + assert(KERN_SUCCESS == kr); + + IOObjectRelease(service); + IOObjectRelease(iter); + } + + return sEventDrvrRef; +} + +void +OSXKeyState::postHIDVirtualKey(const UInt8 virtualKeyCode, + const bool postDown) +{ + static UInt32 modifiers = 0; + + NXEventData event; + IOGPoint loc = { 0, 0 }; + UInt32 modifiersDelta = 0; + + bzero(&event, sizeof(NXEventData)); + + switch (virtualKeyCode) + { + case s_shiftVK: + case s_superVK: + case s_altVK: + case s_controlVK: + case s_capsLockVK: + switch (virtualKeyCode) + { + case s_shiftVK: + modifiersDelta = NX_SHIFTMASK; + m_shiftPressed = postDown; + break; + case s_superVK: + modifiersDelta = NX_COMMANDMASK; + m_superPressed = postDown; + break; + case s_altVK: + modifiersDelta = NX_ALTERNATEMASK; + m_altPressed = postDown; + break; + case s_controlVK: + modifiersDelta = NX_CONTROLMASK; + m_controlPressed = postDown; + break; + case s_capsLockVK: + modifiersDelta = NX_ALPHASHIFTMASK; + m_capsPressed = postDown; + break; + } + + // update the modifier bit + if (postDown) { + modifiers |= modifiersDelta; + } + else { + modifiers &= ~modifiersDelta; + } + + kern_return_t kr; + kr = IOHIDPostEvent(getEventDriver(), NX_FLAGSCHANGED, loc, + &event, kNXEventDataVersion, modifiers, true); + assert(KERN_SUCCESS == kr); + break; + + default: + event.key.repeat = false; + event.key.keyCode = virtualKeyCode; + event.key.origCharSet = event.key.charSet = NX_ASCIISET; + event.key.origCharCode = event.key.charCode = 0; + kr = IOHIDPostEvent(getEventDriver(), + postDown ? NX_KEYDOWN : NX_KEYUP, + loc, &event, kNXEventDataVersion, 0, false); + assert(KERN_SUCCESS == kr); + break; + } +} + void OSXKeyState::fakeKey(const Keystroke& keystroke) { @@ -477,76 +577,14 @@ OSXKeyState::fakeKey(const Keystroke& keystroke) KeyButton button = keystroke.m_data.m_button.m_button; bool keyDown = keystroke.m_data.m_button.m_press; - UInt32 client = keystroke.m_data.m_button.m_client; - CGEventSourceRef source = 0; CGKeyCode virtualKey = mapKeyButtonToVirtualKey(button); LOG((CLOG_DEBUG1 - " button=0x%04x virtualKey=0x%04x keyDown=%s client=0x%04x", - button, virtualKey, keyDown ? "down" : "up", client)); + " button=0x%04x virtualKey=0x%04x keyDown=%s", + button, virtualKey, keyDown ? "down" : "up")); - CGEventRef ref = CGEventCreateKeyboardEvent( - source, virtualKey, keyDown); - - if (ref == NULL) { - LOG((CLOG_CRIT "unable to create keyboard event for keystroke")); - return; - } - - // persist modifier state. - if (virtualKey == s_shiftVK) { - m_shiftPressed = keyDown; - } - - if (virtualKey == s_controlVK) { - m_controlPressed = keyDown; - } - - if (virtualKey == s_altVK) { - m_altPressed = keyDown; - } - - if (virtualKey == s_superVK) { - m_superPressed = keyDown; - } - - if (virtualKey == s_capsLockVK) { - m_capsPressed = keyDown; - } + postHIDVirtualKey(virtualKey, keyDown); - // set the event flags for special keys - // http://tinyurl.com/pxl742y - CGEventFlags modifiers = 0; - - if (m_shiftPressed) { - modifiers |= kCGEventFlagMaskShift; - } - - if (m_controlPressed) { - modifiers |= kCGEventFlagMaskControl; - } - - if (m_altPressed) { - modifiers |= kCGEventFlagMaskAlternate; - } - - if (m_superPressed) { - modifiers |= kCGEventFlagMaskCommand; - } - - if (m_capsPressed) { - modifiers |= kCGEventFlagMaskAlphaShift; - } - - CGEventSetFlags(ref, modifiers); - CGEventPost(kCGHIDEventTap, ref); - CFRelease(ref); - - // add a delay if client data isn't zero - // FIXME -- why? - if (client != 0) { - ARCH->sleep(0.01); - } break; } diff --git a/src/lib/platform/OSXKeyState.h b/src/lib/platform/OSXKeyState.h index 00dbb5518..a3e16315f 100644 --- a/src/lib/platform/OSXKeyState.h +++ b/src/lib/platform/OSXKeyState.h @@ -149,6 +149,12 @@ class OSXKeyState : public KeyState { static UInt32 mapKeyButtonToVirtualKey(KeyButton keyButton); void init(); + + // Post a key event to HID manager. It posts an event to HID client, a + // much lower level than window manager which's the target from carbon + // CGEventPost + void postHIDVirtualKey(const UInt8 virtualKeyCode, + const bool postDown); private: // OS X uses a physical key if 0 for the 'A' key. synergy reserves From 00db51cd9361055b9eade3ca352e5fa72e6603a2 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 5 Dec 2016 11:01:53 +0000 Subject: [PATCH 208/241] Ask for logs in issue template --- .github/ISSUE_TEMPLATE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 590f59a59..b1f62c9bf 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -27,4 +27,6 @@ Client: Applesoft Windy OS 10 * Is there a way to work around it? No/Yes, you can... * Does this bug prevent you from using Synergy entirely? Yes/No +Please follow the link below to send us logs from both your server and client sides if it's appropriate. https://github.com/symless/synergy/wiki/Sending-logs + Put anything else you can think of here. From d8cd60f0570d5c42fdaa92bde4480ad8056e1370 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Tue, 6 Dec 2016 11:22:54 +0000 Subject: [PATCH 209/241] Version to 1.8.6-rc2 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b294eee6..9a6685768 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 6) -set(VERSION_STAGE rc1) +set(VERSION_STAGE rc2) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From a49de587381ebe8c739cbadf8daae99bc08965d6 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Tue, 6 Dec 2016 12:03:40 +0000 Subject: [PATCH 210/241] #5752 Correct tab order in settings dialog --- src/gui/res/SettingsDialogBase.ui | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/res/SettingsDialogBase.ui b/src/gui/res/SettingsDialogBase.ui index dca1bdadd..f604cf7a2 100644 --- a/src/gui/res/SettingsDialogBase.ui +++ b/src/gui/res/SettingsDialogBase.ui @@ -330,11 +330,13 @@ m_pLineEditScreenName m_pSpinBoxPort m_pLineEditInterface + m_pComboElevate + m_pCheckBoxAutoHide + m_pCheckBoxEnableCrypto m_pComboLogLevel m_pCheckBoxLogToFile m_pLineEditLogFilename m_pButtonBrowseLog - buttonBox From 72b1ebcdb2175f7d31d68e30ce24391ab272b718 Mon Sep 17 00:00:00 2001 From: Epakai Date: Sat, 22 Oct 2016 02:00:28 -0500 Subject: [PATCH 211/241] spelling error "unknow => unknown" --- src/lib/synergy/ClipboardChunk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/synergy/ClipboardChunk.cpp b/src/lib/synergy/ClipboardChunk.cpp index 25c0e2d86..292826ee6 100644 --- a/src/lib/synergy/ClipboardChunk.cpp +++ b/src/lib/synergy/ClipboardChunk.cpp @@ -118,7 +118,7 @@ ClipboardChunk::assemble(synergy::IStream* stream, return kFinish; } - LOG((CLOG_ERR "clipboard transmission failed: unknow error")); + LOG((CLOG_ERR "clipboard transmission failed: unknown error")); return kError; } From 4297673387bfc1c348c8e5b5a06248847ab56985 Mon Sep 17 00:00:00 2001 From: Epakai Date: Sat, 22 Oct 2016 01:59:37 -0500 Subject: [PATCH 212/241] spelling error "implmented => implemented" --- src/lib/arch/unix/ArchSystemUnix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/arch/unix/ArchSystemUnix.cpp b/src/lib/arch/unix/ArchSystemUnix.cpp index 3eb60cfa8..5bcea6190 100644 --- a/src/lib/arch/unix/ArchSystemUnix.cpp +++ b/src/lib/arch/unix/ArchSystemUnix.cpp @@ -76,5 +76,5 @@ ArchSystemUnix::setting(const std::string&, const std::string&) const std::string ArchSystemUnix::getLibsUsed(void) const { - return "not implmented.\nuse lsof on shell"; + return "not implemented.\nuse lsof on shell"; } From e1dc29799f7a5e7d380a596d5e39cda1b51aabc5 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Tue, 6 Dec 2016 15:18:18 +0000 Subject: [PATCH 213/241] Fix wrong usage example in hm --- ext/toolchain/commands1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index cbfb31c7d..f32ec4835 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -309,7 +309,7 @@ def usage(self): ' genlist Shows the list of available platform generators\n' ' usage Shows the help screen\n' '\n' - 'Example: %s build -g 3' + 'Example: %s conf -g 3' ) % (app, app) def configureAll(self, targets, extraArgs=''): From 74d63df24445cdb95f88f97d8008fb43443bfbc0 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Wed, 7 Dec 2016 17:28:15 +0000 Subject: [PATCH 214/241] Fix incorrect check of return code from dup --- src/lib/arch/unix/ArchDaemonUnix.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/arch/unix/ArchDaemonUnix.cpp b/src/lib/arch/unix/ArchDaemonUnix.cpp index 91933ab4e..f12ee8f14 100644 --- a/src/lib/arch/unix/ArchDaemonUnix.cpp +++ b/src/lib/arch/unix/ArchDaemonUnix.cpp @@ -117,9 +117,11 @@ ArchDaemonUnix::daemonize(const char* name, DaemonFunc func) open("/dev/null", O_RDWR); int dupErr = dup(1); - if (dupErr) + + if (dupErr < 0) { // NB: file logging actually isn't working at this point! LOG((CLOG_ERR "dup error: %i", dupErr)); + } #ifdef __APPLE__ return execSelfNonDaemonized(); From c31f908fb283dadc1a34856796e2d7a9df7bbb77 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Wed, 7 Dec 2016 17:35:52 +0000 Subject: [PATCH 215/241] Fix warning in IPC proxy classes --- src/lib/ipc/IpcClientProxy.cpp | 2 +- src/lib/ipc/IpcServerProxy.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/ipc/IpcClientProxy.cpp b/src/lib/ipc/IpcClientProxy.cpp index 8c912a837..5ede6a054 100644 --- a/src/lib/ipc/IpcClientProxy.cpp +++ b/src/lib/ipc/IpcClientProxy.cpp @@ -146,7 +146,7 @@ IpcClientProxy::send(const IpcMessage& message) switch (message.type()) { case kIpcLogLine: { const IpcLogLineMessage& llm = static_cast(message); - String logLine = llm.logLine(); + const String logLine = llm.logLine(); ProtocolUtil::writef(&m_stream, kIpcMsgLogLine, &logLine); break; } diff --git a/src/lib/ipc/IpcServerProxy.cpp b/src/lib/ipc/IpcServerProxy.cpp index 90e87478c..39379f527 100644 --- a/src/lib/ipc/IpcServerProxy.cpp +++ b/src/lib/ipc/IpcServerProxy.cpp @@ -94,7 +94,7 @@ IpcServerProxy::send(const IpcMessage& message) case kIpcCommand: { const IpcCommandMessage& cm = static_cast(message); - String command = cm.command(); + const String command = cm.command(); ProtocolUtil::writef(&m_stream, kIpcMsgCommand, &command); break; } From 5061f51a66253c261110c43d78386e228431e9c4 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Mon, 12 Dec 2016 16:55:05 +0000 Subject: [PATCH 216/241] Update Changelog --- ChangeLog | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ChangeLog b/ChangeLog index 477426ca4..eb209c696 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +v1.8.6-stable +============= +Bug #5592 - Some keys don't work for macOS Sierra clients +Bug #5186 - Cursor stuck on client when using multi-DPI server +Bug #5722 - Malformed serial key in registry will crash GUI on startup +Bug #5752 - Tab order is incorrect on Settings dialog +Enhancement #5699 - Unified installers on macOS +Feature #4836 - macOS Sierra build + v1.8.5-stable ============= Bug #5680 - Server crashes when disconnecting SSL clients From 2ab21aaa011dfaaf2a14c0d17ab210f9012d0d18 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Mon, 12 Dec 2016 16:55:31 +0000 Subject: [PATCH 217/241] Version to 1.8.6-stable --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a6685768..3492a0c68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 6) -set(VERSION_STAGE rc2) +set(VERSION_STAGE stable) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From b69570ec2c746948f3987370ff35eba4a93293a4 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 28 Dec 2016 15:57:15 +0000 Subject: [PATCH 218/241] #5784 Fix using the wrong serial key --- src/gui/src/LicenseManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/src/LicenseManager.cpp b/src/gui/src/LicenseManager.cpp index 335dd821b..6f761135e 100644 --- a/src/gui/src/LicenseManager.cpp +++ b/src/gui/src/LicenseManager.cpp @@ -44,7 +44,7 @@ LicenseManager::setSerialKey(SerialKey serialKey, bool acceptExpired) using std::swap; swap (serialKey, m_serialKey); m_AppConfig->setSerialKey(QString::fromStdString - (serialKey.toString())); + (m_serialKey.toString())); emit serialKeyChanged(m_serialKey); if (serialKey.isTrial()) { From fd6ea65f1a73acd72770a7156c6dc459808334cc Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Wed, 18 Jan 2017 12:43:17 +0000 Subject: [PATCH 219/241] Version to1.8.7-stable --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3492a0c68..7034dc2a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ # Version number for Synergy set(VERSION_MAJOR 1) set(VERSION_MINOR 8) -set(VERSION_REV 6) +set(VERSION_REV 7) set(VERSION_STAGE stable) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") From 9799e969037727a0bf07450b60a90aa7412658f1 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 19 Jan 2017 13:18:10 +0000 Subject: [PATCH 220/241] Update changelog for v1.8.7 --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index eb209c696..ebb695d4d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +v1.8.7-stable +============= +Bug #5784 - Edition changes when reopening GUI + v1.8.6-stable ============= Bug #5592 - Some keys don't work for macOS Sierra clients From a5140aa1b965a37c68b40ccb990bac6115252c95 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Wed, 21 Dec 2016 17:36:31 -0800 Subject: [PATCH 221/241] Fix typo in compiler flags --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7034dc2a7..328163a1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,7 @@ if (UNIX) # warnings as errors: # we have a problem with people checking in code with warnings. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wno-unused-local-typedef") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wno-unused-local-typedefs") if (NOT APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") From 3bb833b7983caaab2420204bb966fdf0d8490a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=8A=E5=86=B2?= Date: Fri, 7 Nov 2014 10:59:45 +0800 Subject: [PATCH 222/241] #4193 System tray is unavailable on KDE5 --- src/gui/src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index f7be9fbf7..cf4d0c1ee 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -119,8 +119,8 @@ int waitForTray() if (++trayAttempts > TRAY_RETRY_COUNT) { QMessageBox::critical(NULL, "Synergy", - QObject::tr("System tray is unavailable, quitting.")); - return false; + QObject::tr("System tray is unavailable, don't close your window.")); + return true; } QThreadImpl::msleep(TRAY_RETRY_WAIT); From 3d3b7ca8810f283d2134cdb3fc92b77ef0233c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Oliveira?= Date: Tue, 23 Dec 2014 15:15:53 +0000 Subject: [PATCH 223/241] #4288 Remove auto Alt+Printscreen on Windows As per issue https://github.com/synergy/synergy/issues/4288 --- src/lib/platform/MSWindowsKeyState.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/lib/platform/MSWindowsKeyState.cpp b/src/lib/platform/MSWindowsKeyState.cpp index 9c4e729a7..f94eedcd9 100644 --- a/src/lib/platform/MSWindowsKeyState.cpp +++ b/src/lib/platform/MSWindowsKeyState.cpp @@ -1071,11 +1071,6 @@ MSWindowsKeyState::getKeyMap(synergy::KeyMap& keyMap) } } - // add alt+printscreen - if (m_buttonToVK[0x54u] == 0) { - m_buttonToVK[0x54u] = VK_SNAPSHOT; - } - // set virtual key to button table if (GetKeyboardLayout(0) == m_groups[g]) { for (KeyButton i = 0; i < 512; ++i) { From f5944278ededd3d20e9ae7816012ef2167b82e8a Mon Sep 17 00:00:00 2001 From: zbrode Date: Sun, 8 Mar 2015 11:31:34 +1100 Subject: [PATCH 224/241] #4419 We never define _BYTE_ORDER so don't test for it. --- src/micro/uSynergy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/micro/uSynergy.h b/src/micro/uSynergy.h index 44534d248..3d064d513 100644 --- a/src/micro/uSynergy.h +++ b/src/micro/uSynergy.h @@ -46,7 +46,7 @@ extern "C" { #error "Can't define both USYNERGY_LITTLE_ENDIAN and USYNERGY_BIG_ENDIAN" #elif !defined(USYNERGY_LITTLE_ENDIAN) && !defined(USYNERGY_BIG_ENDIAN) /* Attempt to auto detect */ - #if defined(__LITTLE_ENDIAN__) || defined(LITTLE_ENDIAN) || (_BYTE_ORDER == _LITTLE_ENDIAN) + #if defined(__LITTLE_ENDIAN__) || defined(LITTLE_ENDIAN) #define USYNERGY_LITTLE_ENDIAN #elif defined(__BIG_ENDIAN__) || defined(BIG_ENDIAN) || (_BYTE_ORDER == _BIG_ENDIAN) #define USYNERGY_BIG_ENDIAN From 21d4e6a908270fd9f1db9665ebff6a6dcedf2065 Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Wed, 25 Mar 2015 14:27:49 +0100 Subject: [PATCH 225/241] #4477 Only allow mouse buttons 1-10 for XTestFakeButtons call. These are the only valid ones: [~]>for i in `seq 0 1 11`; do echo Testing $i; xte "mouseclick $i"; done Testing 0 X Error of failed request: BadValue (integer parameter out of range for operation) Major opcode of failed request: 132 (XTEST) Minor opcode of failed request: 2 (X_XTestFakeInput) Value in failed request: 0x0 Serial number of failed request: 12 Current serial number in output stream: 15 Testing 1 Testing 2 Testing 3 Testing 4 Testing 5 Testing 6 Testing 7 Testing 8 Testing 9 Testing 10 Testing 11 X Error of failed request: BadValue (integer parameter out of range for operation) Major opcode of failed request: 132 (XTEST) Minor opcode of failed request: 2 (X_XTestFakeInput) Value in failed request: 0xb Serial number of failed request: 12 And there are mice out there where buttons 11+ can be pressed accidentally, terminating the synergy client and often leaving the system in a bad state. --- src/lib/platform/XWindowsScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index c70324705..211bb15b6 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -827,7 +827,7 @@ void XWindowsScreen::fakeMouseButton(ButtonID button, bool press) { const unsigned int xButton = mapButtonToX(button); - if (xButton != 0) { + if (xButton > 0 && xButton < 11) { XTestFakeButtonEvent(m_display, xButton, press ? True : False, CurrentTime); XFlush(m_display); From 8e6bf5323327ff43e10c6faa6822586e037481b4 Mon Sep 17 00:00:00 2001 From: Jee-Yong Um Date: Sun, 5 Apr 2015 13:30:18 +0900 Subject: [PATCH 226/241] #4504 Improve Korean language description --- src/gui/res/lang/Languages.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/res/lang/Languages.xml b/src/gui/res/lang/Languages.xml index 0a7f3d98a..5948f9c6f 100644 --- a/src/gui/res/lang/Languages.xml +++ b/src/gui/res/lang/Languages.xml @@ -36,11 +36,11 @@ - + - + From ba55369d42510fff46b26096f2c77d82dc19db8b Mon Sep 17 00:00:00 2001 From: Andreas Eriksson Date: Mon, 18 May 2015 22:37:32 +0200 Subject: [PATCH 227/241] #3197 Disable regular motion events when using XInput 2 --- src/lib/platform/XWindowsScreen.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index 211bb15b6..5c29499ad 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -160,15 +160,12 @@ XWindowsScreen::XWindowsScreen( // primary/secondary screen only initialization if (m_isPrimary) { - // start watching for events on other windows - selectEvents(m_root); +#ifdef HAVE_XI2 m_xi2detected = detectXI2(); - if (m_xi2detected) { -#ifdef HAVE_XI2 selectXIRawMotion(); -#endif } else +#endif { // start watching for events on other windows selectEvents(m_root); @@ -745,7 +742,7 @@ XWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask) LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", synergy::KeyMap::formatKey(key, mask).c_str(), key, mask)); return 0; } - + LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", synergy::KeyMap::formatKey(key, mask).c_str(), key, mask, id)); return id; } @@ -1300,7 +1297,7 @@ XWindowsScreen::handleSystemEvent(const Event& event, void*) // handle the event ourself switch (xevent->type) { case CreateNotify: - if (m_isPrimary) { + if (m_isPrimary && !m_xi2detected) { // select events on new window selectEvents(xevent->xcreatewindow.window); } From 0eff5a95bea3c0d21c6adfa468f7696853c10692 Mon Sep 17 00:00:00 2001 From: James McMullan Date: Wed, 3 Dec 2014 16:47:05 -0500 Subject: [PATCH 228/241] #3992 macOS: Dragging broken in Unity OSXScreen was not adding mouse movement deltas to mouse events while dragging. Some 3D applications rely on these deltas to implement dragging. Adding the mouse deltas to the mouse event fixes dragging in these applications. Ex: Unity3d --- src/lib/platform/OSXScreen.mm | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/lib/platform/OSXScreen.mm b/src/lib/platform/OSXScreen.mm index 2c2e66d6b..992144e44 100644 --- a/src/lib/platform/OSXScreen.mm +++ b/src/lib/platform/OSXScreen.mm @@ -496,7 +496,23 @@ // Fix for sticky keys CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags(); CGEventSetFlags(event, modifiers); - + + // Set movement deltas to fix issues with certain 3D programs + SInt64 deltaX = pos.x; + deltaX -= m_xCursor; + + SInt64 deltaY = pos.y; + deltaY -= m_yCursor; + + CGEventSetIntegerValueField(event, kCGMouseEventDeltaX, deltaX); + CGEventSetIntegerValueField(event, kCGMouseEventDeltaY, deltaY); + + double deltaFX = deltaX; + double deltaFY = deltaY; + + CGEventSetDoubleValueField(event, kCGMouseEventDeltaX, deltaFX); + CGEventSetDoubleValueField(event, kCGMouseEventDeltaY, deltaFY); + CGEventPost(kCGHIDEventTap, event); CFRelease(event); From b5a81579edda523eb131c2fe803cdc0b839c9419 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 20 Jan 2017 22:57:53 +0000 Subject: [PATCH 229/241] #5809 macOS: Add a version key to Info.plist --- src/gui/res/mac/Info.plist | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/gui/res/mac/Info.plist b/src/gui/res/mac/Info.plist index 5cd23eaf8..7e79c2b6b 100644 --- a/src/gui/res/mac/Info.plist +++ b/src/gui/res/mac/Info.plist @@ -1,20 +1,28 @@ - - + - - CFBundleInfoDictionaryVersion - 6.0 - NSPrincipalClass - NSApplication - CFBundleIconFile - Synergy.icns - CFBundlePackageType - APPL - CFBundleSignature - ???? - CFBundleExecutable - Synergy - CFBundleIdentifier - synergy - - + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + Synergy + CFBundleExecutable + Synergy + CFBundleIconFile + Synergy.icns + CFBundleIdentifier + synergy + + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Synergy + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.8.7 + CFBundleVersion + 1.8.7 + NSHumanReadableCopyright + © 2012-2016, Symless Ltd + + \ No newline at end of file From adf34eba406211564503042d9631bbe304db8d93 Mon Sep 17 00:00:00 2001 From: Mike Hobbs Date: Thu, 29 Dec 2016 11:32:41 -0600 Subject: [PATCH 230/241] #5785 Fix screen switch problem when cursor is in a corner --- src/lib/server/Server.cpp | 74 ++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 30 deletions(-) diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index c980d03b3..fd90f5f3a 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -1766,52 +1766,66 @@ Server::onMouseMovePrimary(SInt32 x, SInt32 y) } // see if we should change screens - EDirection dir; + // when the cursor is in a corner, there may be a screen either + // horizontally or vertically. check both directions. + EDirection dirh = kNoDirection, dirv = kNoDirection; + SInt32 xh = x, yv = y; if (x < ax + zoneSize) { - x -= zoneSize; - dir = kLeft; + xh -= zoneSize; + dirh = kLeft; } else if (x >= ax + aw - zoneSize) { - x += zoneSize; - dir = kRight; + xh += zoneSize; + dirh = kRight; } - else if (y < ay + zoneSize) { - y -= zoneSize; - dir = kTop; + if (y < ay + zoneSize) { + yv -= zoneSize; + dirv = kTop; } else if (y >= ay + ah - zoneSize) { - y += zoneSize; - dir = kBottom; + yv += zoneSize; + dirv = kBottom; } - else { + if (dirh == kNoDirection && dirv == kNoDirection) { // still on local screen noSwitch(x, y); return false; } - // get jump destination - BaseClientProxy* newScreen = mapToNeighbor(m_active, dir, x, y); + // check both horizontally and vertically + EDirection dirs[] = {dirh, dirv}; + SInt32 xs[] = {xh, x}, ys[] = {y, yv}; + for (int i = 0; i < 2; ++i) { + EDirection dir = dirs[i]; + if (dir == kNoDirection) { + continue; + } + x = xs[i], y = ys[i]; + + // get jump destination + BaseClientProxy* newScreen = mapToNeighbor(m_active, dir, x, y); + + // should we switch or not? + if (isSwitchOkay(newScreen, dir, x, y, xc, yc)) { + if (m_args.m_enableDragDrop + && m_screen->isDraggingStarted() + && m_active != newScreen + && m_waitDragInfoThread) { + if (m_sendDragInfoThread == NULL) { + m_sendDragInfoThread = new Thread( + new TMethodJob( + this, + &Server::sendDragInfoThread, newScreen)); + } - // should we switch or not? - if (isSwitchOkay(newScreen, dir, x, y, xc, yc)) { - if (m_args.m_enableDragDrop - && m_screen->isDraggingStarted() - && m_active != newScreen - && m_waitDragInfoThread) { - if (m_sendDragInfoThread == NULL) { - m_sendDragInfoThread = new Thread( - new TMethodJob( - this, - &Server::sendDragInfoThread, newScreen)); + return false; } - return false; + // switch screen + switchScreen(newScreen, x, y, false); + m_waitDragInfoThread = true; + return true; } - - // switch screen - switchScreen(newScreen, x, y, false); - m_waitDragInfoThread = true; - return true; } return false; From 180d3e57d25025252c04d4e87819bb6b4fd370d3 Mon Sep 17 00:00:00 2001 From: Jiwoong Yoo Date: Thu, 26 Jan 2017 06:04:13 +0900 Subject: [PATCH 231/241] #5196 Korean and Japanese keyboards have same key code --- src/lib/platform/MSWindowsKeyState.cpp | 29 +++++++++++++++++----- src/lib/platform/MSWindowsKeyState.h | 2 +- src/lib/synergy/key_types.h | 6 +++-- .../integtests/platform/MSWindowsKeyStateTests.cpp | 21 ++++++++++++++++ 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/lib/platform/MSWindowsKeyState.cpp b/src/lib/platform/MSWindowsKeyState.cpp index f94eedcd9..88ba9f244 100644 --- a/src/lib/platform/MSWindowsKeyState.cpp +++ b/src/lib/platform/MSWindowsKeyState.cpp @@ -61,11 +61,11 @@ const KeyID MSWindowsKeyState::s_virtualKey[] = /* 0x012 */ { kKeyAlt_L }, // VK_MENU /* 0x013 */ { kKeyPause }, // VK_PAUSE /* 0x014 */ { kKeyCapsLock }, // VK_CAPITAL - /* 0x015 */ { kKeyHangulKana }, // VK_HANGUL, VK_KANA + /* 0x015 */ { kKeyKana }, // VK_HANGUL, VK_KANA /* 0x016 */ { kKeyNone }, // undefined /* 0x017 */ { kKeyNone }, // VK_JUNJA /* 0x018 */ { kKeyNone }, // VK_FINAL - /* 0x019 */ { kKeyHanjaKanzi }, // VK_KANJI + /* 0x019 */ { kKeyKanzi }, // VK_HANJA, VK_KANJI /* 0x01a */ { kKeyNone }, // undefined /* 0x01b */ { kKeyEscape }, // VK_ESCAPE /* 0x01c */ { kKeyHenkan }, // VK_CONVERT @@ -318,11 +318,11 @@ const KeyID MSWindowsKeyState::s_virtualKey[] = /* 0x112 */ { kKeyAlt_R }, // VK_MENU /* 0x113 */ { kKeyNone }, // VK_PAUSE /* 0x114 */ { kKeyNone }, // VK_CAPITAL - /* 0x115 */ { kKeyNone }, // VK_KANA - /* 0x116 */ { kKeyNone }, // VK_HANGUL + /* 0x115 */ { kKeyHangul }, // VK_HANGUL + /* 0x116 */ { kKeyNone }, // undefined /* 0x117 */ { kKeyNone }, // VK_JUNJA /* 0x118 */ { kKeyNone }, // VK_FINAL - /* 0x119 */ { kKeyNone }, // VK_KANJI + /* 0x119 */ { kKeyHanja }, // VK_HANJA /* 0x11a */ { kKeyNone }, // undefined /* 0x11b */ { kKeyNone }, // VK_ESCAPE /* 0x11c */ { kKeyNone }, // VK_CONVERT @@ -728,6 +728,10 @@ MSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey, // that so we clear it. active &= ~s_controlAlt; } + if (id == kKeyHangul) { + // If shift-space is used to change input mode, clear shift modifier. + active &= ~KeyModifierShift; + } *maskOut = active; } @@ -1334,8 +1338,20 @@ MSWindowsKeyState::setWindowGroup(SInt32 group) } KeyID -MSWindowsKeyState::getKeyID(UINT virtualKey, KeyButton button) +MSWindowsKeyState::getKeyID(UINT virtualKey, KeyButton button) const { + // Some virtual keycodes have same values. + // VK_HANGUL == VK_KANA, VK_HANJA == NK_KANJI + // which are used to change the input mode of IME. + // But they have different X11 keysym. So we should distinguish them. + if ((LOWORD(m_keyLayout) & 0xffffu) == 0x0412u) { // 0x0412 : Korean Locale ID + if (virtualKey == VK_HANGUL || virtualKey == VK_HANJA) { + // If shift-space is used to change the input mode, + // the extented bit is not set. So add it to get right key id. + button |= 0x100u; + } + } + if ((button & 0x100u) != 0) { virtualKey += 0x100u; } @@ -1387,3 +1403,4 @@ MSWindowsKeyState::addKeyEntry(synergy::KeyMap& keyMap, synergy::KeyMap::KeyItem m_keyToVKMap[item.m_id] = static_cast(item.m_client); } } + diff --git a/src/lib/platform/MSWindowsKeyState.h b/src/lib/platform/MSWindowsKeyState.h index 4313f741a..fcbd5d98b 100644 --- a/src/lib/platform/MSWindowsKeyState.h +++ b/src/lib/platform/MSWindowsKeyState.h @@ -122,7 +122,7 @@ class MSWindowsKeyState : public KeyState { (button should include the extended key bit), or kKeyNone if there is no such key. */ - static KeyID getKeyID(UINT virtualKey, KeyButton button); + KeyID getKeyID(UINT virtualKey, KeyButton button) const; //! Map button to virtual key /*! diff --git a/src/lib/synergy/key_types.h b/src/lib/synergy/key_types.h index ea9387b1c..f45cea242 100644 --- a/src/lib/synergy/key_types.h +++ b/src/lib/synergy/key_types.h @@ -110,10 +110,12 @@ static const KeyID kKeyScrollLock = 0xEF14; static const KeyID kKeySysReq = 0xEF15; static const KeyID kKeyEscape = 0xEF1B; static const KeyID kKeyHenkan = 0xEF23; /* Start/Stop Conversion */ -static const KeyID kKeyHangulKana = 0xEF26; /* Hangul, Kana */ +static const KeyID kKeyKana = 0xEF26; /* Kana */ static const KeyID kKeyHiraganaKatakana = 0xEF27; /* Hiragana/Katakana toggle */ static const KeyID kKeyZenkaku = 0xEF2A; /* Zenkaku/Hankaku */ -static const KeyID kKeyHanjaKanzi = 0xEF2A; /* Hanja, Kanzi */ +static const KeyID kKeyKanzi = 0xEF2A; /* Kanzi */ +static const KeyID kKeyHangul = 0xEF31; /* Hangul */ +static const KeyID kKeyHanja = 0xEF34; /* Hanja */ static const KeyID kKeyDelete = 0xEFFF; /* Delete, rubout */ // cursor control diff --git a/src/test/integtests/platform/MSWindowsKeyStateTests.cpp b/src/test/integtests/platform/MSWindowsKeyStateTests.cpp index c7a4b666c..0affab6e6 100644 --- a/src/test/integtests/platform/MSWindowsKeyStateTests.cpp +++ b/src/test/integtests/platform/MSWindowsKeyStateTests.cpp @@ -121,3 +121,24 @@ TEST_F(MSWindowsKeyStateTests, saveModifiers_noModifiers_savedModifiers0) ASSERT_EQ(0, keyState.getSavedModifiers()); delete desks; } + +TEST_F(MSWindowsKeyStateTests, testKoreanLocale_inputModeKey_resultCorrectKeyID) +{ + NiceMock eventQueue; + MSWindowsDesks* desks = newDesks(&eventQueue); + MockKeyMap keyMap; + MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap); + + keyState.setKeyLayout((HKL)0x00000412u); // for ko-KR local ID + ASSERT_EQ(0xEF31, keyState.getKeyID(0x15u, 0x1f2u)); // VK_HANGUL from Hangul key + ASSERT_EQ(0xEF34, keyState.getKeyID(0x19u, 0x1f1u)); // VK_HANJA from Hanja key + ASSERT_EQ(0xEF31, keyState.getKeyID(0x15u, 0x11du)); // VK_HANGUL from R-Alt key + ASSERT_EQ(0xEF34, keyState.getKeyID(0x19u, 0x138u)); // VK_HANJA from R-Ctrl key + + keyState.setKeyLayout((HKL)0x00000411); // for ja-jp locale ID + ASSERT_EQ(0xEF26, keyState.getKeyID(0x15u, 0x1du)); // VK_KANA + ASSERT_EQ(0xEF2A, keyState.getKeyID(0x19u, 0x38u)); // VK_KANJI + + delete desks; +} + From f35e3e5e06bbe14ec54be4e5d66585b4cef4a47a Mon Sep 17 00:00:00 2001 From: Jiwoong Yoo Date: Thu, 26 Jan 2017 06:07:44 +0900 Subject: [PATCH 232/241] #5578 Virtual key table mapped for inactive IMEs Virtual key table is mapped for all IMEs not just active IME. And this causes the wrong modifier key to be pressed. For example, if you use Korean and Japanese IMEs, pressing the Hangul key makes alt key pressed. So when I press just 'a', client interprets that as 'alt-a'. --- src/lib/platform/MSWindowsKeyState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/MSWindowsKeyState.cpp b/src/lib/platform/MSWindowsKeyState.cpp index 88ba9f244..4e093bd35 100644 --- a/src/lib/platform/MSWindowsKeyState.cpp +++ b/src/lib/platform/MSWindowsKeyState.cpp @@ -1076,7 +1076,7 @@ MSWindowsKeyState::getKeyMap(synergy::KeyMap& keyMap) } // set virtual key to button table - if (GetKeyboardLayout(0) == m_groups[g]) { + if (activeLayout == m_groups[g]) { for (KeyButton i = 0; i < 512; ++i) { if (m_buttonToVK[i] != 0) { if (m_virtualKeyToButton[m_buttonToVK[i]] == 0) { From 1499f7b27c5b4b34644652a7c1dcf0f387a0febc Mon Sep 17 00:00:00 2001 From: Reinder Feenstra Date: Wed, 25 Jan 2017 22:11:13 +0100 Subject: [PATCH 233/241] #5525 Add support for floating point start/end range values --- src/lib/server/Config.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/server/Config.cpp b/src/lib/server/Config.cpp index dac8f58ea..e50cf1d2e 100644 --- a/src/lib/server/Config.cpp +++ b/src/lib/server/Config.cpp @@ -2096,11 +2096,11 @@ ConfigReadContext::parseInterval(const ArgList& args) const } char* end; - long startValue = strtol(args[0].c_str(), &end, 10); + double startValue = strtod(args[0].c_str(), &end); if (end[0] != '\0') { throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args)); } - long endValue = strtol(args[1].c_str(), &end, 10); + double endValue = strtod(args[1].c_str(), &end); if (end[0] != '\0') { throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args)); } From 4b913b5599a2351aabbe5854e5c3207004132724 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 31 Jan 2017 12:17:33 +0000 Subject: [PATCH 234/241] v1.8.8-rc1 --- CMakeLists.txt | 4 ++-- src/gui/res/mac/Info.plist | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 328163a1c..0fc1097b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,8 +17,8 @@ # Version number for Synergy set(VERSION_MAJOR 1) set(VERSION_MINOR 8) -set(VERSION_REV 7) -set(VERSION_STAGE stable) +set(VERSION_REV 8) +set(VERSION_STAGE rc1) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) diff --git a/src/gui/res/mac/Info.plist b/src/gui/res/mac/Info.plist index 7e79c2b6b..b2d87e095 100644 --- a/src/gui/res/mac/Info.plist +++ b/src/gui/res/mac/Info.plist @@ -19,10 +19,10 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.8.7 + 1.8.8 CFBundleVersion - 1.8.7 + 1.8.8 NSHumanReadableCopyright © 2012-2016, Symless Ltd - \ No newline at end of file + From 2643cea67bed960a3ae57ba881cd06f52843fa62 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 6 Feb 2017 10:51:16 +0000 Subject: [PATCH 235/241] #5074 Nuke -Werror. It's dumb right now --- CMakeLists.txt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fc1097b1..635f328b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 8) -set(VERSION_STAGE rc1) +set(VERSION_STAGE rc1) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) @@ -74,11 +74,6 @@ endif() # Depending on the platform, pass in the required defines. if (UNIX) - - # warnings as errors: - # we have a problem with people checking in code with warnings. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wno-unused-local-typedefs") - if (NOT APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") endif() From 2d9ed0d3355007a67599e445947f17d8645a2f1e Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 6 Feb 2017 12:04:52 +0000 Subject: [PATCH 236/241] Updated Changelog --- ChangeLog | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ChangeLog b/ChangeLog index ebb695d4d..5ccc80f6f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +v1.8.8-rc1 +========== +Bug #5196 - Some keys on Korean and Japanese keyboards have the same keycode +Bug #5578 - Pressing Hangul key results in alt+'a' +Bug #5785 - Can't switch screens when cursor is in a corner +Bug #3992 - macOS: Dragging is broken in Unity 3D +Bug #5075 - macOS: Build fails on macOS 10.9 due to unknown compiler flag +Bug #5809 - macOS: No version number is shown in the App Info dialog +Bug #3197 - Linux: switchDoubleTap option is not working +Bug #4477 - Linux: Mouse buttons higher than id 10 result in crash +Enhancement #4504 - Improved Korean language description +Enhancement #5525 - Added support for precise screen positioning in config file +Enhancement #4290 - Windows: Removed annoying alt+print screen functionality + v1.8.7-stable ============= Bug #5784 - Edition changes when reopening GUI From ed17e9275d55495d06e7f61127ee5de63c642a1f Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Mon, 6 Feb 2017 12:37:43 +0100 Subject: [PATCH 237/241] XRandR: fix screen size calculation XWindowsScreen::saveShape() using XRRSizes / XRRRotations to calculate screen dimensions when XRandR and a rotated screen was detected. This is wrong. The screen dimensions in the display properties already reflect rotation. Moreover, on servers supporting XRandR >= 1.2, the XRRSizes() and XRRRotations calls from XRandR 1.1 will return the properties of the "primary output" in XRandR 1.2 terms rather than the properties of the entire screen. --- src/lib/platform/XWindowsScreen.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index 5c29499ad..6a0d72bfc 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -974,22 +974,6 @@ XWindowsScreen::saveShape() m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display)); m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display)); -#if HAVE_X11_EXTENSIONS_XRANDR_H - if (m_xrandr){ - int numSizes; - XRRScreenSize* xrrs; - Rotation rotation; - xrrs = XRRSizes(m_display, DefaultScreen(m_display), &numSizes); - XRRRotations(m_display, DefaultScreen(m_display), &rotation); - if (xrrs != NULL) { - if (rotation & (RR_Rotate_90|RR_Rotate_270) ){ - m_w = xrrs->height; - m_h = xrrs->width; - } - } - } -#endif - // get center of default screen m_xCenter = m_x + (m_w >> 1); m_yCenter = m_y + (m_h >> 1); From fc3cc78c3e048b8c38d170a1bcd9120c89f02a3b Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 6 Feb 2017 12:47:19 +0000 Subject: [PATCH 238/241] Update changelog... again --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 5ccc80f6f..3a8df3676 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,7 @@ Bug #5075 - macOS: Build fails on macOS 10.9 due to unknown compiler flag Bug #5809 - macOS: No version number is shown in the App Info dialog Bug #3197 - Linux: switchDoubleTap option is not working Bug #4477 - Linux: Mouse buttons higher than id 10 result in crash +Bug #5832 - Linux: Screen size misdetected on multi-monitor display Enhancement #4504 - Improved Korean language description Enhancement #5525 - Added support for precise screen positioning in config file Enhancement #4290 - Windows: Removed annoying alt+print screen functionality From c5b83ce4c47b53d9243a59d84463544059845bdf Mon Sep 17 00:00:00 2001 From: Epakai Date: Sun, 12 Feb 2017 15:18:44 -0600 Subject: [PATCH 239/241] Fix ClipboardChunkTests unit test (Fixes #5840) --- src/test/unittests/synergy/ClipboardChunkTests.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/test/unittests/synergy/ClipboardChunkTests.cpp b/src/test/unittests/synergy/ClipboardChunkTests.cpp index 2460778a7..e1c3336e0 100644 --- a/src/test/unittests/synergy/ClipboardChunkTests.cpp +++ b/src/test/unittests/synergy/ClipboardChunkTests.cpp @@ -26,9 +26,11 @@ TEST(ClipboardChunkTests, start_formatStartChunk) UInt32 sequence = 0; String mockDataSize("10"); ClipboardChunk* chunk = ClipboardChunk::start(id, sequence, mockDataSize); + UInt32 temp_m_chunk; + memcpy(&temp_m_chunk, &(chunk->m_chunk[1]), 4); EXPECT_EQ(id, chunk->m_chunk[0]); - EXPECT_EQ(sequence, (UInt32)chunk->m_chunk[1]); + EXPECT_EQ(sequence, temp_m_chunk); EXPECT_EQ(kDataStart, chunk->m_chunk[5]); EXPECT_EQ('1', chunk->m_chunk[6]); EXPECT_EQ('0', chunk->m_chunk[7]); @@ -43,9 +45,11 @@ TEST(ClipboardChunkTests, data_formatDataChunk) UInt32 sequence = 1; String mockData("mock data"); ClipboardChunk* chunk = ClipboardChunk::data(id, sequence, mockData); + UInt32 temp_m_chunk; + memcpy(&temp_m_chunk, &(chunk->m_chunk[1]), 4); EXPECT_EQ(id, chunk->m_chunk[0]); - EXPECT_EQ(sequence, (UInt32)chunk->m_chunk[1]); + EXPECT_EQ(sequence, temp_m_chunk); EXPECT_EQ(kDataChunk, chunk->m_chunk[5]); EXPECT_EQ('m', chunk->m_chunk[6]); EXPECT_EQ('o', chunk->m_chunk[7]); @@ -66,9 +70,11 @@ TEST(ClipboardChunkTests, end_formatDataChunk) ClipboardID id = 1; UInt32 sequence = 1; ClipboardChunk* chunk = ClipboardChunk::end(id, sequence); + UInt32 temp_m_chunk; + memcpy(&temp_m_chunk, &(chunk->m_chunk[1]), 4); EXPECT_EQ(id, chunk->m_chunk[0]); - EXPECT_EQ(sequence, (UInt32)chunk->m_chunk[1]); + EXPECT_EQ(sequence, temp_m_chunk); EXPECT_EQ(kDataEnd, chunk->m_chunk[5]); EXPECT_EQ('\0', chunk->m_chunk[6]); From 5909df9ee72de9a401421dc1cd306dce97e760a5 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 3 Mar 2017 13:41:07 +0000 Subject: [PATCH 240/241] v1.8.8-stable --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 635f328b4..94c474e8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 8) -set(VERSION_STAGE rc1) +set(VERSION_STAGE stable) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From ec56ac4485ef8e3cf986107b8456949b5aec3527 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 3 Mar 2017 14:51:23 +0000 Subject: [PATCH 241/241] Fix version number in Changelog --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 3a8df3676..dbb3b088c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -v1.8.8-rc1 +v1.8.8 ========== Bug #5196 - Some keys on Korean and Japanese keyboards have the same keycode Bug #5578 - Pressing Hangul key results in alt+'a'