From d961106df92762994dd6e6b2be019fba0573c2d4 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 7 Jul 2015 16:37:30 -0700 Subject: [PATCH 001/572] Update ChangeLog --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 065f5bc3a..2ed63bfd5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -17,6 +17,7 @@ Enhancement #4796 - Improve secure socket intensive try operations Enhancement #4327 - GUI setting to disable drag and drop feature Enhancement #4745 - Tray icon notification for clipboard data transfer progress Enhancement #4793 - Additional logging to output OpenSSL version +Enhancement #4845 - Add timestamp to log output v1.7.3-stable ============= From 8676f64b96a9f77a8301a27a00a3900be16df093 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 9 Jul 2015 09:54:57 -0700 Subject: [PATCH 002/572] Revert "Add drag and drop enable option to GUI settings" --- src/gui/res/ServerConfigDialogBase.ui | 21 +++++++-------------- src/gui/src/MainWindow.cpp | 4 +--- src/gui/src/ServerConfig.cpp | 3 --- src/gui/src/ServerConfig.h | 3 --- src/gui/src/ServerConfigDialog.cpp | 3 --- 5 files changed, 8 insertions(+), 26 deletions(-) diff --git a/src/gui/res/ServerConfigDialogBase.ui b/src/gui/res/ServerConfigDialogBase.ui index cc44f66ed..6d257ba20 100644 --- a/src/gui/res/ServerConfigDialogBase.ui +++ b/src/gui/res/ServerConfigDialogBase.ui @@ -491,21 +491,7 @@ Double click on a screen to edit its settings. - - - - Ignore auto config clients - - - - - - Enable drag and drop file transfers - - - - Qt::Vertical @@ -518,6 +504,13 @@ Double click on a screen to edit its settings. + + + + Ignore auto config clients + + + diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 499e6af6d..0bde1a634 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -543,9 +543,7 @@ void MainWindow::startSynergy() #ifndef Q_OS_LINUX - if (m_ServerConfig.enableDragAndDrop()) { - args << "--enable-drag-drop"; - } + args << "--enable-drag-drop"; #endif diff --git a/src/gui/src/ServerConfig.cpp b/src/gui/src/ServerConfig.cpp index d84f91f03..70f5d46eb 100644 --- a/src/gui/src/ServerConfig.cpp +++ b/src/gui/src/ServerConfig.cpp @@ -50,7 +50,6 @@ ServerConfig::ServerConfig(QSettings* settings, int numColumns, int numRows , m_NumRows(numRows), m_ServerName(serverName), m_IgnoreAutoConfigClient(false), - m_EnableDragAndDrop(false), m_pMainWindow(mainWindow) { Q_ASSERT(m_pSettings); @@ -115,7 +114,6 @@ void ServerConfig::saveSettings() settings().setValue("switchDoubleTap", switchDoubleTap()); settings().setValue("switchCornerSize", switchCornerSize()); settings().setValue("ignoreAutoConfigClient", ignoreAutoConfigClient()); - settings().setValue("enableDragAndDrop", enableDragAndDrop()); writeSettings(settings(), switchCorners(), "switchCorner"); @@ -159,7 +157,6 @@ void ServerConfig::loadSettings() setSwitchDoubleTap(settings().value("switchDoubleTap", 250).toInt()); setSwitchCornerSize(settings().value("switchCornerSize").toInt()); setIgnoreAutoConfigClient(settings().value("ignoreAutoConfigClient").toBool()); - setEnableDragAndDrop(settings().value("enableDragAndDrop", true).toBool()); readSettings(settings(), switchCorners(), "switchCorner", false, NumSwitchCorners); diff --git a/src/gui/src/ServerConfig.h b/src/gui/src/ServerConfig.h index 15214a7ee..9600b35db 100644 --- a/src/gui/src/ServerConfig.h +++ b/src/gui/src/ServerConfig.h @@ -61,7 +61,6 @@ class ServerConfig : public BaseConfig const QList& switchCorners() const { return m_SwitchCorners; } const HotkeyList& hotkeys() const { return m_Hotkeys; } bool ignoreAutoConfigClient() const { return m_IgnoreAutoConfigClient; } - bool enableDragAndDrop() const { return m_EnableDragAndDrop; } void saveSettings(); void loadSettings(); @@ -89,7 +88,6 @@ class ServerConfig : public BaseConfig void setSwitchCorner(int c, bool on) { m_SwitchCorners[c] = on; } void setSwitchCornerSize(int val) { m_SwitchCornerSize = val; } void setIgnoreAutoConfigClient(bool on) { m_IgnoreAutoConfigClient = on; } - void setEnableDragAndDrop(bool on) { m_EnableDragAndDrop = on; } QList& switchCorners() { return m_SwitchCorners; } HotkeyList& hotkeys() { return m_Hotkeys; } @@ -121,7 +119,6 @@ class ServerConfig : public BaseConfig HotkeyList m_Hotkeys; QString m_ServerName; bool m_IgnoreAutoConfigClient; - bool m_EnableDragAndDrop; MainWindow* m_pMainWindow; }; diff --git a/src/gui/src/ServerConfigDialog.cpp b/src/gui/src/ServerConfigDialog.cpp index 501c758c5..3fc04fabf 100644 --- a/src/gui/src/ServerConfigDialog.cpp +++ b/src/gui/src/ServerConfigDialog.cpp @@ -56,8 +56,6 @@ ServerConfigDialog::ServerConfigDialog(QWidget* parent, ServerConfig& config, co m_pCheckBoxIgnoreAutoConfigClient->setChecked(serverConfig().ignoreAutoConfigClient()); - m_pCheckBoxEnableDragAndDrop->setChecked(serverConfig().enableDragAndDrop()); - foreach(const Hotkey& hotkey, serverConfig().hotkeys()) m_pListHotkeys->addItem(hotkey.text()); @@ -99,7 +97,6 @@ void ServerConfigDialog::accept() serverConfig().setSwitchCorner(BaseConfig::BottomRight, m_pCheckBoxCornerBottomRight->isChecked()); serverConfig().setSwitchCornerSize(m_pSpinBoxSwitchCornerSize->value()); serverConfig().setIgnoreAutoConfigClient(m_pCheckBoxIgnoreAutoConfigClient->isChecked()); - serverConfig().setEnableDragAndDrop(m_pCheckBoxEnableDragAndDrop->isChecked()); // now that the dialog has been accepted, copy the new server config to the original one, // which is a reference to the one in MainWindow. From 7254e621e1be7f99307fe8baf0f36e5ddfb7b3cd Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 9 Jul 2015 10:00:02 -0700 Subject: [PATCH 003/572] Revert "Fix missing DLLs after install #3774" --- src/setup/win32/Product.wxs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/setup/win32/Product.wxs b/src/setup/win32/Product.wxs index c47aa0633..6cdac1b2c 100644 --- a/src/setup/win32/Product.wxs +++ b/src/setup/win32/Product.wxs @@ -112,13 +112,13 @@ - - - - - + + + + + - + From 3eb04d1b4c221b49e7d88b35378e709f92dfe74d Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 9 Jul 2015 17:05:33 -0700 Subject: [PATCH 004/572] Revert "Revert "Fix missing DLLs after install #3774"" This reverts commit 7254e621e1be7f99307fe8baf0f36e5ddfb7b3cd. --- src/setup/win32/Product.wxs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/setup/win32/Product.wxs b/src/setup/win32/Product.wxs index 6cdac1b2c..c47aa0633 100644 --- a/src/setup/win32/Product.wxs +++ b/src/setup/win32/Product.wxs @@ -112,13 +112,13 @@ - - - - - + + + + + - + From 8ff44d276b8d6b9aaa0fca4888ac22b10250dd31 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 9 Jul 2015 17:05:43 -0700 Subject: [PATCH 005/572] Revert "Revert "Add drag and drop enable option to GUI settings"" This reverts commit 8676f64b96a9f77a8301a27a00a3900be16df093. --- src/gui/res/ServerConfigDialogBase.ui | 21 ++++++++++++++------- src/gui/src/MainWindow.cpp | 4 +++- src/gui/src/ServerConfig.cpp | 3 +++ src/gui/src/ServerConfig.h | 3 +++ src/gui/src/ServerConfigDialog.cpp | 3 +++ 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/gui/res/ServerConfigDialogBase.ui b/src/gui/res/ServerConfigDialogBase.ui index 6d257ba20..cc44f66ed 100644 --- a/src/gui/res/ServerConfigDialogBase.ui +++ b/src/gui/res/ServerConfigDialogBase.ui @@ -491,7 +491,21 @@ Double click on a screen to edit its settings. + + + + Ignore auto config clients + + + + + + Enable drag and drop file transfers + + + + Qt::Vertical @@ -504,13 +518,6 @@ Double click on a screen to edit its settings. - - - - Ignore auto config clients - - - diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 0bde1a634..499e6af6d 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -543,7 +543,9 @@ void MainWindow::startSynergy() #ifndef Q_OS_LINUX - args << "--enable-drag-drop"; + if (m_ServerConfig.enableDragAndDrop()) { + args << "--enable-drag-drop"; + } #endif diff --git a/src/gui/src/ServerConfig.cpp b/src/gui/src/ServerConfig.cpp index 70f5d46eb..d84f91f03 100644 --- a/src/gui/src/ServerConfig.cpp +++ b/src/gui/src/ServerConfig.cpp @@ -50,6 +50,7 @@ ServerConfig::ServerConfig(QSettings* settings, int numColumns, int numRows , m_NumRows(numRows), m_ServerName(serverName), m_IgnoreAutoConfigClient(false), + m_EnableDragAndDrop(false), m_pMainWindow(mainWindow) { Q_ASSERT(m_pSettings); @@ -114,6 +115,7 @@ void ServerConfig::saveSettings() settings().setValue("switchDoubleTap", switchDoubleTap()); settings().setValue("switchCornerSize", switchCornerSize()); settings().setValue("ignoreAutoConfigClient", ignoreAutoConfigClient()); + settings().setValue("enableDragAndDrop", enableDragAndDrop()); writeSettings(settings(), switchCorners(), "switchCorner"); @@ -157,6 +159,7 @@ void ServerConfig::loadSettings() setSwitchDoubleTap(settings().value("switchDoubleTap", 250).toInt()); setSwitchCornerSize(settings().value("switchCornerSize").toInt()); setIgnoreAutoConfigClient(settings().value("ignoreAutoConfigClient").toBool()); + setEnableDragAndDrop(settings().value("enableDragAndDrop", true).toBool()); readSettings(settings(), switchCorners(), "switchCorner", false, NumSwitchCorners); diff --git a/src/gui/src/ServerConfig.h b/src/gui/src/ServerConfig.h index 9600b35db..15214a7ee 100644 --- a/src/gui/src/ServerConfig.h +++ b/src/gui/src/ServerConfig.h @@ -61,6 +61,7 @@ class ServerConfig : public BaseConfig const QList& switchCorners() const { return m_SwitchCorners; } const HotkeyList& hotkeys() const { return m_Hotkeys; } bool ignoreAutoConfigClient() const { return m_IgnoreAutoConfigClient; } + bool enableDragAndDrop() const { return m_EnableDragAndDrop; } void saveSettings(); void loadSettings(); @@ -88,6 +89,7 @@ class ServerConfig : public BaseConfig void setSwitchCorner(int c, bool on) { m_SwitchCorners[c] = on; } void setSwitchCornerSize(int val) { m_SwitchCornerSize = val; } void setIgnoreAutoConfigClient(bool on) { m_IgnoreAutoConfigClient = on; } + void setEnableDragAndDrop(bool on) { m_EnableDragAndDrop = on; } QList& switchCorners() { return m_SwitchCorners; } HotkeyList& hotkeys() { return m_Hotkeys; } @@ -119,6 +121,7 @@ class ServerConfig : public BaseConfig HotkeyList m_Hotkeys; QString m_ServerName; bool m_IgnoreAutoConfigClient; + bool m_EnableDragAndDrop; MainWindow* m_pMainWindow; }; diff --git a/src/gui/src/ServerConfigDialog.cpp b/src/gui/src/ServerConfigDialog.cpp index 3fc04fabf..501c758c5 100644 --- a/src/gui/src/ServerConfigDialog.cpp +++ b/src/gui/src/ServerConfigDialog.cpp @@ -56,6 +56,8 @@ ServerConfigDialog::ServerConfigDialog(QWidget* parent, ServerConfig& config, co m_pCheckBoxIgnoreAutoConfigClient->setChecked(serverConfig().ignoreAutoConfigClient()); + m_pCheckBoxEnableDragAndDrop->setChecked(serverConfig().enableDragAndDrop()); + foreach(const Hotkey& hotkey, serverConfig().hotkeys()) m_pListHotkeys->addItem(hotkey.text()); @@ -97,6 +99,7 @@ void ServerConfigDialog::accept() serverConfig().setSwitchCorner(BaseConfig::BottomRight, m_pCheckBoxCornerBottomRight->isChecked()); serverConfig().setSwitchCornerSize(m_pSpinBoxSwitchCornerSize->value()); serverConfig().setIgnoreAutoConfigClient(m_pCheckBoxIgnoreAutoConfigClient->isChecked()); + serverConfig().setEnableDragAndDrop(m_pCheckBoxEnableDragAndDrop->isChecked()); // now that the dialog has been accepted, copy the new server config to the original one, // which is a reference to the one in MainWindow. From 392f2904aa9b1ff7cb1901f2c7bf36f77aefc239 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 24 Jul 2015 15:17:27 -0700 Subject: [PATCH 006/572] Version to v1.8.0-alpha --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c478fc5b..00cc15967 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,9 @@ # Version number for Synergy set(VERSION_MAJOR 1) -set(VERSION_MINOR 7) -set(VERSION_REV 4) +set(VERSION_MINOR 8) +set(VERSION_REV 0) +set(VERSION_STAGE alpha) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From 41c03b8f37f330db05441e0f99bbf2058b4e902a Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 24 Jul 2015 15:21:57 -0700 Subject: [PATCH 007/572] Changed installer filename to use branch, stage and revision #4898 --- ext/toolchain/commands1.py | 76 ++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index eff8b9048..0a3002994 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -848,7 +848,7 @@ def signwin(self, pfx, pwdFile, dist): pwd = lines[0] if (dist): - self.signFile(pfx, pwd, 'bin/Release', self.dist_name('win')) + self.signFile(pfx, pwd, 'bin/Release', self.getDistFilename('win')) else: self.signFile(pfx, pwd, 'bin/Release', 'synergy.exe') self.signFile(pfx, pwd, 'bin/Release', 'synergyc.exe') @@ -1086,7 +1086,7 @@ def distRpm(self): templateFile = open(self.cmake_dir + '/synergy.spec.in') template = templateFile.read() - template = template.replace('${in:version}', self.getVersionFromCmake()) + template = template.replace('${in:version}', self.getVersionNumber()) specPath = rpmDir + '/synergy.spec' @@ -1094,10 +1094,8 @@ def distRpm(self): specFile.write(template) specFile.close() - version = self.getVersionFromCmake() target = '../../bin/synergy-%s-%s.rpm' % ( - version, self.getLinuxPlatform()) - + self.getVersionForFilename(), self.getLinuxPlatform()) try: self.try_chdir(rpmDir) @@ -1123,9 +1121,10 @@ def distDeb(self): binDir = self.getGenerator().binDir resDir = self.cmake_dir - version = self.getVersionFromCmake() package = '%s-%s-%s' % ( - self.project, version, self.getLinuxPlatform()) + self.project, + self.getVersionForFilename(), + self.getLinuxPlatform()) debDir = '%s/deb' % buildDir if os.path.exists(debDir): @@ -1138,7 +1137,7 @@ def distDeb(self): template = templateFile.read() template = template.replace('${in:version}', - self.getVersionFromCmake()) + self.getVersionNumber()) template = template.replace('${in:arch}', self.getDebianArch()) @@ -1226,8 +1225,11 @@ def distDeb(self): self.restore_chdir() def distSrc(self): - version = self.getVersionFromCmake() - name = (self.project + '-' + version + '-Source') + name = '%s-%s-%s' % ( + self.project, + self.getVersionForFilename(), + 'Source') + exportPath = self.getGenerator().buildDir + '/' + name if os.path.exists(exportPath): @@ -1280,7 +1282,7 @@ def distMac(self): fileName = "%s-%s-%s.dmg" % ( self.project, - self.getVersionFromCmake(), + self.getVersionForFilename(), self.getMacPackageName()) cmd = "hdiutil create " + fileName + " -srcfolder ./" + name + "/ -ov" @@ -1296,7 +1298,7 @@ def distWix(self): if generator.endswith('Win64'): arch = 'x64' - version = self.getVersionFromCmake() + version = self.getVersionNumber() args = "/p:DefineConstants=\"Version=%s\"" % version self.run_vcbuild( @@ -1305,7 +1307,7 @@ def distWix(self): filename = "%s-%s-Windows-%s.msi" % ( self.project, - version, + self.getVersionForFilename(), arch) old = "bin/Release/synergy.msi" @@ -1340,7 +1342,7 @@ def distNsis(self, vcRedistDir, qtDir): templateFile = open(self.cmake_dir + '\Installer.nsi.in') template = templateFile.read() - template = template.replace('${in:version}', self.getVersionFromCmake()) + template = template.replace('${in:version}', self.getVersionNumber()) template = template.replace('${in:arch}', arch) template = template.replace('${in:vcRedistDir}', vcRedistDir) template = template.replace('${in:qtDir}', qtDir) @@ -1357,7 +1359,7 @@ def distNsis(self, vcRedistDir, qtDir): if err != 0: raise Exception('Package failed: ' + str(err)) - def getVersionFromCmake(self): + def getVersionNumber(self): cmakeFile = open('CMakeLists.txt') cmake = cmakeFile.read() @@ -1370,7 +1372,20 @@ def getVersionFromCmake(self): revRe = re.search('VERSION_REV (\d+)', cmake) rev = revRe.group(1) - return major + '.' + minor + '.' + rev + return "%s.%s.%s" % (major, minor, rev) + + def getVersionStage(self): + cmakeFile = open('CMakeLists.txt') + cmake = cmakeFile.read() + + stageRe = re.search('VERSION_STAGE (\w+)', cmake) + return stageRe.group(1) + + def getVersionForFilename(self): + versionStage = self.getVersionStage() + gitBranch = self.getGitBranchName() + gitRevision = self.getGitRevision() + return "%s-%s-%s" % (gitBranch, versionStage, gitRevision) def distftp(self, type, ftp): if not type: @@ -1380,8 +1395,9 @@ def distftp(self, type, ftp): binDir = self.getGenerator().getBinDir('Release') - packageSource = binDir + '/' + self.dist_name(type) - packageTarget = self.dist_name_rev(type) + filename = self.getDistFilename(type) + packageSource = binDir + '/' + filename + packageTarget = filename ftp.upload(packageSource, packageTarget) if type != 'src': @@ -1393,9 +1409,7 @@ def distftp(self, type, ftp): def getLibraryDistFilename(self, type, dir, name): (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) - branch = self.getGitBranchName() - revision = self.getGitRevision() - firstPart = '%s-%s-%s-%s' % (name, branch, revision, platform) + firstPart = '%s-%s-%s' % (name, self.getVersionForFilename(), platform) filename = '%s.%s' % (firstPart, libraryExt) if type == 'rpm' or type == 'deb': @@ -1453,30 +1467,14 @@ def getDistributePlatformInfo(self, type): return (platform, ext, libraryExt) - def dist_name(self, type): - (platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type) - ext = packageExt - - pattern = ( - re.escape(self.project + '-') + '\d+\.\d+\.\d+' + - re.escape('-' + platform + '.' + ext)) - + def getDistFilename(self, type): + pattern = self.getVersionForFilename() for filename in os.listdir(self.getBinDir('Release')): if re.search(pattern, filename): return filename - # still here? package probably not created yet. raise Exception('Could not find package name with pattern: ' + pattern) - def dist_name_rev(self, type): - branch = self.getGitBranchName() - revision = self.getGitRevision() - - # find the version number (we're puting the rev in after this) - pattern = '(\d+\.\d+\.\d+)' - replace = "%s-%s" % (branch, revision) - return re.sub(pattern, replace, self.dist_name(type)) - def getDebianArch(self): if os.uname()[4][:3] == 'arm': return 'armhf' From 9483fecc42a7122444204d0e122139c14b31df20 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 8 Jul 2015 15:46:13 -0700 Subject: [PATCH 008/572] Fixed code style Revert "Moved note into either warning or info #4745" This reverts commit d3a4ce1f112cdee64def242c3247df81e5c9a9d4. Revert "Changed note to notify #4745" This reverts commit 5006adedfecedd16c07543d32acffe36aeaf61bb. Conflicts: src/test/unittests/ipc/IpcLogOutputterTests.cpp Shortened transmission log #4858 Revert "Added code to throw an error if the plugin can't be deleted or written to #4696" This reverts commit 5696497bc0835106c2df9a9a9de1bbd75821d885. --- src/lib/arch/win32/ArchMiscWindows.cpp | 19 ++++++++++++++----- src/lib/arch/win32/ArchSystemWindows.cpp | 15 +++++++++------ src/lib/base/EventQueue.cpp | 2 +- src/lib/platform/MSWindowsDropTarget.cpp | 4 ++-- src/lib/platform/MSWindowsScreen.cpp | 4 ++-- src/lib/plugin/ns/ns.cpp | 2 +- src/lib/server/ClientListener.cpp | 4 ++-- src/lib/synwinhk/synwinhk.cpp | 6 +++--- 8 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/lib/arch/win32/ArchMiscWindows.cpp b/src/lib/arch/win32/ArchMiscWindows.cpp index 92aad1a46..556ffe04b 100644 --- a/src/lib/arch/win32/ArchMiscWindows.cpp +++ b/src/lib/arch/win32/ArchMiscWindows.cpp @@ -228,8 +228,11 @@ void ArchMiscWindows::setValue(HKEY key, const TCHAR* name, const std::string& value) { - assert(key != NULL); - if(key ==NULL) return; // TODO: throw exception + assert(key != NULL); + if (key == NULL) { + // TODO: throw exception + return; + } RegSetValueEx(key, name, 0, REG_SZ, reinterpret_cast(value.c_str()), (DWORD)value.size() + 1); @@ -238,8 +241,11 @@ ArchMiscWindows::setValue(HKEY key, void ArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value) { - assert(key != NULL); - if(key ==NULL) return; // TODO: throw exception + assert(key != NULL); + if (key == NULL) { + // TODO: throw exception + return; + } RegSetValueEx(key, name, 0, REG_DWORD, reinterpret_cast(&value), sizeof(DWORD)); @@ -251,7 +257,10 @@ ArchMiscWindows::setValueBinary(HKEY key, { assert(key != NULL); assert(name != NULL); - if(key ==NULL || name==NULL) return; // TODO: throw exception + if (key == NULL || name == NULL) { + // TODO: throw exception + return; + } RegSetValueEx(key, name, 0, REG_BINARY, reinterpret_cast(value.data()), (DWORD)value.size()); diff --git a/src/lib/arch/win32/ArchSystemWindows.cpp b/src/lib/arch/win32/ArchSystemWindows.cpp index 391726ecd..5540e40c2 100644 --- a/src/lib/arch/win32/ArchSystemWindows.cpp +++ b/src/lib/arch/win32/ArchSystemWindows.cpp @@ -61,16 +61,19 @@ ArchSystemWindows::getOSName() const case VER_PLATFORM_WIN32_NT: #if WINVER >= _WIN32_WINNT_WIN2K if (info.dwMajorVersion == 6) { - if(info.dwMinorVersion == 0) { + if (info.dwMinorVersion == 0) { if (info.wProductType == VER_NT_WORKSTATION) { return "Microsoft Windows Vista"; - } else { + } + else { return "Microsoft Windows Server 2008"; } - } else if(info.dwMinorVersion == 1) { + } + else if (info.dwMinorVersion == 1) { if (info.wProductType == VER_NT_WORKSTATION) { return "Microsoft Windows 7"; - } else { + } + else { return "Microsoft Windows Server 2008 R2"; } } @@ -102,7 +105,7 @@ std::string ArchSystemWindows::getPlatformName() const { #ifdef _X86_ - if(isWOW64()) + if (isWOW64()) return "x86 (WOW64)"; else return "x86"; @@ -146,7 +149,7 @@ ArchSystemWindows::isWOW64() const (LPFN_ISWOW64PROCESS) GetProcAddress(hModule, "IsWow64Process"); BOOL bIsWow64 = FALSE; - if(NULL != fnIsWow64Process && + if (NULL != fnIsWow64Process && fnIsWow64Process(GetCurrentProcess(), &bIsWow64) && bIsWow64) { diff --git a/src/lib/base/EventQueue.cpp b/src/lib/base/EventQueue.cpp index e6d8bdbb7..bc3b9bc60 100644 --- a/src/lib/base/EventQueue.cpp +++ b/src/lib/base/EventQueue.cpp @@ -566,7 +566,7 @@ EventQueue::waitForReady() const Lock lock(m_readyMutex); while (!m_readyCondVar->wait()) { - if(ARCH->time() > timeout) { + if (ARCH->time() > timeout) { throw std::runtime_error("event queue is not ready within 5 sec"); } } diff --git a/src/lib/platform/MSWindowsDropTarget.cpp b/src/lib/platform/MSWindowsDropTarget.cpp index d938391c4..8cb8caa4f 100644 --- a/src/lib/platform/MSWindowsDropTarget.cpp +++ b/src/lib/platform/MSWindowsDropTarget.cpp @@ -116,8 +116,8 @@ getDropData(IDataObject* dataObject) STGMEDIUM stgMed; // See if the dataobject contains any DROP stored as a HGLOBAL - if(dataObject->QueryGetData(&fmtEtc) == S_OK) { - if(dataObject->GetData(&fmtEtc, &stgMed) == S_OK) { + if (dataObject->QueryGetData(&fmtEtc) == S_OK) { + if (dataObject->GetData(&fmtEtc, &stgMed) == S_OK) { // get data here PVOID data = GlobalLock(stgMed.hGlobal); diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index efb922665..5473feb39 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -310,7 +310,7 @@ MSWindowsScreen::enter() // and that the screen is not in powersave mode. ArchMiscWindows::wakeupDisplay(); - if(m_screensaver != NULL && m_screensaverActive) + if (m_screensaver != NULL && m_screensaverActive) { m_screensaver->deactivate(); m_screensaverActive = 0; @@ -923,7 +923,7 @@ void MSWindowsScreen::sendClipboardEvent(Event::Type type, ClipboardID id) { ClipboardInfo* info = (ClipboardInfo*)malloc(sizeof(ClipboardInfo)); - if(info == NULL) { + if (info == NULL) { LOG((CLOG_ERR "malloc failed on %s:%s", __FILE__, __LINE__ )); return; } diff --git a/src/lib/plugin/ns/ns.cpp b/src/lib/plugin/ns/ns.cpp index 7409a1475..7498ef0d4 100644 --- a/src/lib/plugin/ns/ns.cpp +++ b/src/lib/plugin/ns/ns.cpp @@ -105,7 +105,7 @@ invoke(const char* command, void** args) g_secureListenSocket = NULL; } } - else if(strcmp(command, "version") == 0) { + else if (strcmp(command, "version") == 0) { return (void*) kSynergyVers; } diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index c84cb9e4b..fee90578a 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -151,8 +151,8 @@ ClientListener::handleClientConnecting(const Event&, void*) if (m_useSecureNetwork) { LOG((CLOG_DEBUG2 "attempting sercure Connection")); - while(!socket->isReady()) { - if(socket->isFatal()) { + while (!socket->isReady()) { + if (socket->isFatal()) { m_listen->deleteSocket(socket); return; } diff --git a/src/lib/synwinhk/synwinhk.cpp b/src/lib/synwinhk/synwinhk.cpp index 1d1630aa8..8638f2440 100644 --- a/src/lib/synwinhk/synwinhk.cpp +++ b/src/lib/synwinhk/synwinhk.cpp @@ -334,7 +334,7 @@ doKeyboardHookHandler(WPARAM wParam, LPARAM lParam) // map the key event to a character. we have to put the dead // key back first and this has the side effect of removing it. if (g_deadVirtKey != 0) { - if(ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, + if (ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, g_deadKeyState, &c, flags) == 2) { // If ToAscii returned 2, it means that we accidentally removed @@ -366,7 +366,7 @@ doKeyboardHookHandler(WPARAM wParam, LPARAM lParam) PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, wParam | 0x05000000, lParam); if (g_deadVirtKey != 0) { - if(ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, + if (ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, g_deadKeyState, &c, flags) == 2) { ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16, @@ -397,7 +397,7 @@ doKeyboardHookHandler(WPARAM wParam, LPARAM lParam) default: // key is a dead key - if(lParam & 0x80000000u) + if (lParam & 0x80000000u) // This handles the obscure situation where a key has been // pressed which is both a dead key and a normal character // depending on which modifiers have been pressed. We From c89dc68cd7e48dbad8e1540b0fe340b69b48fffb Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 8 Jul 2015 17:04:44 -0700 Subject: [PATCH 009/572] Revert "Added warning for enable encryption #4584" This reverts commit 8e15b77db35345d748e2c9420be9806012b570f5. --- src/gui/src/SettingsDialog.cpp | 20 ++------------------ src/gui/src/SettingsDialog.h | 1 - 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index dbd3ef277..bcaa2263e 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -37,8 +37,7 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), Ui::SettingsDialogBase(), m_AppConfig(config), - m_SuppressElevateWarning(false), - m_SuppressEncryptionWarning(false) + m_SuppressElevateWarning(false) { setupUi(this); @@ -69,9 +68,7 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) : m_pCheckBoxEnableCrypto->setChecked(false); } else { - m_SuppressEncryptionWarning = true; m_pCheckBoxEnableCrypto->setChecked(m_AppConfig.getCryptoEnabled()); - m_SuppressEncryptionWarning = false; } } @@ -86,7 +83,6 @@ void SettingsDialog::accept() appConfig().setLanguage(m_pComboLanguage->itemData(m_pComboLanguage->currentIndex()).toString()); appConfig().setElevateMode(m_pCheckBoxElevateMode->isChecked()); appConfig().setAutoHide(m_pCheckBoxAutoHide->isChecked()); - appConfig().setCryptoEnabled(m_pCheckBoxEnableCrypto->isChecked()); appConfig().saveSettings(); QDialog::accept(); } @@ -168,17 +164,5 @@ void SettingsDialog::on_m_pCheckBoxElevateMode_toggled(bool checked) void SettingsDialog::on_m_pCheckBoxEnableCrypto_toggled(bool checked) { - if (checked && !m_SuppressEncryptionWarning) { - int r = QMessageBox::warning( - this, tr("Synergy SSL Encryption"), - tr("Are you sure you want to enable SSL encryption?\n\n" - "This allows Synergy to establish a secure connection, " - "but can slow down the data transfer of clipboard and file."), - QMessageBox::Yes | QMessageBox::No); - - if (r != QMessageBox::Yes) { - m_pCheckBoxEnableCrypto->setChecked(false); - return; - } - } + m_AppConfig.setCryptoEnabled(checked); } diff --git a/src/gui/src/SettingsDialog.h b/src/gui/src/SettingsDialog.h index dc0d1bb2a..8ab2e1e04 100644 --- a/src/gui/src/SettingsDialog.h +++ b/src/gui/src/SettingsDialog.h @@ -47,7 +47,6 @@ class SettingsDialog : public QDialog, public Ui::SettingsDialogBase SynergyLocale m_Locale; CoreInterface m_CoreInterface; bool m_SuppressElevateWarning; - bool m_SuppressEncryptionWarning; private slots: void on_m_pCheckBoxEnableCrypto_toggled(bool checked); From d8582d1093212990ab44e0616571189df187e137 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 9 Jul 2015 13:26:31 -0700 Subject: [PATCH 010/572] Revert "Removed warning and error logging level in GUI #4745" This reverts commit 756000d8a9577cb9a99bb27056348ecb385a2415. Conflicts: src/gui/res/SettingsDialogBase.ui src/gui/src/AppConfig.cpp src/gui/src/MainWindow.cpp --- src/gui/res/SettingsDialogBase.ui | 15 +++++++++++++++ src/gui/src/AppConfig.cpp | 15 +++++---------- src/gui/src/AppConfig.h | 1 - src/gui/src/MainWindow.cpp | 4 ++-- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/gui/res/SettingsDialogBase.ui b/src/gui/res/SettingsDialogBase.ui index 666fc7879..6b77b8745 100644 --- a/src/gui/res/SettingsDialogBase.ui +++ b/src/gui/res/SettingsDialogBase.ui @@ -208,6 +208,21 @@ + + + Error + + + + + Warning + + + + + Note + + Info diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index a795538ef..f663a8594 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -37,6 +37,9 @@ const char AppConfig::m_SynergyLogDir[] = "/var/log/"; static const char* logLevelNames[] = { + "ERROR", + "WARNING", + "NOTE", "INFO", "DEBUG", "DEBUG1", @@ -55,8 +58,7 @@ AppConfig::AppConfig(QSettings* settings) : m_ElevateMode(false), m_AutoConfigPrompted(false), m_CryptoEnabled(false), - m_AutoHide(false), - m_ResetLogLevel(true) + m_AutoHide(false) { Q_ASSERT(m_pSettings); @@ -116,7 +118,7 @@ void AppConfig::loadSettings() m_ScreenName = settings().value("screenName", QHostInfo::localHostName()).toString(); m_Port = settings().value("port", 24800).toInt(); m_Interface = settings().value("interface").toString(); - m_LogLevel = settings().value("logLevel", 0).toInt(); // level 0: INFO + m_LogLevel = settings().value("logLevel", 3).toInt(); // level 3: INFO m_LogToFile = settings().value("logToFile", false).toBool(); m_LogFilename = settings().value("logFilename", synergyLogDir() + "synergy.log").toString(); m_WizardLastRun = settings().value("wizardLastRun", 0).toInt(); @@ -130,12 +132,6 @@ void AppConfig::loadSettings() m_UserToken = settings().value("userToken", "").toString(); m_CryptoEnabled = settings().value("cryptoEnabled", false).toBool(); m_AutoHide = settings().value("autoHide", false).toBool(); - m_ResetLogLevel = settings().value("resetLogLevel", true).toBool(); - - if (m_ResetLogLevel) { - m_LogLevel = 0; - m_ResetLogLevel = false; - } } void AppConfig::saveSettings() @@ -157,7 +153,6 @@ void AppConfig::saveSettings() settings().setValue("userToken", m_UserToken); settings().setValue("cryptoEnabled", m_CryptoEnabled); settings().setValue("autoHide", m_AutoHide); - settings().setValue("resetLogLevel", m_ResetLogLevel); } void AppConfig::setAutoConfig(bool autoConfig) diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 1090bad9c..3a304b91e 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -128,7 +128,6 @@ class AppConfig QString m_UserToken; bool m_CryptoEnabled; bool m_AutoHide; - bool m_ResetLogLevel; 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 499e6af6d..a4c8f0b36 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -374,7 +374,7 @@ void MainWindow::appendLogInfo(const QString& text) } void MainWindow::appendLogDebug(const QString& text) { - if (appConfig().logLevel() >= 1) { + if (appConfig().logLevel() >= 4) { appendLogRaw(getTimeStamp() + " DEBUG: " + text); } } @@ -587,7 +587,7 @@ void MainWindow::startSynergy() qDebug() << args; // show command if debug log level... - if (appConfig().logLevel() >= 1) { + if (appConfig().logLevel() >= 4) { appendLogInfo(QString("command: %1 %2").arg(app, args.join(" "))); } From 69a6038cf9459649d04d7c8eedb97d9d325649d3 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 9 Jul 2015 13:28:47 -0700 Subject: [PATCH 011/572] Revert "Moved note into either warning or info #4745" This reverts commit d3a4ce1f112cdee64def242c3247df81e5c9a9d4. --- src/lib/client/Client.cpp | 4 ++-- src/lib/client/ServerProxy.cpp | 2 +- src/lib/plugin/ns/SecureSocket.cpp | 2 +- src/lib/server/ClientListener.cpp | 4 ++-- src/lib/server/ClientProxy1_0.cpp | 4 ++-- src/lib/server/ClientProxyUnknown.cpp | 6 +++--- src/lib/server/Server.cpp | 12 ++++++------ src/lib/synergy/ArgParser.cpp | 2 +- src/lib/synergy/ClientApp.cpp | 8 ++++---- src/lib/synergy/ServerApp.cpp | 6 +++--- 10 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 8deb084a6..5ba8b8e76 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -109,7 +109,7 @@ Client::Client( if (m_args.m_enableCrypto) { m_useSecureNetwork = ARCH->plugin().exists(s_networkSecurity); if (m_useSecureNetwork == false) { - LOG((CLOG_WARN "crypto disabled because of ns plugin not available")); + LOG((CLOG_NOTE "crypto disabled because of ns plugin not available")); } } } @@ -154,7 +154,7 @@ Client::connect() // m_serverAddress will be null if the hostname address is not reolved if (m_serverAddress.getAddress() != NULL) { // to help users troubleshoot, show server host name (issue: 60) - LOG((CLOG_INFO "connecting to '%s': %s:%i", + LOG((CLOG_NOTE "connecting to '%s': %s:%i", m_serverAddress.getHostname().c_str(), ARCH->addrToString(m_serverAddress.getAddress()).c_str(), m_serverAddress.getPort())); diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index 112ad7b87..70a8b26c7 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -334,7 +334,7 @@ ServerProxy::parseMessage(const UInt8* code) void ServerProxy::handleKeepAliveAlarm(const Event&, void*) { - LOG((CLOG_INFO "server is dead")); + LOG((CLOG_NOTE "server is dead")); m_client->disconnect("server is not responding"); } diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 40c3f2194..899b11ad7 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -562,7 +562,7 @@ SecureSocket::verifyCertFingerprint() // format fingerprint into hexdecimal format with colon separator String fingerprint(reinterpret_cast(tempFingerprint), tempFingerprintLen); formatFingerprint(fingerprint); - LOG((CLOG_INFO "server fingerprint: %s", fingerprint.c_str())); + LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str())); String trustedServersFilename; trustedServersFilename = synergy::string::sprintf( diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index fee90578a..874997a88 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -55,7 +55,7 @@ ClientListener::ClientListener(const NetworkAddress& address, if (enableCrypto) { m_useSecureNetwork = ARCH->plugin().exists(s_networkSecurity); if (m_useSecureNetwork == false) { - LOG((CLOG_WARN "crypto disabled because of ns plugin not available")); + LOG((CLOG_NOTE "crypto disabled because of ns plugin not available")); } } @@ -147,7 +147,7 @@ ClientListener::handleClientConnecting(const Event&, void*) return; } - LOG((CLOG_INFO "accepted client connection")); + LOG((CLOG_NOTE "accepted client connection")); if (m_useSecureNetwork) { LOG((CLOG_DEBUG2 "attempting sercure Connection")); diff --git a/src/lib/server/ClientProxy1_0.cpp b/src/lib/server/ClientProxy1_0.cpp index 85ba56ad5..c124624f1 100644 --- a/src/lib/server/ClientProxy1_0.cpp +++ b/src/lib/server/ClientProxy1_0.cpp @@ -210,7 +210,7 @@ ClientProxy1_0::parseMessage(const UInt8* code) void ClientProxy1_0::handleDisconnect(const Event&, void*) { - LOG((CLOG_INFO "client \"%s\" has disconnected", getName().c_str())); + LOG((CLOG_NOTE "client \"%s\" has disconnected", getName().c_str())); disconnect(); } @@ -225,7 +225,7 @@ void ClientProxy1_0::handleFlatline(const Event&, void*) { // didn't get a heartbeat fast enough. assume client is dead. - LOG((CLOG_INFO "client \"%s\" is dead", getName().c_str())); + LOG((CLOG_NOTE "client \"%s\" is dead", getName().c_str())); disconnect(); } diff --git a/src/lib/server/ClientProxyUnknown.cpp b/src/lib/server/ClientProxyUnknown.cpp index 34e1767e2..a328d2138 100644 --- a/src/lib/server/ClientProxyUnknown.cpp +++ b/src/lib/server/ClientProxyUnknown.cpp @@ -270,21 +270,21 @@ ClientProxyUnknown::handleData(const Event&, void*) void ClientProxyUnknown::handleWriteError(const Event&, void*) { - LOG((CLOG_ERR "error communicating with new client")); + LOG((CLOG_NOTE "error communicating with new client")); sendFailure(); } void ClientProxyUnknown::handleTimeout(const Event&, void*) { - LOG((CLOG_INFO "new client is unresponsive")); + LOG((CLOG_NOTE "new client is unresponsive")); sendFailure(); } void ClientProxyUnknown::handleDisconnect(const Event&, void*) { - LOG((CLOG_INFO "new client disconnected")); + LOG((CLOG_NOTE "new client disconnected")); sendFailure(); } diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 1f04489c6..93e1b4503 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -206,7 +206,7 @@ Server::Server( // Determine if scroll lock is already set. If so, lock the cursor to the primary screen if (m_primaryClient->getToggleMask() & KeyModifierScrollLock) { - LOG((CLOG_INFO "Scroll Lock is on, locking cursor to screen")); + LOG((CLOG_NOTE "Scroll Lock is on, locking cursor to screen")); m_lockedToScreen = true; } @@ -332,7 +332,7 @@ Server::adoptClient(BaseClientProxy* client) closeClient(client, kMsgEBusy); return; } - LOG((CLOG_INFO "client \"%s\" has connected", getName(client).c_str())); + LOG((CLOG_NOTE "client \"%s\" has connected", getName(client).c_str())); // send configuration options to client sendOptions(client); @@ -421,7 +421,7 @@ Server::isLockedToScreen() const { // locked if we say we're locked if (isLockedToScreenServer()) { - LOG((CLOG_INFO "Cursor is locked to screen, check Scroll Lock key")); + LOG((CLOG_NOTE "Cursor is locked to screen, check Scroll Lock key")); return true; } @@ -1384,7 +1384,7 @@ Server::handleClientCloseTimeout(const Event&, void* vclient) { // client took too long to disconnect. just dump it. BaseClientProxy* client = reinterpret_cast(vclient); - LOG((CLOG_INFO "forced disconnection of client \"%s\"", getName(client).c_str())); + 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()); @@ -1481,7 +1481,7 @@ Server::handleLockCursorToScreenEvent(const Event& event, void*) // enter new state if (newState != m_lockedToScreen) { m_lockedToScreen = newState; - LOG((CLOG_INFO "cursor %s current screen", m_lockedToScreen ? "locked to" : "unlocked from")); + LOG((CLOG_NOTE "cursor %s current screen", m_lockedToScreen ? "locked to" : "unlocked from")); m_primaryClient->reconfigure(getActivePrimarySides()); if (!isLockedToScreenServer()) { @@ -2152,7 +2152,7 @@ Server::closeClient(BaseClientProxy* client, const char* msg) // note that this method also works on clients that are not in // the m_clients list. adoptClient() may call us with such a // client. - LOG((CLOG_INFO "disconnecting client \"%s\"", getName(client).c_str())); + LOG((CLOG_NOTE "disconnecting client \"%s\"", getName(client).c_str())); // send message // FIXME -- avoid type cast (kinda hard, though) diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index f4b454cbf..91a9c866b 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -313,7 +313,7 @@ bool ArgParser::parseDeprecatedArgs(int argc, const char* const* argv, int& i) { if (isArg(i, argc, argv, NULL, "--crypto-pass")) { - LOG((CLOG_WARN "--crypto-pass is deprecated")); + LOG((CLOG_NOTE "--crypto-pass is deprecated")); i++; return true; } diff --git a/src/lib/synergy/ClientApp.cpp b/src/lib/synergy/ClientApp.cpp index ca7b7855e..972f63c4a 100644 --- a/src/lib/synergy/ClientApp.cpp +++ b/src/lib/synergy/ClientApp.cpp @@ -291,7 +291,7 @@ ClientApp::scheduleClientRestart(double retryTime) void ClientApp::handleClientConnected(const Event&, void*) { - LOG((CLOG_INFO "connected to server")); + LOG((CLOG_NOTE "connected to server")); resetRestartTimeout(); updateStatus(); } @@ -321,7 +321,7 @@ ClientApp::handleClientFailed(const Event& e, void*) void ClientApp::handleClientDisconnected(const Event&, void*) { - LOG((CLOG_INFO "disconnected from server")); + LOG((CLOG_NOTE "disconnected from server")); if (!args().m_restartable) { m_events->addEvent(Event(Event::kQuit)); } @@ -401,7 +401,7 @@ ClientApp::startClient() m_client = openClient(args().m_name, *m_serverAddress, clientScreen); m_clientScreen = clientScreen; - LOG((CLOG_INFO "started client")); + LOG((CLOG_NOTE "started client")); } m_client->connect(); @@ -500,7 +500,7 @@ ClientApp::mainLoop() LOG((CLOG_DEBUG1 "stopping client")); stopClient(); updateStatus(); - LOG((CLOG_INFO "stopped client")); + LOG((CLOG_NOTE "stopped client")); if (argsBase().m_enableIpc) { cleanupIpcClient(); diff --git a/src/lib/synergy/ServerApp.cpp b/src/lib/synergy/ServerApp.cpp index 8b5c11d83..ae9246dfd 100644 --- a/src/lib/synergy/ServerApp.cpp +++ b/src/lib/synergy/ServerApp.cpp @@ -174,7 +174,7 @@ ServerApp::reloadConfig(const Event&, void*) if (m_server != NULL) { m_server->setConfig(*args().m_config); } - LOG((CLOG_INFO "reloaded configuration")); + LOG((CLOG_NOTE "reloaded configuration")); } } @@ -546,7 +546,7 @@ ServerApp::startServer() m_server->setListener(listener); m_listener = listener; updateStatus(); - LOG((CLOG_INFO "started server, waiting for clients")); + LOG((CLOG_NOTE "started server, waiting for clients")); m_serverState = kStarted; return true; } @@ -774,7 +774,7 @@ ServerApp::mainLoop() m_events->getSystemTarget()); cleanupServer(); updateStatus(); - LOG((CLOG_INFO "stopped server")); + LOG((CLOG_NOTE "stopped server")); if (argsBase().m_enableIpc) { cleanupIpcClient(); From a8cf9173c8cb46e54791a7cbe745a297623882be Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 9 Jul 2015 13:34:49 -0700 Subject: [PATCH 012/572] Revert "Changed note to notify #4745" This reverts commit 5006adedfecedd16c07543d32acffe36aeaf61bb. Conflicts: src/test/unittests/ipc/IpcLogOutputterTests.cpp --- .../synergyc/MSWindowsClientTaskBarReceiver.cpp | 2 +- .../synergyp/MSWindowsPortableTaskBarReceiver.cpp | 2 +- .../synergys/MSWindowsServerTaskBarReceiver.cpp | 2 +- src/lib/base/ELevel.h | 2 +- src/lib/base/Log.h | 2 +- src/test/unittests/ipc/IpcLogOutputterTests.cpp | 36 ++++++++++++---------- 6 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp b/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp index 965d9eef9..7455d8c7e 100644 --- a/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp +++ b/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp @@ -187,7 +187,7 @@ MSWindowsClientTaskBarReceiver::runMenu(int x, int y) break; case IDC_TASKBAR_LOG_LEVEL_NOTE: - CLOG->setFilter(kNOTIFY); + CLOG->setFilter(kNOTE); break; case IDC_TASKBAR_LOG_LEVEL_INFO: diff --git a/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp b/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp index 3a6b4c5e3..c09234a2b 100644 --- a/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp +++ b/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp @@ -204,7 +204,7 @@ MSWindowsPortableTaskBarReceiver::runMenu(int x, int y) break; case IDC_TASKBAR_LOG_LEVEL_NOTE: - CLOG->setFilter(kNOTIFY); + CLOG->setFilter(kNOTE); break; case IDC_TASKBAR_LOG_LEVEL_INFO: diff --git a/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp b/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp index 83c73ce34..d7ced0737 100644 --- a/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp +++ b/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp @@ -218,7 +218,7 @@ MSWindowsServerTaskBarReceiver::runMenu(int x, int y) break; case IDC_TASKBAR_LOG_LEVEL_NOTE: - CLOG->setFilter(kNOTIFY); + CLOG->setFilter(kNOTE); break; case IDC_TASKBAR_LOG_LEVEL_INFO: diff --git a/src/lib/base/ELevel.h b/src/lib/base/ELevel.h index 6c66df6f6..c489dffd4 100644 --- a/src/lib/base/ELevel.h +++ b/src/lib/base/ELevel.h @@ -27,7 +27,7 @@ enum ELevel { kFATAL, //!< For fatal errors kERROR, //!< For serious errors kWARNING, //!< For minor errors and warnings - kNOTIFY, //!< For messages about notable events + kNOTE, //!< For messages about notable events kINFO, //!< For informational messages kDEBUG, //!< For important debugging messages kDEBUG1, //!< For verbosity +1 debugging messages diff --git a/src/lib/base/Log.h b/src/lib/base/Log.h index 097661302..eaa2c8a87 100644 --- a/src/lib/base/Log.h +++ b/src/lib/base/Log.h @@ -203,7 +203,7 @@ otherwise it expands to a call that doesn't. #define CLOG_CRIT CLOG_TRACE "%z\060" // char is '0' #define CLOG_ERR CLOG_TRACE "%z\061" #define CLOG_WARN CLOG_TRACE "%z\062" -#define CLOG_NOTIFY CLOG_TRACE "%z\063" +#define CLOG_NOTE CLOG_TRACE "%z\063" #define CLOG_INFO CLOG_TRACE "%z\064" #define CLOG_DEBUG CLOG_TRACE "%z\065" #define CLOG_DEBUG1 CLOG_TRACE "%z\066" diff --git a/src/test/unittests/ipc/IpcLogOutputterTests.cpp b/src/test/unittests/ipc/IpcLogOutputterTests.cpp index 5583a0cb4..444b506a8 100644 --- a/src/test/unittests/ipc/IpcLogOutputterTests.cpp +++ b/src/test/unittests/ipc/IpcLogOutputterTests.cpp @@ -57,9 +57,9 @@ TEST(IpcLogOutputterTests, write_threadingEnabled_bufferIsSent) EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\n"), _)).Times(1); IpcLogOutputter outputter(mockServer, true); - outputter.write(kNOTIFY, "mock 1"); + outputter.write(kNOTE, "mock 1"); mockServer.waitForSend(); - outputter.write(kNOTIFY, "mock 2"); + outputter.write(kNOTE, "mock 2"); mockServer.waitForSend(); } @@ -76,9 +76,9 @@ TEST(IpcLogOutputterTests, write_overBufferMaxSize_firstLineTruncated) outputter.bufferMaxSize(2); // log more lines than the buffer can contain - outputter.write(kNOTIFY, "mock 1"); - outputter.write(kNOTIFY, "mock 2"); - outputter.write(kNOTIFY, "mock 3"); + outputter.write(kNOTE, "mock 1"); + outputter.write(kNOTE, "mock 2"); + outputter.write(kNOTE, "mock 3"); outputter.sendBuffer(); } @@ -95,8 +95,8 @@ TEST(IpcLogOutputterTests, write_underBufferMaxSize_allLinesAreSent) outputter.bufferMaxSize(2); // log more lines than the buffer can contain - outputter.write(kNOTIFY, "mock 1"); - outputter.write(kNOTIFY, "mock 2"); + outputter.write(kNOTE, "mock 1"); + outputter.write(kNOTE, "mock 2"); outputter.sendBuffer(); } @@ -118,9 +118,10 @@ TEST(IpcLogOutputterTests, write_overBufferRateLimit_lastLineTruncated) outputter.bufferRateLimit(2, 1); // 1s // log 1 more line than the buffer can accept in time limit. - outputter.write(kNOTIFY, "mock 1"); - outputter.write(kNOTIFY, "mock 2"); - outputter.write(kNOTIFY, "mock 3"); + outputter.write(kNOTE, "mock 1"); + outputter.write(kNOTE, "mock 2"); + outputter.write(kNOTE, "mock 3"); + outputter.sendBuffer(); // after waiting the time limit send another to make sure @@ -128,9 +129,10 @@ TEST(IpcLogOutputterTests, write_overBufferRateLimit_lastLineTruncated) // HACK: sleep causes the unit test to fail intermittently, // so lets try 100ms (there must be a better way to solve this) ARCH->sleep(2); // 2s - outputter.write(kNOTIFY, "mock 4"); - outputter.write(kNOTIFY, "mock 5"); - outputter.write(kNOTIFY, "mock 6"); + outputter.write(kNOTE, "mock 4"); + outputter.write(kNOTE, "mock 5"); + outputter.write(kNOTE, "mock 6"); + outputter.sendBuffer(); } #endif @@ -149,14 +151,14 @@ TEST(IpcLogOutputterTests, write_underBufferRateLimit_allLinesAreSent) outputter.bufferRateLimit(4, 1); // 1s (should be plenty of time) // log 1 more line than the buffer can accept in time limit. - outputter.write(kNOTIFY, "mock 1"); - outputter.write(kNOTIFY, "mock 2"); + outputter.write(kNOTE, "mock 1"); + outputter.write(kNOTE, "mock 2"); outputter.sendBuffer(); // after waiting the time limit send another to make sure // we can log after the time limit passes. - outputter.write(kNOTIFY, "mock 3"); - outputter.write(kNOTIFY, "mock 4"); + outputter.write(kNOTE, "mock 3"); + outputter.write(kNOTE, "mock 4"); outputter.sendBuffer(); } From 81649376fa486f39ecd8f751abb67620438a7c5b Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 9 Jul 2015 13:46:17 -0700 Subject: [PATCH 013/572] Revert "Added symbolic link creation to linux and macos #4696" This reverts commit ad9cfd64af4f8f15623069c3381f78d7db13a734. Conflicts: src/gui/src/PluginManager.cpp --- src/lib/arch/unix/ArchLogUnix.cpp | 2 +- src/lib/client/Client.cpp | 2 +- src/lib/client/ServerProxy.cpp | 6 +++--- src/lib/server/ClientProxy1_5.cpp | 2 +- src/lib/server/ClientProxy1_6.cpp | 4 ++-- src/lib/synergy/ClipboardChunk.cpp | 4 ++-- src/lib/synergy/DropHelper.cpp | 6 +++--- src/lib/synergy/FileChunk.cpp | 2 +- src/lib/synergy/StreamChunker.cpp | 4 ++-- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/lib/arch/unix/ArchLogUnix.cpp b/src/lib/arch/unix/ArchLogUnix.cpp index 516ffa6af..a290463f0 100644 --- a/src/lib/arch/unix/ArchLogUnix.cpp +++ b/src/lib/arch/unix/ArchLogUnix.cpp @@ -66,7 +66,7 @@ ArchLogUnix::writeLog(ELevel level, const char* msg) priority = LOG_WARNING; break; - case kNOTIFY: + case kNOTE: priority = LOG_NOTICE; break; diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 5ba8b8e76..da8611d47 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -285,7 +285,7 @@ Client::leave() if (!m_receivedFileData.empty()) { m_receivedFileData.clear(); - LOG((CLOG_NOTIFY "file transmission interrupted")); + LOG((CLOG_DEBUG "file transmission interrupted")); } return true; diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index 70a8b26c7..ac01df358 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -565,11 +565,11 @@ ServerProxy::setClipboard() clipboard.unmarshall(dataCached, 0); m_client->setClipboard(id, &clipboard); - LOG((CLOG_NOTIFY "clipboard transmission complete")); + LOG((CLOG_DEBUG "clipboard transmission complete")); } else if (r == kStart) { size_t size = ClipboardChunk::getExpectedSize(); - LOG((CLOG_NOTIFY "clipboard transmission started: start receiving %u bytes of clipboard data", size)); + LOG((CLOG_DEBUG "clipboard transmission started: start receiving %u bytes of clipboard data", size)); } } @@ -873,7 +873,7 @@ ServerProxy::fileChunkReceived() else if (result == kStart) { if (m_client->getDragFileList().size() > 0) { String filename = m_client->getDragFileList().at(0).getFilename(); - LOG((CLOG_NOTIFY "file transmission started: start receiving %s", filename.c_str())); + LOG((CLOG_DEBUG "file transmission started: start receiving %s", filename.c_str())); } } } diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp index 2e98d7713..09c85995e 100644 --- a/src/lib/server/ClientProxy1_5.cpp +++ b/src/lib/server/ClientProxy1_5.cpp @@ -93,7 +93,7 @@ ClientProxy1_5::fileChunkReceived() else if (result == kStart) { if (server->getFakeDragFileList().size() > 0) { String filename = server->getFakeDragFileList().at(0).getFilename(); - LOG((CLOG_NOTIFY "file transmission started: start receiving %s", filename.c_str())); + LOG((CLOG_DEBUG "file transmission started: start receiving %s", filename.c_str())); } } } diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index 762341126..40c8eae80 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -91,11 +91,11 @@ ClientProxy1_6::recvClipboard() info->m_sequenceNumber = seq; m_events->addEvent(Event(m_events->forClipboard().clipboardChanged(), getEventTarget(), info)); - LOG((CLOG_NOTIFY "clipboard transmission complete")); + LOG((CLOG_DEBUG "clipboard transmission complete")); } else if (r == kStart) { size_t size = ClipboardChunk::getExpectedSize(); - LOG((CLOG_NOTIFY "clipboard transmission started: start receiving %u bytes of clipboard data", size)); + LOG((CLOG_DEBUG "clipboard transmission started: start receiving %u bytes of clipboard data", size)); } return true; diff --git a/src/lib/synergy/ClipboardChunk.cpp b/src/lib/synergy/ClipboardChunk.cpp index ef60954e4..b17a52a11 100644 --- a/src/lib/synergy/ClipboardChunk.cpp +++ b/src/lib/synergy/ClipboardChunk.cpp @@ -115,13 +115,13 @@ ClipboardChunk::assemble(synergy::IStream* stream, } else if (s_expectedSize != dataCached.size()) { LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", s_expectedSize, dataCached.size())); - LOG((CLOG_NOTIFY "clipboard transmission failed: corrupted clipboard data")); + LOG((CLOG_DEBUG "clipboard transmission failed: corrupted clipboard data")); return kError; } return kFinish; } - LOG((CLOG_NOTIFY "clipboard transmission failed: unknow error")); + LOG((CLOG_DEBUG "clipboard transmission failed: unknow error")); return kError; } diff --git a/src/lib/synergy/DropHelper.cpp b/src/lib/synergy/DropHelper.cpp index 631701281..314f8d142 100644 --- a/src/lib/synergy/DropHelper.cpp +++ b/src/lib/synergy/DropHelper.cpp @@ -38,18 +38,18 @@ DropHelper::writeToDir(const String& destination, DragFileList& fileList, String file.open(dropTarget.c_str(), std::ios::out | std::ios::binary); if (!file.is_open()) { LOG((CLOG_ERR "drop file failed: can not open %s", dropTarget.c_str())); - LOG((CLOG_NOTIFY "file transmission failed: can not open %s", dropTarget.c_str())); + LOG((CLOG_DEBUG "file transmission failed: can not open %s", dropTarget.c_str())); } file.write(data.c_str(), data.size()); file.close(); - LOG((CLOG_NOTIFY "file transmission complete: %s is saved to %s", fileList.at(0).getFilename().c_str(), destination.c_str())); + LOG((CLOG_DEBUG "file transmission complete: %s is saved to %s", fileList.at(0).getFilename().c_str(), destination.c_str())); fileList.clear(); } else { LOG((CLOG_ERR "drop file failed: drop target is empty")); - LOG((CLOG_NOTIFY "file transmission failed: drop target is empty")); + LOG((CLOG_DEBUG "file transmission failed: drop target is empty")); } } diff --git a/src/lib/synergy/FileChunk.cpp b/src/lib/synergy/FileChunk.cpp index 21c91bcac..441e15243 100644 --- a/src/lib/synergy/FileChunk.cpp +++ b/src/lib/synergy/FileChunk.cpp @@ -116,7 +116,7 @@ FileChunk::assemble(synergy::IStream* stream, String& dataReceived, size_t& expe case kDataEnd: if (expectedSize != dataReceived.size()) { LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", expectedSize, dataReceived.size())); - LOG((CLOG_NOTIFY "file transmission failed: corrupted file data")); + LOG((CLOG_DEBUG "file transmission failed: corrupted file data")); return kError; } diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index b23f4dbbd..080938bfa 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -82,7 +82,7 @@ StreamChunker::sendFile( while (true) { if (s_interruptFile) { s_interruptFile = false; - LOG((CLOG_NOTIFY "file transmission interrupted")); + LOG((CLOG_DEBUG "file transmission interrupted")); break; } @@ -154,7 +154,7 @@ StreamChunker::sendClipboard( while (true) { if (s_interruptClipboard) { s_interruptClipboard = false; - LOG((CLOG_NOTIFY "clipboard transmission interrupted")); + LOG((CLOG_DEBUG "clipboard transmission interrupted")); break; } From 485547d6ea948d492ea51f19c54c55d334856067 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 9 Jul 2015 13:52:36 -0700 Subject: [PATCH 014/572] Reverted Notify back to note #4855 --- src/lib/base/Log.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/base/Log.cpp b/src/lib/base/Log.cpp index 5c66020ee..257088f3e 100644 --- a/src/lib/base/Log.cpp +++ b/src/lib/base/Log.cpp @@ -33,7 +33,7 @@ static const char* g_priority[] = { "FATAL", "ERROR", "WARNING", - "NOTIFY", + "NOTE", "INFO", "DEBUG", "DEBUG1", From 4fe46e117e41163268d743417211a82c0db7f1a8 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 9 Jul 2015 14:10:52 -0700 Subject: [PATCH 015/572] Shorten data transfer log #4858 --- src/lib/client/ServerProxy.cpp | 4 ++-- src/lib/server/ClientProxy1_5.cpp | 2 +- src/lib/server/ClientProxy1_6.cpp | 2 +- src/lib/synergy/ClipboardChunk.cpp | 3 +-- src/lib/synergy/DropHelper.cpp | 4 +--- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index ac01df358..0722bdfd4 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -569,7 +569,7 @@ ServerProxy::setClipboard() } else if (r == kStart) { size_t size = ClipboardChunk::getExpectedSize(); - LOG((CLOG_DEBUG "clipboard transmission started: start receiving %u bytes of clipboard data", size)); + LOG((CLOG_DEBUG "receiving %u bytes of clipboard data", size)); } } @@ -873,7 +873,7 @@ ServerProxy::fileChunkReceived() else if (result == kStart) { if (m_client->getDragFileList().size() > 0) { String filename = m_client->getDragFileList().at(0).getFilename(); - LOG((CLOG_DEBUG "file transmission started: start receiving %s", filename.c_str())); + LOG((CLOG_DEBUG "start receiving %s", filename.c_str())); } } } diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp index 09c85995e..2cdceb400 100644 --- a/src/lib/server/ClientProxy1_5.cpp +++ b/src/lib/server/ClientProxy1_5.cpp @@ -93,7 +93,7 @@ ClientProxy1_5::fileChunkReceived() else if (result == kStart) { if (server->getFakeDragFileList().size() > 0) { String filename = server->getFakeDragFileList().at(0).getFilename(); - LOG((CLOG_DEBUG "file transmission started: start receiving %s", filename.c_str())); + LOG((CLOG_DEBUG "start receiving %s", filename.c_str())); } } } diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index 40c8eae80..9df8808db 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -95,7 +95,7 @@ ClientProxy1_6::recvClipboard() } else if (r == kStart) { size_t size = ClipboardChunk::getExpectedSize(); - LOG((CLOG_DEBUG "clipboard transmission started: start receiving %u bytes of clipboard data", size)); + LOG((CLOG_DEBUG "receiving %u bytes of clipboard data", size)); } return true; diff --git a/src/lib/synergy/ClipboardChunk.cpp b/src/lib/synergy/ClipboardChunk.cpp index b17a52a11..d3d6de282 100644 --- a/src/lib/synergy/ClipboardChunk.cpp +++ b/src/lib/synergy/ClipboardChunk.cpp @@ -115,13 +115,12 @@ ClipboardChunk::assemble(synergy::IStream* stream, } else if (s_expectedSize != dataCached.size()) { LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", s_expectedSize, dataCached.size())); - LOG((CLOG_DEBUG "clipboard transmission failed: corrupted clipboard data")); return kError; } return kFinish; } - LOG((CLOG_DEBUG "clipboard transmission failed: unknow error")); + LOG((CLOG_ERR "clipboard transmission failed: unknow error")); return kError; } diff --git a/src/lib/synergy/DropHelper.cpp b/src/lib/synergy/DropHelper.cpp index 314f8d142..a5fde5f1b 100644 --- a/src/lib/synergy/DropHelper.cpp +++ b/src/lib/synergy/DropHelper.cpp @@ -38,18 +38,16 @@ DropHelper::writeToDir(const String& destination, DragFileList& fileList, String file.open(dropTarget.c_str(), std::ios::out | std::ios::binary); if (!file.is_open()) { LOG((CLOG_ERR "drop file failed: can not open %s", dropTarget.c_str())); - LOG((CLOG_DEBUG "file transmission failed: can not open %s", dropTarget.c_str())); } file.write(data.c_str(), data.size()); file.close(); - LOG((CLOG_DEBUG "file transmission complete: %s is saved to %s", fileList.at(0).getFilename().c_str(), destination.c_str())); + LOG((CLOG_DEBUG "%s is saved to %s", fileList.at(0).getFilename().c_str(), destination.c_str())); fileList.clear(); } else { LOG((CLOG_ERR "drop file failed: drop target is empty")); - LOG((CLOG_DEBUG "file transmission failed: drop target is empty")); } } From 44966ee17fa3c44b680e19ba558e100603b3105c Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 9 Jul 2015 14:29:23 -0700 Subject: [PATCH 016/572] Removed redundant log #4858 --- src/lib/synergy/FileChunk.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/synergy/FileChunk.cpp b/src/lib/synergy/FileChunk.cpp index 441e15243..ad1392af4 100644 --- a/src/lib/synergy/FileChunk.cpp +++ b/src/lib/synergy/FileChunk.cpp @@ -116,7 +116,6 @@ FileChunk::assemble(synergy::IStream* stream, String& dataReceived, size_t& expe case kDataEnd: if (expectedSize != dataReceived.size()) { LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", expectedSize, dataReceived.size())); - LOG((CLOG_DEBUG "file transmission failed: corrupted file data")); return kError; } From 0ddf544efb37cfc0af23357109d1945f5459fc36 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 9 Jul 2015 15:02:55 -0700 Subject: [PATCH 017/572] Used static variable than define #4750 Conflicts: src/lib/plugin/ns/SecureSocket.cpp --- src/lib/plugin/ns/SecureSocket.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 899b11ad7..24b7b82d4 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -36,11 +36,9 @@ #define MAX_ERROR_SIZE 65535 -enum { - // this limit seems extremely high, but mac client seem to generate around - // 50,000 errors before they establish a connection (wtf?) - kMaxRetryCount = 100000 -}; +// g_retryDelay * g_maxRetry = 10s +static const int g_maxRetry = 1000; +static const float g_retryDelay = 0.01f; enum { kMsgSize = 128 @@ -318,6 +316,7 @@ SecureSocket::secureAccept(int socket) if (retry > 0) { LOG((CLOG_DEBUG2 "retry accepting secure socket")); m_secureReady = false; + ARCH->sleep(g_retryDelay); return 0; } @@ -351,6 +350,7 @@ SecureSocket::secureConnect(int socket) if (retry > 0) { LOG((CLOG_DEBUG2 "retry connect secure socket")); m_secureReady = false; + ARCH->sleep(g_retryDelay); return 0; } @@ -475,8 +475,8 @@ SecureSocket::checkResult(int status, int& retry) } // If the retry max would exceed the allowed, treat it as a fatal error - if (retry > maxRetry()) { - LOG((CLOG_ERR "passive ssl error limit exceeded: %d", retry)); + if (retry > g_maxRetry) { + LOG((CLOG_DEBUG "retry exceeded %f sec", g_maxRetry * g_retryDelay)); isFatal(true); } From f8bb948776bd686dbd7f86b76a3d100490e474f3 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 31 Jul 2015 13:42:41 -0700 Subject: [PATCH 018/572] Removed unused variable #4750 --- src/lib/plugin/ns/SecureSocket.cpp | 6 ++---- src/lib/plugin/ns/SecureSocket.h | 3 --- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 24b7b82d4..81c95c838 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -59,8 +59,7 @@ SecureSocket::SecureSocket( SocketMultiplexer* socketMultiplexer) : TCPSocket(events, socketMultiplexer), m_secureReady(false), - m_fatal(false), - m_maxRetry(kMaxRetryCount) + m_fatal(false) { } @@ -70,8 +69,7 @@ SecureSocket::SecureSocket( ArchSocket socket) : TCPSocket(events, socketMultiplexer, socket), m_secureReady(false), - m_fatal(false), - m_maxRetry(kMaxRetryCount) + m_fatal(false) { } diff --git a/src/lib/plugin/ns/SecureSocket.h b/src/lib/plugin/ns/SecureSocket.h index 0c0f3b10a..871e1e4d7 100644 --- a/src/lib/plugin/ns/SecureSocket.h +++ b/src/lib/plugin/ns/SecureSocket.h @@ -52,8 +52,6 @@ class SecureSocket : public TCPSocket { int secureWrite(const void* buffer, int size, int& wrote); void initSsl(bool server); bool loadCertificates(String& CertFile); - void maxRetry(int limit) { m_maxRetry = limit; }; - int maxRetry() const { return m_maxRetry; }; private: // SSL @@ -87,5 +85,4 @@ class SecureSocket : public TCPSocket { Ssl* m_ssl; bool m_secureReady; bool m_fatal; - int m_maxRetry; }; From d51eb7b8b50258ef8ff42d68c7eeb0ee75ceeb6d Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 10 Jul 2015 12:37:30 -0700 Subject: [PATCH 019/572] Fixed code style --- src/lib/plugin/ns/SecureSocket.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 81c95c838..2e181ae67 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -36,9 +36,9 @@ #define MAX_ERROR_SIZE 65535 -// g_retryDelay * g_maxRetry = 10s -static const int g_maxRetry = 1000; -static const float g_retryDelay = 0.01f; +// s_retryDelay * s_maxRetry = 10s +static const int s_maxRetry = 1000; +static const float s_retryDelay = 0.01f; enum { kMsgSize = 128 @@ -314,7 +314,7 @@ SecureSocket::secureAccept(int socket) if (retry > 0) { LOG((CLOG_DEBUG2 "retry accepting secure socket")); m_secureReady = false; - ARCH->sleep(g_retryDelay); + ARCH->sleep(s_retryDelay); return 0; } @@ -348,7 +348,7 @@ SecureSocket::secureConnect(int socket) if (retry > 0) { LOG((CLOG_DEBUG2 "retry connect secure socket")); m_secureReady = false; - ARCH->sleep(g_retryDelay); + ARCH->sleep(s_retryDelay); return 0; } @@ -473,8 +473,8 @@ SecureSocket::checkResult(int status, int& retry) } // If the retry max would exceed the allowed, treat it as a fatal error - if (retry > g_maxRetry) { - LOG((CLOG_DEBUG "retry exceeded %f sec", g_maxRetry * g_retryDelay)); + if (retry > s_maxRetry) { + LOG((CLOG_DEBUG "retry exceeded %f sec", s_maxRetry * s_retryDelay)); isFatal(true); } From c2b96cfbb721640f50ddc76a21cbb3b81d2f7674 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 10 Jul 2015 12:40:01 -0700 Subject: [PATCH 020/572] Updated comment #4750 --- src/lib/plugin/ns/SecureSocket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 2e181ae67..80bad6dcc 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -36,7 +36,7 @@ #define MAX_ERROR_SIZE 65535 -// s_retryDelay * s_maxRetry = 10s +// maxmium retry time limit set to 10s static const int s_maxRetry = 1000; static const float s_retryDelay = 0.01f; From 9800bec8577636e889014c8b7af0508f9fb2c9b7 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 10 Jul 2015 12:57:05 -0700 Subject: [PATCH 021/572] Made clipboard log more consistent #4712 --- src/lib/client/ServerProxy.cpp | 4 ++-- src/lib/server/ClientProxy1_6.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index 0722bdfd4..ac50815bf 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -565,11 +565,11 @@ ServerProxy::setClipboard() clipboard.unmarshall(dataCached, 0); m_client->setClipboard(id, &clipboard); - LOG((CLOG_DEBUG "clipboard transmission complete")); + LOG((CLOG_INFO "clipboard was updated")); } else if (r == kStart) { size_t size = ClipboardChunk::getExpectedSize(); - LOG((CLOG_DEBUG "receiving %u bytes of clipboard data", size)); + LOG((CLOG_DEBUG "receiving clipboard %d size=%d", id, size)); } } diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index 9df8808db..60d4f0c02 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -91,11 +91,10 @@ ClientProxy1_6::recvClipboard() info->m_sequenceNumber = seq; m_events->addEvent(Event(m_events->forClipboard().clipboardChanged(), getEventTarget(), info)); - LOG((CLOG_DEBUG "clipboard transmission complete")); } else if (r == kStart) { size_t size = ClipboardChunk::getExpectedSize(); - LOG((CLOG_DEBUG "receiving %u bytes of clipboard data", size)); + LOG((CLOG_DEBUG "receiving clipboard %d size=%d", id, size)); } return true; From a44e9832c5deea04c4596e3909661dc5827973e6 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 10 Jul 2015 12:59:12 -0700 Subject: [PATCH 022/572] Refactored code order to make it more readable --- src/lib/client/ServerProxy.cpp | 10 +++++----- src/lib/server/ClientProxy1_6.cpp | 13 +++++++------ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index ac50815bf..9c2b9c75a 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -557,7 +557,11 @@ ServerProxy::setClipboard() int r = ClipboardChunk::assemble(m_stream, dataCached, id, seq); - if (r == kFinish) { + if (r == kStart) { + size_t size = ClipboardChunk::getExpectedSize(); + LOG((CLOG_DEBUG "receiving clipboard %d size=%d", id, size)); + } + else if (r == kFinish) { LOG((CLOG_DEBUG "received clipboard %d size=%d", id, dataCached.size())); // forward @@ -567,10 +571,6 @@ ServerProxy::setClipboard() LOG((CLOG_INFO "clipboard was updated")); } - else if (r == kStart) { - size_t size = ClipboardChunk::getExpectedSize(); - LOG((CLOG_DEBUG "receiving clipboard %d size=%d", id, size)); - } } void diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index 60d4f0c02..bdd0f23fb 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -79,8 +79,13 @@ ClientProxy1_6::recvClipboard() int r = ClipboardChunk::assemble(getStream(), dataCached, id, seq); - if (r == kFinish) { - LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seq, dataCached.size())); + if (r == kStart) { + size_t size = ClipboardChunk::getExpectedSize(); + LOG((CLOG_DEBUG "receiving clipboard %d size=%d", id, size)); + } + else if (r == kFinish) { + LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", + getName().c_str(), id, seq, dataCached.size())); // save clipboard m_clipboard[id].m_clipboard.unmarshall(dataCached, 0); m_clipboard[id].m_sequenceNumber = seq; @@ -92,10 +97,6 @@ ClientProxy1_6::recvClipboard() m_events->addEvent(Event(m_events->forClipboard().clipboardChanged(), getEventTarget(), info)); } - else if (r == kStart) { - size_t size = ClipboardChunk::getExpectedSize(); - LOG((CLOG_DEBUG "receiving clipboard %d size=%d", id, size)); - } return true; } From cc3dc315f9254e9e3cb05291afda07194f6bfcc2 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 10 Jul 2015 13:22:34 -0700 Subject: [PATCH 023/572] Allowed reconnect after SSL fatal error #4857 --- src/lib/plugin/ns/SecureSocket.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 80bad6dcc..034a7f778 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -516,7 +516,6 @@ SecureSocket::getError() void SecureSocket::disconnect() { - sendEvent(getEvents()->forISocket().stopRetry()); sendEvent(getEvents()->forISocket().disconnected()); sendEvent(getEvents()->forIStream().inputShutdown()); } From afdcb9cefe22358617d20de1478976baea286e6e Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Mon, 13 Jul 2015 11:00:10 +0100 Subject: [PATCH 024/572] Made buffer thread wait until there is a GUI client #4721 Conflicts: src/lib/ipc/IpcLogOutputter.cpp --- src/lib/ipc/IpcLogOutputter.cpp | 16 ++++++++-------- src/lib/ipc/IpcLogOutputter.h | 4 +++- src/lib/synergy/DaemonApp.cpp | 2 +- src/test/unittests/ipc/IpcLogOutputterTests.cpp | 17 ++++++++--------- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/lib/ipc/IpcLogOutputter.cpp b/src/lib/ipc/IpcLogOutputter.cpp index 276e0a941..b61c5386b 100644 --- a/src/lib/ipc/IpcLogOutputter.cpp +++ b/src/lib/ipc/IpcLogOutputter.cpp @@ -37,7 +37,7 @@ enum EIpcLogOutputter { kBufferRateTimeLimit = 1 // seconds }; -IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer, bool useThread) : +IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer, EIpcClientType clientType, bool useThread) : m_ipcServer(ipcServer), m_bufferMutex(ARCH->newMutex()), m_sending(false), @@ -50,7 +50,8 @@ IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer, bool useThread) : m_bufferRateWriteLimit(kBufferRateWriteLimit), m_bufferRateTimeLimit(kBufferRateTimeLimit), m_bufferWriteCount(0), - m_bufferRateStart(ARCH->time()) + m_bufferRateStart(ARCH->time()), + m_clientType(clientType) { if (useThread) { m_bufferThread = new Thread(new TMethodJob( @@ -102,7 +103,9 @@ IpcLogOutputter::write(ELevel, const char* text) } appendBuffer(text); - notifyBuffer(); + if (m_ipcServer.hasClients(m_clientType)) { + notifyBuffer(); + } return true; } @@ -141,7 +144,7 @@ IpcLogOutputter::bufferThread(void*) try { while (m_running) { - if (m_buffer.empty()) { + if (m_buffer.empty() || !m_ipcServer.hasClients(m_clientType)) { ArchMutexLock lock(m_notifyMutex); ARCH->waitCondVar(m_notifyCond, m_notifyMutex, -1); } @@ -184,10 +187,7 @@ IpcLogOutputter::getChunk(size_t count) void IpcLogOutputter::sendBuffer() { - if (m_buffer.empty() || !m_ipcServer.hasClients(kIpcClientGui)) { - if (!m_buffer.empty()) { - m_buffer.clear(); - } + if (m_buffer.empty() || !m_ipcServer.hasClients(m_clientType)) { return; } diff --git a/src/lib/ipc/IpcLogOutputter.h b/src/lib/ipc/IpcLogOutputter.h index a6a476a64..f5e771a56 100644 --- a/src/lib/ipc/IpcLogOutputter.h +++ b/src/lib/ipc/IpcLogOutputter.h @@ -21,6 +21,7 @@ #include "arch/Arch.h" #include "arch/IArchMultithread.h" #include "base/ILogOutputter.h" +#include "ipc/Ipc.h" #include @@ -39,7 +40,7 @@ class IpcLogOutputter : public ILogOutputter { If \p useThread is \c false, then the buffer needs to be sent manually using the \c sendBuffer() function. */ - IpcLogOutputter(IpcServer& ipcServer, bool useThread); + IpcLogOutputter(IpcServer& ipcServer, EIpcClientType clientType, bool useThread); virtual ~IpcLogOutputter(); // ILogOutputter overrides @@ -113,4 +114,5 @@ class IpcLogOutputter : public ILogOutputter { UInt16 m_bufferWriteCount; double m_bufferRateStart; bool m_useThread; + EIpcClientType m_clientType; }; diff --git a/src/lib/synergy/DaemonApp.cpp b/src/lib/synergy/DaemonApp.cpp index 15ff14dff..bb1ef91f8 100644 --- a/src/lib/synergy/DaemonApp.cpp +++ b/src/lib/synergy/DaemonApp.cpp @@ -211,7 +211,7 @@ DaemonApp::mainLoop(bool logToFile) m_ipcServer = new IpcServer(m_events, &multiplexer); // send logging to gui via ipc, log system adopts outputter. - m_ipcLogOutputter = new IpcLogOutputter(*m_ipcServer, true); + m_ipcLogOutputter = new IpcLogOutputter(*m_ipcServer, kIpcClientGui, true); CLOG->insert(m_ipcLogOutputter); #if SYSAPI_WIN32 diff --git a/src/test/unittests/ipc/IpcLogOutputterTests.cpp b/src/test/unittests/ipc/IpcLogOutputterTests.cpp index 444b506a8..72c75b492 100644 --- a/src/test/unittests/ipc/IpcLogOutputterTests.cpp +++ b/src/test/unittests/ipc/IpcLogOutputterTests.cpp @@ -52,11 +52,11 @@ TEST(IpcLogOutputterTests, write_threadingEnabled_bufferIsSent) ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); - EXPECT_CALL(mockServer, hasClients(_)).Times(2); + EXPECT_CALL(mockServer, hasClients(_)).Times(5); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\n"), _)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\n"), _)).Times(1); - IpcLogOutputter outputter(mockServer, true); + IpcLogOutputter outputter(mockServer, kIpcClientUnknown, true); outputter.write(kNOTE, "mock 1"); mockServer.waitForSend(); outputter.write(kNOTE, "mock 2"); @@ -68,11 +68,10 @@ TEST(IpcLogOutputterTests, write_overBufferMaxSize_firstLineTruncated) MockIpcServer mockServer; ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); - - EXPECT_CALL(mockServer, hasClients(_)).Times(1); + EXPECT_CALL(mockServer, hasClients(_)).Times(4); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\nmock 3\n"), _)).Times(1); - IpcLogOutputter outputter(mockServer, false); + IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false); outputter.bufferMaxSize(2); // log more lines than the buffer can contain @@ -88,10 +87,10 @@ TEST(IpcLogOutputterTests, write_underBufferMaxSize_allLinesAreSent) ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); - EXPECT_CALL(mockServer, hasClients(_)).Times(1); + EXPECT_CALL(mockServer, hasClients(_)).Times(3); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\nmock 2\n"), _)).Times(1); - IpcLogOutputter outputter(mockServer, false); + IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false); outputter.bufferMaxSize(2); // log more lines than the buffer can contain @@ -143,11 +142,11 @@ TEST(IpcLogOutputterTests, write_underBufferRateLimit_allLinesAreSent) ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); - EXPECT_CALL(mockServer, hasClients(_)).Times(2); + EXPECT_CALL(mockServer, hasClients(_)).Times(6); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\nmock 2\n"), _)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 3\nmock 4\n"), _)).Times(1); - IpcLogOutputter outputter(mockServer, false); + IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false); outputter.bufferRateLimit(4, 1); // 1s (should be plenty of time) // log 1 more line than the buffer can accept in time limit. From 88214a0d3c5b55f85b16c6c3118938fc6e8aaa54 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Mon, 13 Jul 2015 13:21:31 +0100 Subject: [PATCH 025/572] Removed unrelated checking #4721 --- src/test/unittests/ipc/IpcLogOutputterTests.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/unittests/ipc/IpcLogOutputterTests.cpp b/src/test/unittests/ipc/IpcLogOutputterTests.cpp index 72c75b492..0e8f14cf2 100644 --- a/src/test/unittests/ipc/IpcLogOutputterTests.cpp +++ b/src/test/unittests/ipc/IpcLogOutputterTests.cpp @@ -52,7 +52,6 @@ TEST(IpcLogOutputterTests, write_threadingEnabled_bufferIsSent) ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); - EXPECT_CALL(mockServer, hasClients(_)).Times(5); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\n"), _)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\n"), _)).Times(1); From 5a9cbc97e3c978e462b7778f308e315cbbc2c57b Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Tue, 14 Jul 2015 15:11:12 +0100 Subject: [PATCH 026/572] Fixed code style Conflicts: src/lib/arch/win32/ArchPluginWindows.cpp --- src/gui/src/MainWindow.cpp | 2 +- src/lib/arch/win32/ArchPluginWindows.cpp | 2 +- src/lib/platform/MSWindowsKeyState.cpp | 8 ++++---- src/lib/platform/XWindowsEventQueueBuffer.cpp | 2 +- src/lib/plugin/winmmjoy/winmmjoy.cpp | 2 +- src/lib/server/Server.cpp | 2 +- src/lib/synergy/ServerApp.cpp | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index a4c8f0b36..8263d5ea6 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -1351,7 +1351,7 @@ void MainWindow::delay(unsigned int s) { QTime dieTime= QTime::currentTime().addSecs(s); - while( QTime::currentTime() < dieTime ) { + while (QTime::currentTime() < dieTime) { QCoreApplication::processEvents(QEventLoop::AllEvents, 100); } } diff --git a/src/lib/arch/win32/ArchPluginWindows.cpp b/src/lib/arch/win32/ArchPluginWindows.cpp index 37440d7ce..bd0f6832a 100644 --- a/src/lib/arch/win32/ArchPluginWindows.cpp +++ b/src/lib/arch/win32/ArchPluginWindows.cpp @@ -69,8 +69,8 @@ ArchPluginWindows::load() void* lib = reinterpret_cast(library); String filename = synergy::string::removeFileExt(*it); m_pluginTable.insert(std::make_pair(filename, lib)); + const char* version = (char*)invoke(filename.c_str(), "version",NULL); - const char * version = (char*)invoke( filename.c_str(),"version",NULL); if (version == NULL) { version = kPre174Plugin; } diff --git a/src/lib/platform/MSWindowsKeyState.cpp b/src/lib/platform/MSWindowsKeyState.cpp index 863dae684..2b66a048b 100644 --- a/src/lib/platform/MSWindowsKeyState.cpp +++ b/src/lib/platform/MSWindowsKeyState.cpp @@ -810,11 +810,11 @@ MSWindowsKeyState::fakeCtrlAltDel() // current thread must be on that desktop to do the broadcast // and we can't switch just any thread because some own windows // or hooks. so start a new thread to do the real work. - HANDLE hEvtSendSas = OpenEvent( EVENT_MODIFY_STATE, FALSE, "Global\\SendSAS" ); - if ( hEvtSendSas ) { + HANDLE hEvtSendSas = OpenEvent(EVENT_MODIFY_STATE, FALSE, "Global\\SendSAS"); + if (hEvtSendSas) { LOG((CLOG_DEBUG "found the SendSAS event - signaling my launcher to simulate ctrl+alt+del")); - SetEvent( hEvtSendSas ); - CloseHandle( hEvtSendSas ); + SetEvent(hEvtSendSas); + CloseHandle(hEvtSendSas); } else { Thread cad(new FunctionJob(&MSWindowsKeyState::ctrlAltDelThread)); diff --git a/src/lib/platform/XWindowsEventQueueBuffer.cpp b/src/lib/platform/XWindowsEventQueueBuffer.cpp index 6376213e3..49836e230 100644 --- a/src/lib/platform/XWindowsEventQueueBuffer.cpp +++ b/src/lib/platform/XWindowsEventQueueBuffer.cpp @@ -162,7 +162,7 @@ XWindowsEventQueueBuffer::waitForEvent(double dtimeout) // we want to give the cpu a chance s owe up this to 25ms #define TIMEOUT_DELAY 25 - while( ((dtimeout < 0.0) || (remaining > 0)) && QLength(m_display)==0 && retval==0){ + while (((dtimeout < 0.0) || (remaining > 0)) && QLength(m_display)==0 && retval==0){ #if HAVE_POLL retval = poll(pfds, 2, TIMEOUT_DELAY); //16ms = 60hz, but we make it > to play nicely with the cpu if (pfds[1].revents & POLLIN) { diff --git a/src/lib/plugin/winmmjoy/winmmjoy.cpp b/src/lib/plugin/winmmjoy/winmmjoy.cpp index ab3e5a597..40dddedef 100644 --- a/src/lib/plugin/winmmjoy/winmmjoy.cpp +++ b/src/lib/plugin/winmmjoy/winmmjoy.cpp @@ -28,7 +28,7 @@ std::stringstream _logStream; #define LOG(s) \ _logStream.str(""); \ _logStream << "winmmjoy: " << s << std::endl; \ - s_log( _logStream.str().c_str()) + s_log(_logStream.str().c_str()) static bool s_running = true; static void (*s_sendEvent)(const char*, void*) = NULL; diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 93e1b4503..1baf61506 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -886,7 +886,7 @@ Server::isSwitchOkay(BaseClientProxy* newScreen, } // check for optional needed modifiers - KeyModifierMask mods = this->m_primaryClient->getToggleMask( ); + KeyModifierMask mods = this->m_primaryClient->getToggleMask(); if (!preventSwitch && ( (this->m_switchNeedsShift && ((mods & KeyModifierShift) != KeyModifierShift)) || diff --git a/src/lib/synergy/ServerApp.cpp b/src/lib/synergy/ServerApp.cpp index ae9246dfd..c4de7dd33 100644 --- a/src/lib/synergy/ServerApp.cpp +++ b/src/lib/synergy/ServerApp.cpp @@ -317,7 +317,7 @@ ServerApp::updateStatus() updateStatus(""); } -void ServerApp::updateStatus( const String& msg ) +void ServerApp::updateStatus(const String& msg) { if (m_taskBarReceiver) { From 01526bbe7838ec8479a25661945bf00f05b02a14 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 15 Jul 2015 11:57:04 -0700 Subject: [PATCH 027/572] Revert "Removed unrelated checking #4721" This reverts commit 2de276cfca71a79df31a1d4a89d412212279a1a5. --- src/test/unittests/ipc/IpcLogOutputterTests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/unittests/ipc/IpcLogOutputterTests.cpp b/src/test/unittests/ipc/IpcLogOutputterTests.cpp index 0e8f14cf2..72c75b492 100644 --- a/src/test/unittests/ipc/IpcLogOutputterTests.cpp +++ b/src/test/unittests/ipc/IpcLogOutputterTests.cpp @@ -52,6 +52,7 @@ TEST(IpcLogOutputterTests, write_threadingEnabled_bufferIsSent) ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); + EXPECT_CALL(mockServer, hasClients(_)).Times(5); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\n"), _)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\n"), _)).Times(1); From 5ec9ccc76e527c41ffbf906822b5cbd12d5ea57a Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 15 Jul 2015 11:57:33 -0700 Subject: [PATCH 028/572] Revert "Made buffer thread wait until there is a GUI client #4721" This reverts commit 9a4327e44236c0ac30809660dc87a97e984dc84f. --- src/lib/ipc/IpcLogOutputter.cpp | 13 +++++-------- src/lib/ipc/IpcLogOutputter.h | 4 +--- src/lib/synergy/DaemonApp.cpp | 2 +- src/test/unittests/ipc/IpcLogOutputterTests.cpp | 17 +++++++++-------- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/lib/ipc/IpcLogOutputter.cpp b/src/lib/ipc/IpcLogOutputter.cpp index b61c5386b..6ce8d774f 100644 --- a/src/lib/ipc/IpcLogOutputter.cpp +++ b/src/lib/ipc/IpcLogOutputter.cpp @@ -37,7 +37,7 @@ enum EIpcLogOutputter { kBufferRateTimeLimit = 1 // seconds }; -IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer, EIpcClientType clientType, bool useThread) : +IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer, bool useThread) : m_ipcServer(ipcServer), m_bufferMutex(ARCH->newMutex()), m_sending(false), @@ -50,8 +50,7 @@ IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer, EIpcClientType clientType m_bufferRateWriteLimit(kBufferRateWriteLimit), m_bufferRateTimeLimit(kBufferRateTimeLimit), m_bufferWriteCount(0), - m_bufferRateStart(ARCH->time()), - m_clientType(clientType) + m_bufferRateStart(ARCH->time()) { if (useThread) { m_bufferThread = new Thread(new TMethodJob( @@ -103,9 +102,7 @@ IpcLogOutputter::write(ELevel, const char* text) } appendBuffer(text); - if (m_ipcServer.hasClients(m_clientType)) { - notifyBuffer(); - } + notifyBuffer(); return true; } @@ -144,7 +141,7 @@ IpcLogOutputter::bufferThread(void*) try { while (m_running) { - if (m_buffer.empty() || !m_ipcServer.hasClients(m_clientType)) { + if (m_buffer.empty()) { ArchMutexLock lock(m_notifyMutex); ARCH->waitCondVar(m_notifyCond, m_notifyMutex, -1); } @@ -187,7 +184,7 @@ IpcLogOutputter::getChunk(size_t count) void IpcLogOutputter::sendBuffer() { - if (m_buffer.empty() || !m_ipcServer.hasClients(m_clientType)) { + if (m_buffer.empty() || !m_ipcServer.hasClients(kIpcClientGui)) { return; } diff --git a/src/lib/ipc/IpcLogOutputter.h b/src/lib/ipc/IpcLogOutputter.h index f5e771a56..a6a476a64 100644 --- a/src/lib/ipc/IpcLogOutputter.h +++ b/src/lib/ipc/IpcLogOutputter.h @@ -21,7 +21,6 @@ #include "arch/Arch.h" #include "arch/IArchMultithread.h" #include "base/ILogOutputter.h" -#include "ipc/Ipc.h" #include @@ -40,7 +39,7 @@ class IpcLogOutputter : public ILogOutputter { If \p useThread is \c false, then the buffer needs to be sent manually using the \c sendBuffer() function. */ - IpcLogOutputter(IpcServer& ipcServer, EIpcClientType clientType, bool useThread); + IpcLogOutputter(IpcServer& ipcServer, bool useThread); virtual ~IpcLogOutputter(); // ILogOutputter overrides @@ -114,5 +113,4 @@ class IpcLogOutputter : public ILogOutputter { UInt16 m_bufferWriteCount; double m_bufferRateStart; bool m_useThread; - EIpcClientType m_clientType; }; diff --git a/src/lib/synergy/DaemonApp.cpp b/src/lib/synergy/DaemonApp.cpp index bb1ef91f8..15ff14dff 100644 --- a/src/lib/synergy/DaemonApp.cpp +++ b/src/lib/synergy/DaemonApp.cpp @@ -211,7 +211,7 @@ DaemonApp::mainLoop(bool logToFile) m_ipcServer = new IpcServer(m_events, &multiplexer); // send logging to gui via ipc, log system adopts outputter. - m_ipcLogOutputter = new IpcLogOutputter(*m_ipcServer, kIpcClientGui, true); + m_ipcLogOutputter = new IpcLogOutputter(*m_ipcServer, true); CLOG->insert(m_ipcLogOutputter); #if SYSAPI_WIN32 diff --git a/src/test/unittests/ipc/IpcLogOutputterTests.cpp b/src/test/unittests/ipc/IpcLogOutputterTests.cpp index 72c75b492..444b506a8 100644 --- a/src/test/unittests/ipc/IpcLogOutputterTests.cpp +++ b/src/test/unittests/ipc/IpcLogOutputterTests.cpp @@ -52,11 +52,11 @@ TEST(IpcLogOutputterTests, write_threadingEnabled_bufferIsSent) ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); - EXPECT_CALL(mockServer, hasClients(_)).Times(5); + EXPECT_CALL(mockServer, hasClients(_)).Times(2); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\n"), _)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\n"), _)).Times(1); - IpcLogOutputter outputter(mockServer, kIpcClientUnknown, true); + IpcLogOutputter outputter(mockServer, true); outputter.write(kNOTE, "mock 1"); mockServer.waitForSend(); outputter.write(kNOTE, "mock 2"); @@ -68,10 +68,11 @@ TEST(IpcLogOutputterTests, write_overBufferMaxSize_firstLineTruncated) MockIpcServer mockServer; ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); - EXPECT_CALL(mockServer, hasClients(_)).Times(4); + + EXPECT_CALL(mockServer, hasClients(_)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\nmock 3\n"), _)).Times(1); - IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false); + IpcLogOutputter outputter(mockServer, false); outputter.bufferMaxSize(2); // log more lines than the buffer can contain @@ -87,10 +88,10 @@ TEST(IpcLogOutputterTests, write_underBufferMaxSize_allLinesAreSent) ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); - EXPECT_CALL(mockServer, hasClients(_)).Times(3); + EXPECT_CALL(mockServer, hasClients(_)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\nmock 2\n"), _)).Times(1); - IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false); + IpcLogOutputter outputter(mockServer, false); outputter.bufferMaxSize(2); // log more lines than the buffer can contain @@ -142,11 +143,11 @@ TEST(IpcLogOutputterTests, write_underBufferRateLimit_allLinesAreSent) ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); - EXPECT_CALL(mockServer, hasClients(_)).Times(6); + EXPECT_CALL(mockServer, hasClients(_)).Times(2); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\nmock 2\n"), _)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 3\nmock 4\n"), _)).Times(1); - IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false); + IpcLogOutputter outputter(mockServer, false); outputter.bufferRateLimit(4, 1); // 1s (should be plenty of time) // log 1 more line than the buffer can accept in time limit. From 516c692c943375ee915284c8ff9d70d73725b4a9 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 16 Jul 2015 12:19:58 -0700 Subject: [PATCH 029/572] Removed unused function #4745 --- src/gui/src/MainWindow.cpp | 17 ----------------- src/gui/src/MainWindow.h | 1 - 2 files changed, 18 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 8263d5ea6..aebf134a1 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -398,7 +398,6 @@ void MainWindow::updateStateFromLogLine(const QString &line) { checkConnected(line); checkFingerprint(line); - checkTransmission(line); } void MainWindow::checkConnected(const QString& line) @@ -465,22 +464,6 @@ void MainWindow::checkFingerprint(const QString& line) } } -void MainWindow::checkTransmission(const QString& line) -{ - if (appConfig().logLevel() >= 2) { - if (line.contains("transmission")) { - if (line.contains("started")) { - setSynergyState(synergyTransfering); - } - else if (line.contains("failed") || - line.contains("complete") || - line.contains("interrupted")) { - setSynergyState(synergyConnected); - } - } - } -} - bool MainWindow::autoHide() { if ((appConfig().processMode() == Desktop) && diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index fd3547a43..3f57386b6 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -172,7 +172,6 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase QString getProfileRootForArg(); void checkConnected(const QString& line); void checkFingerprint(const QString& line); - void checkTransmission(const QString& line); bool autoHide(); QString getTimeStamp(); From 18a6f75371e25848f15bfc7d01b61a85543404bf Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 16 Jul 2015 12:23:56 -0700 Subject: [PATCH 030/572] Fixed dialog too big #4852 --- 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 6b77b8745..c7a5d3eca 100644 --- a/src/gui/res/SettingsDialogBase.ui +++ b/src/gui/res/SettingsDialogBase.ui @@ -7,7 +7,7 @@ 0 0 368 - 439 + 377 From c3d38db053be85cc9fcc1d89dec8bbaeca729ecb Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 16 Jul 2015 15:53:53 -0700 Subject: [PATCH 031/572] Made buffer thread wait for notify when no gui #4721 --- src/lib/ipc/IpcLogOutputter.cpp | 21 ++++++++++++++++----- src/lib/ipc/IpcLogOutputter.h | 6 +++++- src/lib/synergy/DaemonApp.cpp | 2 +- src/test/unittests/ipc/IpcLogOutputterTests.cpp | 10 ++++------ 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/lib/ipc/IpcLogOutputter.cpp b/src/lib/ipc/IpcLogOutputter.cpp index 6ce8d774f..9999c412c 100644 --- a/src/lib/ipc/IpcLogOutputter.cpp +++ b/src/lib/ipc/IpcLogOutputter.cpp @@ -37,7 +37,7 @@ enum EIpcLogOutputter { kBufferRateTimeLimit = 1 // seconds }; -IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer, bool useThread) : +IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer, EIpcClientType clientType, bool useThread) : m_ipcServer(ipcServer), m_bufferMutex(ARCH->newMutex()), m_sending(false), @@ -50,7 +50,9 @@ IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer, bool useThread) : m_bufferRateWriteLimit(kBufferRateWriteLimit), m_bufferRateTimeLimit(kBufferRateTimeLimit), m_bufferWriteCount(0), - m_bufferRateStart(ARCH->time()) + m_bufferRateStart(ARCH->time()), + m_clientType(clientType), + m_runningMutex(ARCH->newMutex()) { if (useThread) { m_bufferThread = new Thread(new TMethodJob( @@ -81,6 +83,7 @@ void IpcLogOutputter::close() { if (m_bufferThread != nullptr) { + ArchMutexLock lock(m_runningMutex); m_running = false; notifyBuffer(); m_bufferThread->wait(5); @@ -103,6 +106,7 @@ IpcLogOutputter::write(ELevel, const char* text) appendBuffer(text); notifyBuffer(); + return true; } @@ -133,6 +137,13 @@ IpcLogOutputter::appendBuffer(const String& text) m_bufferWriteCount++; } +bool +IpcLogOutputter::isRunning() +{ + ArchMutexLock lock(m_runningMutex); + return m_running; +} + void IpcLogOutputter::bufferThread(void*) { @@ -140,8 +151,8 @@ IpcLogOutputter::bufferThread(void*) m_running = true; try { - while (m_running) { - if (m_buffer.empty()) { + while (isRunning()) { + if (m_buffer.empty() || !m_ipcServer.hasClients(m_clientType)) { ArchMutexLock lock(m_notifyMutex); ARCH->waitCondVar(m_notifyCond, m_notifyMutex, -1); } @@ -184,7 +195,7 @@ IpcLogOutputter::getChunk(size_t count) void IpcLogOutputter::sendBuffer() { - if (m_buffer.empty() || !m_ipcServer.hasClients(kIpcClientGui)) { + if (m_buffer.empty() || !m_ipcServer.hasClients(m_clientType)) { return; } diff --git a/src/lib/ipc/IpcLogOutputter.h b/src/lib/ipc/IpcLogOutputter.h index a6a476a64..efe8238a0 100644 --- a/src/lib/ipc/IpcLogOutputter.h +++ b/src/lib/ipc/IpcLogOutputter.h @@ -21,6 +21,7 @@ #include "arch/Arch.h" #include "arch/IArchMultithread.h" #include "base/ILogOutputter.h" +#include "ipc/Ipc.h" #include @@ -39,7 +40,7 @@ class IpcLogOutputter : public ILogOutputter { If \p useThread is \c false, then the buffer needs to be sent manually using the \c sendBuffer() function. */ - IpcLogOutputter(IpcServer& ipcServer, bool useThread); + IpcLogOutputter(IpcServer& ipcServer, EIpcClientType clientType, bool useThread); virtual ~IpcLogOutputter(); // ILogOutputter overrides @@ -92,6 +93,7 @@ class IpcLogOutputter : public ILogOutputter { void bufferThread(void*); String getChunk(size_t count); void appendBuffer(const String& text); + bool isRunning(); private: typedef std::deque Buffer; @@ -113,4 +115,6 @@ class IpcLogOutputter : public ILogOutputter { UInt16 m_bufferWriteCount; double m_bufferRateStart; bool m_useThread; + EIpcClientType m_clientType; + ArchMutex m_runningMutex; }; diff --git a/src/lib/synergy/DaemonApp.cpp b/src/lib/synergy/DaemonApp.cpp index 15ff14dff..bb1ef91f8 100644 --- a/src/lib/synergy/DaemonApp.cpp +++ b/src/lib/synergy/DaemonApp.cpp @@ -211,7 +211,7 @@ DaemonApp::mainLoop(bool logToFile) m_ipcServer = new IpcServer(m_events, &multiplexer); // send logging to gui via ipc, log system adopts outputter. - m_ipcLogOutputter = new IpcLogOutputter(*m_ipcServer, true); + m_ipcLogOutputter = new IpcLogOutputter(*m_ipcServer, kIpcClientGui, true); CLOG->insert(m_ipcLogOutputter); #if SYSAPI_WIN32 diff --git a/src/test/unittests/ipc/IpcLogOutputterTests.cpp b/src/test/unittests/ipc/IpcLogOutputterTests.cpp index 444b506a8..2248d6d1a 100644 --- a/src/test/unittests/ipc/IpcLogOutputterTests.cpp +++ b/src/test/unittests/ipc/IpcLogOutputterTests.cpp @@ -52,11 +52,10 @@ TEST(IpcLogOutputterTests, write_threadingEnabled_bufferIsSent) ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); - EXPECT_CALL(mockServer, hasClients(_)).Times(2); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\n"), _)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\n"), _)).Times(1); - IpcLogOutputter outputter(mockServer, true); + IpcLogOutputter outputter(mockServer, kIpcClientUnknown, true); outputter.write(kNOTE, "mock 1"); mockServer.waitForSend(); outputter.write(kNOTE, "mock 2"); @@ -68,11 +67,10 @@ TEST(IpcLogOutputterTests, write_overBufferMaxSize_firstLineTruncated) MockIpcServer mockServer; ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); - EXPECT_CALL(mockServer, hasClients(_)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\nmock 3\n"), _)).Times(1); - IpcLogOutputter outputter(mockServer, false); + IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false); outputter.bufferMaxSize(2); // log more lines than the buffer can contain @@ -91,7 +89,7 @@ TEST(IpcLogOutputterTests, write_underBufferMaxSize_allLinesAreSent) EXPECT_CALL(mockServer, hasClients(_)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\nmock 2\n"), _)).Times(1); - IpcLogOutputter outputter(mockServer, false); + IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false); outputter.bufferMaxSize(2); // log more lines than the buffer can contain @@ -147,7 +145,7 @@ TEST(IpcLogOutputterTests, write_underBufferRateLimit_allLinesAreSent) EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\nmock 2\n"), _)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 3\nmock 4\n"), _)).Times(1); - IpcLogOutputter outputter(mockServer, false); + IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false); outputter.bufferRateLimit(4, 1); // 1s (should be plenty of time) // log 1 more line than the buffer can accept in time limit. From a5c865913fb90734e9d7b37acde4f7da3ed638cf Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 16 Jul 2015 17:05:22 -0700 Subject: [PATCH 032/572] Added expect call time for hasClients #4721 --- src/test/unittests/ipc/IpcLogOutputterTests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/unittests/ipc/IpcLogOutputterTests.cpp b/src/test/unittests/ipc/IpcLogOutputterTests.cpp index 2248d6d1a..9e6e53dea 100644 --- a/src/test/unittests/ipc/IpcLogOutputterTests.cpp +++ b/src/test/unittests/ipc/IpcLogOutputterTests.cpp @@ -52,6 +52,7 @@ TEST(IpcLogOutputterTests, write_threadingEnabled_bufferIsSent) ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); + EXPECT_CALL(mockServer, hasClients(_)).Times(3); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\n"), _)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\n"), _)).Times(1); From bfc3ac340ffd1eb4a1e4f15846f73d723d589b19 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 17 Jul 2015 11:11:00 -0700 Subject: [PATCH 033/572] Fixed possible loss of data warning #4677 --- src/lib/base/log_outputters.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/base/log_outputters.cpp b/src/lib/base/log_outputters.cpp index 010d79f29..962c9d683 100644 --- a/src/lib/base/log_outputters.cpp +++ b/src/lib/base/log_outputters.cpp @@ -264,7 +264,7 @@ FileLogOutputter::write(ELevel level, const char *message) m_handle << message << std::endl; // when file size exceeds limits, move to 'old log' filename. - int p = m_handle.tellp(); + size_t p = m_handle.tellp(); if (p > (kFileSizeLimit * 1024)) { moveFile = true; } From 7259e714399877947237cf8ce255defbc7d0ff3f Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 17 Jul 2015 13:35:37 -0700 Subject: [PATCH 034/572] Reset thread back to null on finish #4712 --- src/lib/client/Client.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index da8611d47..30c80ae1d 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -775,6 +775,8 @@ Client::sendClipboardThread(void*) sendClipboard(id); } } + + m_sendClipboardThread = NULL; } void From 1369f46cee69c91aa5149db5cbec80fcebc543b4 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 20 Jul 2015 16:19:46 -0700 Subject: [PATCH 035/572] Show connected message box only when main GUI is visible #4850 --- src/gui/src/MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index aebf134a1..d7488b59d 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -409,7 +409,7 @@ void MainWindow::checkConnected(const QString& line) { setSynergyState(synergyConnected); - if (!appConfig().startedBefore()) { + if (!appConfig().startedBefore() && isVisible()) { QMessageBox::information( this, "Synergy", tr("Synergy is now connected, You can close the " From 9d44affc890aa0b40cf14a879ff5512efdef1bf8 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 21 Jul 2015 14:10:50 -0700 Subject: [PATCH 036/572] Fixed Ipc unit test intermittently fail #4721 --- src/lib/ipc/IpcLogOutputter.cpp | 2 ++ src/test/mock/ipc/MockIpcServer.h | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/lib/ipc/IpcLogOutputter.cpp b/src/lib/ipc/IpcLogOutputter.cpp index 9999c412c..d102b7476 100644 --- a/src/lib/ipc/IpcLogOutputter.cpp +++ b/src/lib/ipc/IpcLogOutputter.cpp @@ -67,6 +67,8 @@ IpcLogOutputter::~IpcLogOutputter() ARCH->closeMutex(m_bufferMutex); if (m_bufferThread != nullptr) { + m_bufferThread->cancel(); + m_bufferThread->wait(); delete m_bufferThread; } diff --git a/src/test/mock/ipc/MockIpcServer.h b/src/test/mock/ipc/MockIpcServer.h index 6500887b2..448e8a826 100644 --- a/src/test/mock/ipc/MockIpcServer.h +++ b/src/test/mock/ipc/MockIpcServer.h @@ -34,6 +34,16 @@ class MockIpcServer : public IpcServer MockIpcServer() : m_sendCond(ARCH->newCondVar()), m_sendMutex(ARCH->newMutex()) { } + + ~MockIpcServer() { + if (m_sendCond != NULL) { + ARCH->closeCondVar(m_sendCond); + } + + if (m_sendMutex != NULL) { + ARCH->closeMutex(m_sendMutex); + } + } MOCK_METHOD0(listen, void()); MOCK_METHOD2(send, void(const IpcMessage&, EIpcClientType)); From de49b46eddeef262e93621c1b5201ac0569f93b9 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 21 Jul 2015 15:29:09 -0700 Subject: [PATCH 037/572] Expected hasClients at least 3 times #4721 --- src/test/unittests/ipc/IpcLogOutputterTests.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/unittests/ipc/IpcLogOutputterTests.cpp b/src/test/unittests/ipc/IpcLogOutputterTests.cpp index 9e6e53dea..1b9133214 100644 --- a/src/test/unittests/ipc/IpcLogOutputterTests.cpp +++ b/src/test/unittests/ipc/IpcLogOutputterTests.cpp @@ -36,6 +36,7 @@ using ::testing::Matcher; using ::testing::MatcherCast; using ::testing::Property; using ::testing::StrEq; +using ::testing::AtLeast; using namespace synergy; @@ -52,7 +53,7 @@ TEST(IpcLogOutputterTests, write_threadingEnabled_bufferIsSent) ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true)); - EXPECT_CALL(mockServer, hasClients(_)).Times(3); + EXPECT_CALL(mockServer, hasClients(_)).Times(AtLeast(3)); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\n"), _)).Times(1); EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\n"), _)).Times(1); From 1659f9f018fa01d4d1bd06fee0164888827808bd Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Wed, 22 Jul 2015 15:24:51 +0100 Subject: [PATCH 038/572] Added keep alive massge before each data transfer #4712 --- src/lib/synergy/StreamChunker.cpp | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index 080938bfa..380a37a38 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -31,7 +31,6 @@ #include -#define KEEP_ALIVE_THRESHOLD 1 #define SEND_THRESHOLD 0.005f using namespace std; @@ -73,9 +72,7 @@ StreamChunker::sendFile( // send chunk messages with a fixed chunk size size_t sentLength = 0; size_t chunkSize = s_chunkSize; - Stopwatch keepAliveStopwatch; Stopwatch sendStopwatch; - keepAliveStopwatch.start(); sendStopwatch.start(); file.seekg (0, std::ios::beg); @@ -86,12 +83,9 @@ StreamChunker::sendFile( break; } - if (keepAliveStopwatch.getTime() > KEEP_ALIVE_THRESHOLD) { + if (sendStopwatch.getTime() > SEND_THRESHOLD) { events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); - keepAliveStopwatch.reset(); - } - if (sendStopwatch.getTime() > SEND_THRESHOLD) { // make sure we don't read too much from the mock data. if (sentLength + chunkSize > size) { chunkSize = size - sentLength; @@ -146,9 +140,7 @@ StreamChunker::sendClipboard( // send clipboard chunk with a fixed size size_t sentLength = 0; size_t chunkSize = s_chunkSize; - Stopwatch keepAliveStopwatch; Stopwatch sendStopwatch; - keepAliveStopwatch.start(); sendStopwatch.start(); while (true) { @@ -157,13 +149,10 @@ StreamChunker::sendClipboard( LOG((CLOG_DEBUG "clipboard transmission interrupted")); break; } - - if (keepAliveStopwatch.getTime() > KEEP_ALIVE_THRESHOLD) { - events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); - keepAliveStopwatch.reset(); - } if (sendStopwatch.getTime() > SEND_THRESHOLD) { + events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); + // make sure we don't read too much from the mock data. if (sentLength + chunkSize > size) { chunkSize = size - sentLength; From 5d6199640555dc293791ac481e2b45ae624a1c28 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 23 Jul 2015 12:37:00 -0700 Subject: [PATCH 039/572] Fixed code style #4712 --- src/lib/platform/XWindowsClipboard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/platform/XWindowsClipboard.cpp b/src/lib/platform/XWindowsClipboard.cpp index 1a5ea4640..7db899f17 100644 --- a/src/lib/platform/XWindowsClipboard.cpp +++ b/src/lib/platform/XWindowsClipboard.cpp @@ -561,7 +561,7 @@ XWindowsClipboard::icccmFillCache() IClipboard::EFormat format = converter->getFormat(); m_data[format] = converter->toIClipboard(targetData); m_added[format] = true; - LOG((CLOG_DEBUG " added format %d for target %s (%u %s)", format, XWindowsUtil::atomToString(m_display, target).c_str(), targetData.size(), targetData.size() == 1 ? "byte" : "bytes")); + LOG((CLOG_DEBUG "added format %d for target %s (%u %s)", format, XWindowsUtil::atomToString(m_display, target).c_str(), targetData.size(), targetData.size() == 1 ? "byte" : "bytes")); } } @@ -799,7 +799,7 @@ XWindowsClipboard::motifFillCache() IClipboard::EFormat format = converter->getFormat(); m_data[format] = converter->toIClipboard(targetData); m_added[format] = true; - LOG((CLOG_DEBUG " added format %d for target %s", format, XWindowsUtil::atomToString(m_display, target).c_str())); + LOG((CLOG_DEBUG "added format %d for target %s", format, XWindowsUtil::atomToString(m_display, target).c_str())); } } From 86d5567e747f34b4a98c676f2bda701106632c57 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 23 Jul 2015 12:41:00 -0700 Subject: [PATCH 040/572] Removed redundant logging #4721 --- src/lib/platform/XWindowsScreen.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index 067c4f3e3..d10b7e1c4 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -1323,7 +1323,6 @@ XWindowsScreen::handleSystemEvent(const Event& event, void*) // selection owner. report that to the receiver. ClipboardID id = getClipboardID(xevent->xselectionclear.selection); if (id != kClipboardEnd) { - LOG((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time)); m_clipboard[id]->lost(xevent->xselectionclear.time); sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), id); return; From fc600efdfe8639f3625a6a902be96ce5ba9c75b7 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Wed, 29 Jul 2015 02:01:39 +0100 Subject: [PATCH 041/572] Restarted process from GUI in desktop mode #4901 --- 1.patch | 96 ++++++++++++++++++++++++++++++++++++++++++++++ src/gui/src/MainWindow.cpp | 25 +++++++++++- src/gui/src/MainWindow.h | 9 ++++- 3 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 1.patch diff --git a/1.patch b/1.patch new file mode 100644 index 000000000..8e46b0461 --- /dev/null +++ b/1.patch @@ -0,0 +1,96 @@ +diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp +index 40c3f21..e02ba9d 100644 +--- a/src/lib/plugin/ns/SecureSocket.cpp ++++ b/src/lib/plugin/ns/SecureSocket.cpp +@@ -35,12 +35,9 @@ + // + + #define MAX_ERROR_SIZE 65535 +- +-enum { +- // this limit seems extremely high, but mac client seem to generate around +- // 50,000 errors before they establish a connection (wtf?) +- kMaxRetryCount = 100000 +-}; ++// RETRY_DELAY * MAX_RETRY = 10s ++#define MAX_RETRY 1000 ++#define RETRY_DELAY 0.01f + + enum { + kMsgSize = 128 +@@ -61,8 +58,7 @@ SecureSocket::SecureSocket( + SocketMultiplexer* socketMultiplexer) : + TCPSocket(events, socketMultiplexer), + m_secureReady(false), +- m_fatal(false), +- m_maxRetry(kMaxRetryCount) ++ m_fatal(false) + { + } + +@@ -72,8 +68,7 @@ SecureSocket::SecureSocket( + ArchSocket socket) : + TCPSocket(events, socketMultiplexer, socket), + m_secureReady(false), +- m_fatal(false), +- m_maxRetry(kMaxRetryCount) ++ m_fatal(false) + { + } + +@@ -295,8 +290,7 @@ SecureSocket::secureAccept(int socket) + + if (isFatal()) { + // tell user and sleep so the socket isn't hammered. +- LOG((CLOG_ERR "failed to accept secure socket")); +- LOG((CLOG_INFO "client connection may not be secure")); ++ LOG((CLOG_WARN "failed to accept secure socket")); + m_secureReady = false; + ARCH->sleep(1); + retry = 0; +@@ -318,6 +312,7 @@ SecureSocket::secureAccept(int socket) + if (retry > 0) { + LOG((CLOG_DEBUG2 "retry accepting secure socket")); + m_secureReady = false; ++ ARCH->sleep(RETRY_DELAY); + return 0; + } + +@@ -351,6 +346,7 @@ SecureSocket::secureConnect(int socket) + if (retry > 0) { + LOG((CLOG_DEBUG2 "retry connect secure socket")); + m_secureReady = false; ++ ARCH->sleep(RETRY_DELAY); + return 0; + } + +@@ -475,8 +471,8 @@ SecureSocket::checkResult(int status, int& retry) + } + + // If the retry max would exceed the allowed, treat it as a fatal error +- if (retry > maxRetry()) { +- LOG((CLOG_ERR "passive ssl error limit exceeded: %d", retry)); ++ if (retry > MAX_RETRY) { ++ LOG((CLOG_DEBUG "retry exceeded %d sec", RETRY_DELAY * MAX_RETRY)); + isFatal(true); + } + +diff --git a/src/lib/plugin/ns/SecureSocket.h b/src/lib/plugin/ns/SecureSocket.h +index 0c0f3b1..871e1e4 100644 +--- a/src/lib/plugin/ns/SecureSocket.h ++++ b/src/lib/plugin/ns/SecureSocket.h +@@ -52,8 +52,6 @@ public: + int secureWrite(const void* buffer, int size, int& wrote); + void initSsl(bool server); + bool loadCertificates(String& CertFile); +- void maxRetry(int limit) { m_maxRetry = limit; }; +- int maxRetry() const { return m_maxRetry; }; + + private: + // SSL +@@ -87,5 +85,4 @@ private: + Ssl* m_ssl; + bool m_secureReady; + bool m_fatal; +- int m_maxRetry; + }; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index d7488b59d..162320b6e 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -96,7 +96,8 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : m_pCancelButton(NULL), m_SuppressAutoConfigWarning(false), m_BonjourInstall(NULL), - m_SuppressEmptyServerWarning(false) + m_SuppressEmptyServerWarning(false), + m_ExpectedRunningState(kStopped) { setupUi(this); @@ -142,6 +143,7 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : MainWindow::~MainWindow() { if (appConfig().processMode() == Desktop) { + m_ExpectedRunningState = kStopped; stopDesktop(); } @@ -777,6 +779,15 @@ void MainWindow::synergyFinished(int exitCode, QProcess::ExitStatus) QMessageBox::critical(this, tr("Synergy terminated with an error"), QString(tr("Synergy terminated unexpectedly with an exit code of %1.

Please see the log output for details.")).arg(exitCode)); stopSynergy(); } + + if (appConfig().processMode() == Desktop) { + if (m_ExpectedRunningState == kStarted) { + stopSynergy(); + delay(1); + startSynergy(); + } + } + #else Q_UNUSED(exitCode); #endif @@ -823,6 +834,7 @@ void MainWindow::setSynergyState(qSynergyState state) } setStatus(tr("Synergy is running.")); + m_ExpectedRunningState = kStarted; break; } @@ -1338,3 +1350,14 @@ void MainWindow::delay(unsigned int s) QCoreApplication::processEvents(QEventLoop::AllEvents, 100); } } + +void MainWindow::on_m_pButtonToggleStart_clicked() +{ + if (m_SynergyState != synergyConnected) { + m_ExpectedRunningState = kStarted; + } + else { + m_ExpectedRunningState = kStopped; + } + +} diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 3f57386b6..94c20c6d2 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -85,6 +85,11 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase Info }; + enum qRuningState { + kStarted, + kStopped + }; + public: MainWindow(QSettings& settings, AppConfig& appConfig); ~MainWindow(); @@ -138,7 +143,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase protected: QSettings& settings() { return m_Settings; } AppConfig& appConfig() { return m_AppConfig; } - QProcess*& synergyProcess() { return m_pSynergy; } + QProcess* synergyProcess() { return m_pSynergy; } void setSynergyProcess(QProcess* p) { m_pSynergy = p; } void initConnections(); void createMenuBar(); @@ -200,12 +205,14 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase bool m_SuppressAutoConfigWarning; CommandProcess* m_BonjourInstall; bool m_SuppressEmptyServerWarning; + qRuningState m_ExpectedRunningState; private slots: void on_m_pCheckBoxAutoConfig_toggled(bool checked); void on_m_pComboServerList_currentIndexChanged(QString ); void on_m_pButtonApply_clicked(); void installBonjour(); + void on_m_pButtonToggleStart_clicked(); }; #endif From 8a8f3601c4f680b87b61c95e5f3d45a3c48d9b65 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 29 Jul 2015 11:48:06 -0700 Subject: [PATCH 042/572] Deleted accidentally committed file --- 1.patch | 96 ----------------------------------------------------------------- 1 file changed, 96 deletions(-) delete mode 100644 1.patch diff --git a/1.patch b/1.patch deleted file mode 100644 index 8e46b0461..000000000 --- a/1.patch +++ /dev/null @@ -1,96 +0,0 @@ -diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp -index 40c3f21..e02ba9d 100644 ---- a/src/lib/plugin/ns/SecureSocket.cpp -+++ b/src/lib/plugin/ns/SecureSocket.cpp -@@ -35,12 +35,9 @@ - // - - #define MAX_ERROR_SIZE 65535 -- --enum { -- // this limit seems extremely high, but mac client seem to generate around -- // 50,000 errors before they establish a connection (wtf?) -- kMaxRetryCount = 100000 --}; -+// RETRY_DELAY * MAX_RETRY = 10s -+#define MAX_RETRY 1000 -+#define RETRY_DELAY 0.01f - - enum { - kMsgSize = 128 -@@ -61,8 +58,7 @@ SecureSocket::SecureSocket( - SocketMultiplexer* socketMultiplexer) : - TCPSocket(events, socketMultiplexer), - m_secureReady(false), -- m_fatal(false), -- m_maxRetry(kMaxRetryCount) -+ m_fatal(false) - { - } - -@@ -72,8 +68,7 @@ SecureSocket::SecureSocket( - ArchSocket socket) : - TCPSocket(events, socketMultiplexer, socket), - m_secureReady(false), -- m_fatal(false), -- m_maxRetry(kMaxRetryCount) -+ m_fatal(false) - { - } - -@@ -295,8 +290,7 @@ SecureSocket::secureAccept(int socket) - - if (isFatal()) { - // tell user and sleep so the socket isn't hammered. -- LOG((CLOG_ERR "failed to accept secure socket")); -- LOG((CLOG_INFO "client connection may not be secure")); -+ LOG((CLOG_WARN "failed to accept secure socket")); - m_secureReady = false; - ARCH->sleep(1); - retry = 0; -@@ -318,6 +312,7 @@ SecureSocket::secureAccept(int socket) - if (retry > 0) { - LOG((CLOG_DEBUG2 "retry accepting secure socket")); - m_secureReady = false; -+ ARCH->sleep(RETRY_DELAY); - return 0; - } - -@@ -351,6 +346,7 @@ SecureSocket::secureConnect(int socket) - if (retry > 0) { - LOG((CLOG_DEBUG2 "retry connect secure socket")); - m_secureReady = false; -+ ARCH->sleep(RETRY_DELAY); - return 0; - } - -@@ -475,8 +471,8 @@ SecureSocket::checkResult(int status, int& retry) - } - - // If the retry max would exceed the allowed, treat it as a fatal error -- if (retry > maxRetry()) { -- LOG((CLOG_ERR "passive ssl error limit exceeded: %d", retry)); -+ if (retry > MAX_RETRY) { -+ LOG((CLOG_DEBUG "retry exceeded %d sec", RETRY_DELAY * MAX_RETRY)); - isFatal(true); - } - -diff --git a/src/lib/plugin/ns/SecureSocket.h b/src/lib/plugin/ns/SecureSocket.h -index 0c0f3b1..871e1e4 100644 ---- a/src/lib/plugin/ns/SecureSocket.h -+++ b/src/lib/plugin/ns/SecureSocket.h -@@ -52,8 +52,6 @@ public: - int secureWrite(const void* buffer, int size, int& wrote); - void initSsl(bool server); - bool loadCertificates(String& CertFile); -- void maxRetry(int limit) { m_maxRetry = limit; }; -- int maxRetry() const { return m_maxRetry; }; - - private: - // SSL -@@ -87,5 +85,4 @@ private: - Ssl* m_ssl; - bool m_secureReady; - bool m_fatal; -- int m_maxRetry; - }; From 94664e413b089dbc23b206a9a98a73a334a684d3 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Wed, 29 Jul 2015 14:31:47 -0700 Subject: [PATCH 043/572] Rename update zeroconf mutex --- src/gui/src/MainWindow.cpp | 2 +- src/gui/src/MainWindow.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 162320b6e..7c55d7a65 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -939,7 +939,7 @@ void MainWindow::changeEvent(QEvent* event) void MainWindow::updateZeroconfService() { - QMutexLocker locker(&m_Mutex); + QMutexLocker locker(&m_UpdateZeroconfMutex); if (isBonjourRunning()) { if (!m_AppConfig.wizardShouldRun()) { diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 94c20c6d2..6d03326cf 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -201,7 +201,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase DataDownloader* m_pDataDownloader; QMessageBox* m_DownloadMessageBox; QAbstractButton* m_pCancelButton; - QMutex m_Mutex; + QMutex m_UpdateZeroconfMutex; bool m_SuppressAutoConfigWarning; CommandProcess* m_BonjourInstall; bool m_SuppressEmptyServerWarning; From 67fbecb82582185939e4b8e1ab2af1a16243f713 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Wed, 29 Jul 2015 14:34:21 -0700 Subject: [PATCH 044/572] Fixed auto restart sometimes cause GUI crash #4901 --- src/gui/src/MainWindow.cpp | 61 +++++++++++++++++----------------------------- src/gui/src/MainWindow.h | 6 ++--- 2 files changed, 25 insertions(+), 42 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 7c55d7a65..165ac80fb 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -2,11 +2,11 @@ * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Synergy Si 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 @@ -346,7 +346,7 @@ void MainWindow::logOutput() { if (!line.isEmpty()) { - appendLogRaw(line); + appendLogRaw(line); } } } @@ -493,11 +493,8 @@ void MainWindow::startSynergy() bool desktopMode = appConfig().processMode() == Desktop; bool serviceMode = appConfig().processMode() == Service; - if (desktopMode) - { - stopSynergy(); - } - + appendLogDebug("starting process"); + m_ExpectedRunningState = kStarted; setSynergyState(synergyConnecting); QString app; @@ -587,7 +584,6 @@ void MainWindow::startSynergy() synergyProcess()->start(app, args); if (!synergyProcess()->waitForStarted()) { - stopSynergy(); 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; @@ -729,6 +725,10 @@ bool MainWindow::serverArgs(QStringList& args, QString& app) void MainWindow::stopSynergy() { + appendLogDebug("stopping process"); + + m_ExpectedRunningState = kStopped; + if (appConfig().processMode() == Service) { stopService(); @@ -757,6 +757,7 @@ void MainWindow::stopService() void MainWindow::stopDesktop() { + QMutexLocker locker(&m_StopDesktopMutex); if (!synergyProcess()) { return; } @@ -772,27 +773,21 @@ void MainWindow::stopDesktop() void MainWindow::synergyFinished(int exitCode, QProcess::ExitStatus) { - // on Windows, we always seem to have an exit code != 0. -#if !defined(Q_OS_WIN) - if (exitCode != 0) - { - QMessageBox::critical(this, tr("Synergy terminated with an error"), QString(tr("Synergy terminated unexpectedly with an exit code of %1.

Please see the log output for details.")).arg(exitCode)); - stopSynergy(); + if (exitCode == 0) { + appendLogInfo(QString("process exited normally")); } - - if (appConfig().processMode() == Desktop) { - if (m_ExpectedRunningState == kStarted) { - stopSynergy(); - delay(1); - startSynergy(); - } + else { + appendLogError(QString("process exited with error code: %1").arg(exitCode)); } -#else - Q_UNUSED(exitCode); -#endif - - setSynergyState(synergyDisconnected); + if (m_ExpectedRunningState == kStarted) { + delay(1); + appendLogInfo(QString("detected process not running, auto restarting")); + startSynergy(); + } + else { + setSynergyState(synergyDisconnected); + } } void MainWindow::setSynergyState(qSynergyState state) @@ -834,7 +829,6 @@ void MainWindow::setSynergyState(qSynergyState state) } setStatus(tr("Synergy is running.")); - m_ExpectedRunningState = kStarted; break; } @@ -1350,14 +1344,3 @@ void MainWindow::delay(unsigned int s) QCoreApplication::processEvents(QEventLoop::AllEvents, 100); } } - -void MainWindow::on_m_pButtonToggleStart_clicked() -{ - if (m_SynergyState != synergyConnected) { - m_ExpectedRunningState = kStarted; - } - else { - m_ExpectedRunningState = kStopped; - } - -} diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 6d03326cf..89d8c380d 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -2,11 +2,11 @@ * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Synergy Si 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 @@ -206,13 +206,13 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase CommandProcess* m_BonjourInstall; bool m_SuppressEmptyServerWarning; qRuningState m_ExpectedRunningState; + QMutex m_StopDesktopMutex; private slots: void on_m_pCheckBoxAutoConfig_toggled(bool checked); void on_m_pComboServerList_currentIndexChanged(QString ); void on_m_pButtonApply_clicked(); void installBonjour(); - void on_m_pButtonToggleStart_clicked(); }; #endif From 08effbcf99614b75a60e1168502faf5882b46e3c Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Wed, 29 Jul 2015 15:03:35 -0700 Subject: [PATCH 045/572] Fixed code style #4901 --- 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 165ac80fb..0f576a56e 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -764,8 +764,9 @@ void MainWindow::stopDesktop() appendLogInfo("stopping synergy desktop process"); - if (synergyProcess()->isOpen()) + if (synergyProcess()->isOpen()) { synergyProcess()->close(); + } delete synergyProcess(); setSynergyProcess(NULL); From 20cb624c3be74697f5b88dedf20dda1d425384a0 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Wed, 29 Jul 2015 15:15:19 -0700 Subject: [PATCH 046/572] Used timer instead of delay #4901 --- src/gui/src/MainWindow.cpp | 12 +----------- src/gui/src/MainWindow.h | 1 - 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 0f576a56e..53cf5de91 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -782,9 +782,8 @@ void MainWindow::synergyFinished(int exitCode, QProcess::ExitStatus) } if (m_ExpectedRunningState == kStarted) { - delay(1); + QTimer::singleShot(1000, this, SLOT(startSynergy())); appendLogInfo(QString("detected process not running, auto restarting")); - startSynergy(); } else { setSynergyState(synergyDisconnected); @@ -1336,12 +1335,3 @@ QString MainWindow::getProfileRootForArg() return QString("\"%1\"").arg(dir); } - -void MainWindow::delay(unsigned int s) -{ - QTime dieTime= QTime::currentTime().addSecs(s); - - while (QTime::currentTime() < dieTime) { - QCoreApplication::processEvents(QEventLoop::AllEvents, 100); - } -} diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 89d8c380d..5f0035ac9 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -114,7 +114,6 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase void serverDetected(const QString name); void setEdition(int type); void updateLocalFingerprint(); - void delay(unsigned int); public slots: void appendLogRaw(const QString& text); From e472e478152fb7915cf59fa8d8801e947ba5080e Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Thu, 30 Jul 2015 11:56:06 -0700 Subject: [PATCH 047/572] Stopped old process on apply button clicked #4908 --- src/gui/src/MainWindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 53cf5de91..775ae5bb4 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -1097,6 +1097,7 @@ void MainWindow::on_m_pActionWizard_triggered() void MainWindow::on_m_pButtonApply_clicked() { + stopSynergy(); startSynergy(); } From d70c19b099e7141237fe903ed21b1e6a6b30f096 Mon Sep 17 00:00:00 2001 From: Jerry Date: Thu, 30 Jul 2015 15:52:14 -0700 Subject: [PATCH 048/572] Stopped old process before start a new one #4908 --- src/gui/src/MainWindow.cpp | 23 +++++++++++------------ src/gui/src/MainWindow.h | 1 + 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 775ae5bb4..60adc5f36 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -451,19 +451,13 @@ void MainWindow::checkFingerprint(const QString& line) .arg(fingerprint), QMessageBox::Yes | QMessageBox::No); + stopSynergy(); + if (fingerprintReply == QMessageBox::Yes) { // restart core process after trusting fingerprint. Fingerprint::trustedServers().trust(fingerprint); startSynergy(); } - else { - // on all platforms, the core process will stop if the - // fingerprint is not trusted, so technically the stop - // isn't really needed. however on windows, the core - // process will keep trying (and failing) unless we - // tell it to stop. - stopSynergy(); - } } bool MainWindow::autoHide() @@ -483,6 +477,12 @@ QString MainWindow::getTimeStamp() return '[' + current.toString(Qt::ISODate) + ']'; } +void MainWindow::restartSynergy() +{ + stopSynergy(); + startSynergy(); +} + void MainWindow::clearLog() { m_pLogOutput->clear(); @@ -1072,7 +1072,7 @@ void MainWindow::autoAddScreen(const QString name) } } else { - startSynergy(); + restartSynergy(); } } } @@ -1097,8 +1097,7 @@ void MainWindow::on_m_pActionWizard_triggered() void MainWindow::on_m_pButtonApply_clicked() { - stopSynergy(); - startSynergy(); + restartSynergy(); } #if defined(Q_OS_WIN) @@ -1282,7 +1281,7 @@ void MainWindow::updateEdition() void MainWindow::on_m_pComboServerList_currentIndexChanged(QString ) { if (m_pComboServerList->count() != 0) { - startSynergy(); + restartSynergy(); } } diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 5f0035ac9..c159747a7 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -178,6 +178,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase void checkFingerprint(const QString& line); bool autoHide(); QString getTimeStamp(); + void restartSynergy(); private: QSettings& m_Settings; From 0612ba585d040cdc9bf7d7531711f08960490028 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Thu, 30 Jul 2015 16:51:00 -0700 Subject: [PATCH 049/572] Stopped multiple fingerprint message boxes popping up #4910 --- src/gui/src/MainWindow.cpp | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 60adc5f36..126a4cdbe 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -436,27 +436,34 @@ void MainWindow::checkFingerprint(const QString& line) return; } - 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); + static bool messageBoxAlreadyShown = false; + + if (!messageBoxAlreadyShown) { + 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); - stopSynergy(); + messageBoxAlreadyShown = false; - if (fingerprintReply == QMessageBox::Yes) { - // restart core process after trusting fingerprint. - Fingerprint::trustedServers().trust(fingerprint); - startSynergy(); + stopSynergy(); + + if (fingerprintReply == QMessageBox::Yes) { + // restart core process after trusting fingerprint. + Fingerprint::trustedServers().trust(fingerprint); + startSynergy(); + } } } From dbdc2a1e29fc8ac2bf5d6fbbc5d02c47494f9b4d Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Thu, 30 Jul 2015 17:15:02 -0700 Subject: [PATCH 050/572] Solved fingerprint message box race condition #4901 --- src/gui/src/MainWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 126a4cdbe..7f3753edd 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -455,8 +455,6 @@ void MainWindow::checkFingerprint(const QString& line) .arg(fingerprint), QMessageBox::Yes | QMessageBox::No); - messageBoxAlreadyShown = false; - stopSynergy(); if (fingerprintReply == QMessageBox::Yes) { @@ -464,6 +462,8 @@ void MainWindow::checkFingerprint(const QString& line) Fingerprint::trustedServers().trust(fingerprint); startSynergy(); } + + messageBoxAlreadyShown = false; } } From 02902066a4778062e9148c986bc1d848b45d8c6c Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 31 Jul 2015 14:15:28 -0700 Subject: [PATCH 051/572] Removed the usage of old delay in GUI #4696 --- src/gui/src/PluginWizardPage.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/gui/src/PluginWizardPage.cpp b/src/gui/src/PluginWizardPage.cpp index 331d6a0c7..27328fbc5 100644 --- a/src/gui/src/PluginWizardPage.cpp +++ b/src/gui/src/PluginWizardPage.cpp @@ -80,10 +80,8 @@ void PluginWizardPage::queryPluginDone() } else { m_mainWindow.stopSynergy(); - m_mainWindow.delay(5); copyPlugins(); m_mainWindow.startSynergy(); - m_mainWindow.delay(5); } } From a249c38b9660aa17f89759ca6293c7d22a851c08 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 15 Jul 2015 17:34:31 -0700 Subject: [PATCH 052/572] Only loaded matching plugin on Windows #4866 Conflicts: src/gui/gui.pro src/gui/src/PluginManager.cpp src/lib/arch/win32/ArchPluginWindows.cpp --- src/gui/gui.pro | 6 +++++- src/gui/src/PluginManager.cpp | 3 +++ src/lib/arch/win32/ArchPluginWindows.cpp | 23 ++++++++++++++------ src/lib/common/PluginVersion.cpp | 37 ++++++++++++++++++++++++++++++++ src/lib/common/PluginVersion.h | 21 ++++++++++++++++++ src/lib/plugin/ns/ns.cpp | 5 +++-- 6 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 src/lib/common/PluginVersion.cpp create mode 100644 src/lib/common/PluginVersion.h diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 0abb57153..fdee073cf 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -59,7 +59,9 @@ SOURCES += src/main.cpp \ src/SslCertificate.cpp \ src/FileSysClient.cpp \ src/Plugin.cpp \ - src/WebClient.cpp + src/WebClient.cpp \ + ../lib/common/PluginVersion.cpp + HEADERS += src/MainWindow.h \ src/AboutDialog.h \ src/ServerConfig.h \ @@ -106,6 +108,8 @@ HEADERS += src/MainWindow.h \ src/FileSysClient.h \ src/Plugin.h \ src/WebClient.h + ../lib/common/PluginVersion.h + RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc macx { diff --git a/src/gui/src/PluginManager.cpp b/src/gui/src/PluginManager.cpp index 31ec2858d..92b9f9102 100644 --- a/src/gui/src/PluginManager.cpp +++ b/src/gui/src/PluginManager.cpp @@ -24,6 +24,7 @@ #include "ProcessorArch.h" #include "Fingerprint.h" #include "Plugin.h" +#include "../lib/common/PluginVersion.h" #include @@ -32,6 +33,7 @@ #include #include + PluginManager::PluginManager() : m_FileSysPluginList() { @@ -154,6 +156,7 @@ void PluginManager::copyPlugins() "plugin list. Please contact the help desk, and " "provide the following details.\n\n%1").arg(e.what())); } + emit copyFinished(); return; } diff --git a/src/lib/arch/win32/ArchPluginWindows.cpp b/src/lib/arch/win32/ArchPluginWindows.cpp index bd0f6832a..81fd7a32a 100644 --- a/src/lib/arch/win32/ArchPluginWindows.cpp +++ b/src/lib/arch/win32/ArchPluginWindows.cpp @@ -18,6 +18,7 @@ #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" @@ -67,15 +68,23 @@ ArchPluginWindows::load() } void* lib = reinterpret_cast(library); - String filename = synergy::string::removeFileExt(*it); - m_pluginTable.insert(std::make_pair(filename, lib)); - const char* version = (char*)invoke(filename.c_str(), "version",NULL); - if (version == NULL) { - version = kPre174Plugin; - } + String pluginName = synergy::string::removeFileExt(*it); + m_pluginTable.insert(std::make_pair(pluginName, lib)); - LOG((CLOG_DEBUG "loaded plugin: %s (%s)", (*it).c_str(),version)); + char* version = (char*)invoke(pluginName.c_str(), "version", NULL); + String expectedVersion(pluginVersion(pluginName.c_str())); + if (version != NULL && expectedVersion.compare(version) == 0) { + LOG((CLOG_DEBUG "loaded plugin: %s (%s)", (*it).c_str(), version)); + } + else { + LOG((CLOG_WARN "plugin version doesn't match")); + LOG((CLOG_DEBUG "expected plugin version: %s actual plugin version: %s", + expectedVersion.c_str(), version)); + LOG((CLOG_WARN "skip plugin: %s", (*it).c_str())); + m_pluginTable.erase(pluginName); + FreeLibrary(library); + } } } diff --git a/src/lib/common/PluginVersion.cpp b/src/lib/common/PluginVersion.cpp new file mode 100644 index 000000000..35a8ceb5e --- /dev/null +++ b/src/lib/common/PluginVersion.cpp @@ -0,0 +1,37 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Si 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 int kpluginCount = 1; +static const char kUnknownVersion[] = "unknown"; +static const char* s_pluginNames[] = {"ns"}; +static const char* s_pluginVersions[] = {"1.2"}; + +const char* pluginVersion(const char* pluginName) +{ + for (int i = 0; i < kpluginCount; i++) { + if (strcmp(pluginName, 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 new file mode 100644 index 000000000..40c6ac94b --- /dev/null +++ b/src/lib/common/PluginVersion.h @@ -0,0 +1,21 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Si 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 + +// return plugin version map +const char* pluginVersion(const char* pluginName); diff --git a/src/lib/plugin/ns/ns.cpp b/src/lib/plugin/ns/ns.cpp index 7498ef0d4..0fc2161aa 100644 --- a/src/lib/plugin/ns/ns.cpp +++ b/src/lib/plugin/ns/ns.cpp @@ -20,6 +20,7 @@ #include "SecureSocket.h" #include "SecureListenSocket.h" #include "arch/Arch.h" +#include "common/PluginVersion.h" #include "base/Log.h" #include @@ -27,11 +28,11 @@ #include #include -const char * kSynergyVers = VERSION; SecureSocket* g_secureSocket = NULL; SecureListenSocket* g_secureListenSocket = NULL; Arch* g_arch = NULL; Log* g_log = NULL; +static const char kPluginName[] = "ns"; std::string helperGetLibsUsed(void) @@ -106,7 +107,7 @@ invoke(const char* command, void** args) } } else if (strcmp(command, "version") == 0) { - return (void*) kSynergyVers; + return (void*)pluginVersion(kPluginName); } return NULL; From a99699df7a585598c565bf5ea6b3ec0c592bbee1 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Thu, 16 Jul 2015 02:24:33 +0100 Subject: [PATCH 053/572] Only loaded matching plugin on Mac #4866 Conflicts: src/lib/arch/unix/ArchPluginUnix.cpp --- src/lib/arch/unix/ArchPluginUnix.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/lib/arch/unix/ArchPluginUnix.cpp b/src/lib/arch/unix/ArchPluginUnix.cpp index bba2b0177..e13e9472d 100644 --- a/src/lib/arch/unix/ArchPluginUnix.cpp +++ b/src/lib/arch/unix/ArchPluginUnix.cpp @@ -19,6 +19,7 @@ #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" @@ -84,12 +85,21 @@ ArchPluginUnix::load() String filename = synergy::string::removeFileExt(*it); m_pluginTable.insert(std::make_pair(filename, library)); - const char * version = (char*)invoke( filename.c_str(),"version",NULL); - if (version == NULL) { - version = kPre174Plugin; + size_t pos = filename.find("lib"); + String pluginName = filename.substr(pos + 3); + char* version = (char*)invoke(filename.c_str(), "version", NULL); + String expectedVersion(pluginVersion(pluginName.c_str())); + if (version != NULL && expectedVersion.compare(version) == 0) { + LOG((CLOG_DEBUG "loaded plugin: %s (%s)", (*it).c_str(), version)); + } + else { + LOG((CLOG_WARN "plugin version doesn't match")); + LOG((CLOG_DEBUG "expected plugin version: %s actual plugin version: %s", + expectedVersion.c_str(), version)); + LOG((CLOG_WARN "skip plugin: %s", (*it).c_str())); + m_pluginTable.erase(filename); + dlclose(library); } - - LOG((CLOG_DEBUG "loaded plugin: %s (%s)", (*it).c_str(),version)); } } From 6602ebe435ddc3ff811ee9added06a74474bd1de Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 16 Jul 2015 12:09:55 -0700 Subject: [PATCH 054/572] Refactored adding plugin only after loaded #4866 Conflicts: src/lib/arch/unix/ArchPluginUnix.cpp --- src/lib/arch/IArchPlugin.h | 3 ++- src/lib/arch/unix/ArchPluginUnix.cpp | 44 ++++++++++++++++++++------------ src/lib/arch/unix/ArchPluginUnix.h | 3 ++- src/lib/arch/win32/ArchPluginWindows.cpp | 44 +++++++++++++++++++------------- src/lib/arch/win32/ArchPluginWindows.h | 3 ++- 5 files changed, 59 insertions(+), 38 deletions(-) diff --git a/src/lib/arch/IArchPlugin.h b/src/lib/arch/IArchPlugin.h index e864bc4d5..f6ae46371 100644 --- a/src/lib/arch/IArchPlugin.h +++ b/src/lib/arch/IArchPlugin.h @@ -70,7 +70,8 @@ class IArchPlugin : public IInterface { */ virtual void* invoke(const char* plugin, const char* command, - void** args) = 0; + void** args, + void* library = NULL) = 0; //@} diff --git a/src/lib/arch/unix/ArchPluginUnix.cpp b/src/lib/arch/unix/ArchPluginUnix.cpp index e13e9472d..ce81946f5 100644 --- a/src/lib/arch/unix/ArchPluginUnix.cpp +++ b/src/lib/arch/unix/ArchPluginUnix.cpp @@ -83,21 +83,20 @@ ArchPluginUnix::load() } String filename = synergy::string::removeFileExt(*it); - m_pluginTable.insert(std::make_pair(filename, library)); - size_t pos = filename.find("lib"); String pluginName = filename.substr(pos + 3); - char* version = (char*)invoke(filename.c_str(), "version", NULL); + char* version = (char*)invoke(filename.c_str(), "version", NULL, library); String expectedVersion(pluginVersion(pluginName.c_str())); + if (version != NULL && expectedVersion.compare(version) == 0) { LOG((CLOG_DEBUG "loaded plugin: %s (%s)", (*it).c_str(), version)); + m_pluginTable.insert(std::make_pair(filename, library)); } else { LOG((CLOG_WARN "plugin version doesn't match")); LOG((CLOG_DEBUG "expected plugin version: %s actual plugin version: %s", expectedVersion.c_str(), version)); LOG((CLOG_WARN "skip plugin: %s", (*it).c_str())); - m_pluginTable.erase(filename); dlclose(library); } } @@ -166,26 +165,37 @@ void* ArchPluginUnix::invoke( const char* plugin, const char* command, - void** args) + void** args, + void* library) { - PluginTable::iterator it; - it = m_pluginTable.find(plugin); - if (it != m_pluginTable.end()) { - invokeFunc invokePlugin = (invokeFunc)dlsym(it->second, "invoke"); - void* result = NULL; - if (invokePlugin != NULL) { - result = invokePlugin(command, args); + 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 "no invoke function in %s", it->first.c_str())); + LOG((CLOG_DEBUG "invoke command failed, plugin: %s command: %s", + plugin, command)); + return NULL; } - return result; } else { - LOG((CLOG_DEBUG "invoke command failed, plugin: %s command: %s", - plugin, command)); - return NULL; + 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 diff --git a/src/lib/arch/unix/ArchPluginUnix.h b/src/lib/arch/unix/ArchPluginUnix.h index 5438dffb3..894c675b5 100644 --- a/src/lib/arch/unix/ArchPluginUnix.h +++ b/src/lib/arch/unix/ArchPluginUnix.h @@ -38,7 +38,8 @@ class ArchPluginUnix : public IArchPlugin { bool exists(const char* name); virtual void* invoke(const char* pluginName, const char* functionName, - void** args); + void** args, + void* library = NULL); private: String getPluginsDir(); diff --git a/src/lib/arch/win32/ArchPluginWindows.cpp b/src/lib/arch/win32/ArchPluginWindows.cpp index 81fd7a32a..63a1cf410 100644 --- a/src/lib/arch/win32/ArchPluginWindows.cpp +++ b/src/lib/arch/win32/ArchPluginWindows.cpp @@ -70,19 +70,18 @@ ArchPluginWindows::load() void* lib = reinterpret_cast(library); String pluginName = synergy::string::removeFileExt(*it); - m_pluginTable.insert(std::make_pair(pluginName, lib)); - - char* version = (char*)invoke(pluginName.c_str(), "version", NULL); + char* version = (char*)invoke(pluginName.c_str(), "version", NULL, lib); String expectedVersion(pluginVersion(pluginName.c_str())); + if (version != NULL && expectedVersion.compare(version) == 0) { LOG((CLOG_DEBUG "loaded plugin: %s (%s)", (*it).c_str(), version)); + m_pluginTable.insert(std::make_pair(pluginName, lib)); } else { LOG((CLOG_WARN "plugin version doesn't match")); LOG((CLOG_DEBUG "expected plugin version: %s actual plugin version: %s", expectedVersion.c_str(), version)); LOG((CLOG_WARN "skip plugin: %s", (*it).c_str())); - m_pluginTable.erase(pluginName); FreeLibrary(library); } } @@ -158,28 +157,37 @@ void* ArchPluginWindows::invoke( const char* plugin, const char* command, - void** args) + void** args, + void* library) { - PluginTable::iterator it; - it = m_pluginTable.find(plugin); - if (it != m_pluginTable.end()) { - HINSTANCE lib = reinterpret_cast(it->second); - invokeFunc invokePlugin = (invokeFunc)GetProcAddress(lib, "invoke"); - void* result = NULL; - if (invokePlugin != NULL) { - result = invokePlugin(command, args); + 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 "no invoke function in %s", it->first.c_str())); + LOG((CLOG_DEBUG "invoke command failed, plugin: %s command: %s", + plugin, command)); + return NULL; } + } + else { + lib = reinterpret_cast(library); + } - return result; + invokeFunc invokePlugin = (invokeFunc)GetProcAddress(lib, "invoke"); + void* result = NULL; + if (invokePlugin != NULL) { + result = invokePlugin(command, args); } else { - LOG((CLOG_DEBUG "invoke command failed, plugin: %s command: %s", - plugin, command)); - return NULL; + LOG((CLOG_DEBUG "no invoke function in %s", plugin)); } + + return result; } void diff --git a/src/lib/arch/win32/ArchPluginWindows.h b/src/lib/arch/win32/ArchPluginWindows.h index fd325dfe0..50f77a64b 100644 --- a/src/lib/arch/win32/ArchPluginWindows.h +++ b/src/lib/arch/win32/ArchPluginWindows.h @@ -41,7 +41,8 @@ class ArchPluginWindows : public IArchPlugin { bool exists(const char* name); void* invoke(const char* pluginName, const char* functionName, - void** args); + void** args, + void* library = NULL); private: void getFilenames(const String& pattern, std::vector& filenames); From dc72b4e512bf13b10c72c400178405b7ba1db099 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 16 Jul 2015 14:25:03 -0700 Subject: [PATCH 055/572] Changed plugin version mismatch log level to error #4866 --- src/lib/arch/unix/ArchPluginUnix.cpp | 6 +++--- src/lib/arch/win32/ArchPluginWindows.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/arch/unix/ArchPluginUnix.cpp b/src/lib/arch/unix/ArchPluginUnix.cpp index ce81946f5..a1f364110 100644 --- a/src/lib/arch/unix/ArchPluginUnix.cpp +++ b/src/lib/arch/unix/ArchPluginUnix.cpp @@ -87,16 +87,16 @@ ArchPluginUnix::load() String pluginName = filename.substr(pos + 3); char* version = (char*)invoke(filename.c_str(), "version", NULL, library); String expectedVersion(pluginVersion(pluginName.c_str())); - + if (version != NULL && expectedVersion.compare(version) == 0) { LOG((CLOG_DEBUG "loaded plugin: %s (%s)", (*it).c_str(), version)); m_pluginTable.insert(std::make_pair(filename, library)); } else { - LOG((CLOG_WARN "plugin version doesn't match")); + LOG((CLOG_ERR "plugin version doesn't match")); LOG((CLOG_DEBUG "expected plugin version: %s actual plugin version: %s", expectedVersion.c_str(), version)); - LOG((CLOG_WARN "skip plugin: %s", (*it).c_str())); + LOG((CLOG_ERR "skip plugin: %s", (*it).c_str())); dlclose(library); } } diff --git a/src/lib/arch/win32/ArchPluginWindows.cpp b/src/lib/arch/win32/ArchPluginWindows.cpp index 63a1cf410..6cbad45ad 100644 --- a/src/lib/arch/win32/ArchPluginWindows.cpp +++ b/src/lib/arch/win32/ArchPluginWindows.cpp @@ -78,10 +78,10 @@ ArchPluginWindows::load() m_pluginTable.insert(std::make_pair(pluginName, lib)); } else { - LOG((CLOG_WARN "plugin version doesn't match")); + LOG((CLOG_ERR "plugin version doesn't match")); LOG((CLOG_DEBUG "expected plugin version: %s actual plugin version: %s", expectedVersion.c_str(), version)); - LOG((CLOG_WARN "skip plugin: %s", (*it).c_str())); + LOG((CLOG_ERR "skip plugin: %s", (*it).c_str())); FreeLibrary(library); } } From bfabd436d70f1a62918e567c47cf0e6306c2dbe3 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 23 Jul 2015 15:56:38 -0700 Subject: [PATCH 056/572] Increased wizard version to force plugin download #4866 --- src/gui/src/AppConfig.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 3a304b91e..9bdd2265b 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -31,10 +31,11 @@ // 1: first version // 2: added language page // 3: added premium page and removed -// 4: ssl plugin 'ns' introduced -// 5: ssl plugin 'ns' updated +// 4: ssl plugin 'ns' v1.0 +// 5: ssl plugin 'ns' v1.1 +// 6: ssl plugin 'ns' v1.2 // -const int kWizardVersion = 5; +const int kWizardVersion = 6; class QSettings; class SettingsDialog; From cb5f0f7b12eca4a56dd71d9a9b96aed43c1e1b68 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 23 Jul 2015 18:02:05 -0700 Subject: [PATCH 057/572] Improved plugin version logging for Windows #4866 Conflicts: src/lib/arch/win32/ArchPluginWindows.cpp --- src/lib/arch/win32/ArchPluginWindows.cpp | 64 +++++++++++++++++++++----------- src/lib/arch/win32/ArchPluginWindows.h | 1 + src/lib/common/PluginVersion.cpp | 8 ++-- src/lib/common/PluginVersion.h | 7 +++- src/lib/plugin/ns/ns.cpp | 2 +- 5 files changed, 54 insertions(+), 28 deletions(-) diff --git a/src/lib/arch/win32/ArchPluginWindows.cpp b/src/lib/arch/win32/ArchPluginWindows.cpp index 6cbad45ad..7a8c62483 100644 --- a/src/lib/arch/win32/ArchPluginWindows.cpp +++ b/src/lib/arch/win32/ArchPluginWindows.cpp @@ -57,33 +57,42 @@ ArchPluginWindows::load() std::vector::iterator it; for (it = plugins.begin(); it != plugins.end(); ++it) { - LOG((CLOG_DEBUG "loading plugin: %s", (*it).c_str())); - String path = String(dir).append("\\").append(*it); - HINSTANCE library = LoadLibrary(path.c_str()); - - if (library == NULL) { + String filename = *it; + String nameNoExt = synergy::string::removeFileExt(*it); + 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", (*it).c_str(), error.c_str())); + LOG((CLOG_ERR "failed to load plugin '%s', error: %s", + filename.c_str(), error.c_str())); continue; } - void* lib = reinterpret_cast(library); + String expectedVersion = getExpectedPluginVersion(nameNoExt.c_str()); + String currentVersion = getCurrentVersion(nameNoExt.c_str(), voidHandle); - String pluginName = synergy::string::removeFileExt(*it); - char* version = (char*)invoke(pluginName.c_str(), "version", NULL, lib); - String expectedVersion(pluginVersion(pluginName.c_str())); + 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())); - if (version != NULL && expectedVersion.compare(version) == 0) { - LOG((CLOG_DEBUG "loaded plugin: %s (%s)", (*it).c_str(), version)); - m_pluginTable.insert(std::make_pair(pluginName, lib)); - } - else { - LOG((CLOG_ERR "plugin version doesn't match")); - LOG((CLOG_DEBUG "expected plugin version: %s actual plugin version: %s", - expectedVersion.c_str(), version)); - LOG((CLOG_ERR "skip plugin: %s", (*it).c_str())); - FreeLibrary(library); + FreeLibrary(handle); + continue; } + + LOG((CLOG_DEBUG "plugin loaded: %s (version %s)", + filename.c_str(), + currentVersion.c_str())); + + m_pluginTable.insert(std::make_pair(nameNoExt, voidHandle)); } } @@ -208,11 +217,24 @@ ArchPluginWindows::getFilenames(const String& pattern, std::vector& file FindClose(find); } -String ArchPluginWindows::getPluginsDir() +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) { diff --git a/src/lib/arch/win32/ArchPluginWindows.h b/src/lib/arch/win32/ArchPluginWindows.h index 50f77a64b..86f5c8123 100644 --- a/src/lib/arch/win32/ArchPluginWindows.h +++ b/src/lib/arch/win32/ArchPluginWindows.h @@ -47,6 +47,7 @@ class ArchPluginWindows : public IArchPlugin { private: void getFilenames(const String& pattern, std::vector& filenames); String getPluginsDir(); + String getCurrentVersion(const String& name, void* handle); private: PluginTable m_pluginTable; diff --git a/src/lib/common/PluginVersion.cpp b/src/lib/common/PluginVersion.cpp index 35a8ceb5e..a34fd9ad0 100644 --- a/src/lib/common/PluginVersion.cpp +++ b/src/lib/common/PluginVersion.cpp @@ -21,13 +21,13 @@ static const int kpluginCount = 1; static const char kUnknownVersion[] = "unknown"; -static const char* s_pluginNames[] = {"ns"}; -static const char* s_pluginVersions[] = {"1.2"}; +static const char* s_pluginNames[] = { "ns" }; +static const char* s_pluginVersions[] = { "1.2" }; -const char* pluginVersion(const char* pluginName) +const char* getExpectedPluginVersion(const char* name) { for (int i = 0; i < kpluginCount; i++) { - if (strcmp(pluginName, s_pluginNames[i]) == 0) { + if (strcmp(name, s_pluginNames[i]) == 0) { return s_pluginVersions[i]; break; } diff --git a/src/lib/common/PluginVersion.h b/src/lib/common/PluginVersion.h index 40c6ac94b..738afef9c 100644 --- a/src/lib/common/PluginVersion.h +++ b/src/lib/common/PluginVersion.h @@ -17,5 +17,8 @@ #pragma once -// return plugin version map -const char* pluginVersion(const char* pluginName); +//! 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/ns/ns.cpp b/src/lib/plugin/ns/ns.cpp index 0fc2161aa..a053d7052 100644 --- a/src/lib/plugin/ns/ns.cpp +++ b/src/lib/plugin/ns/ns.cpp @@ -107,7 +107,7 @@ invoke(const char* command, void** args) } } else if (strcmp(command, "version") == 0) { - return (void*)pluginVersion(kPluginName); + return (void*)getExpectedPluginVersion(kPluginName); } return NULL; From 3eb1bffb702fa5a8bf330d94fae648fcc2035acd Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 23 Jul 2015 18:16:06 -0700 Subject: [PATCH 058/572] Improved variable name for plugin loading on Windows #4866 --- src/lib/arch/win32/ArchPluginWindows.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/arch/win32/ArchPluginWindows.cpp b/src/lib/arch/win32/ArchPluginWindows.cpp index 7a8c62483..7d26520f0 100644 --- a/src/lib/arch/win32/ArchPluginWindows.cpp +++ b/src/lib/arch/win32/ArchPluginWindows.cpp @@ -58,7 +58,7 @@ ArchPluginWindows::load() std::vector::iterator it; for (it = plugins.begin(); it != plugins.end(); ++it) { String filename = *it; - String nameNoExt = synergy::string::removeFileExt(*it); + String name = synergy::string::removeFileExt(filename); String path = synergy::string::sprintf( "%s\\%s", dir.c_str(), filename.c_str()); @@ -73,8 +73,8 @@ ArchPluginWindows::load() continue; } - String expectedVersion = getExpectedPluginVersion(nameNoExt.c_str()); - String currentVersion = getCurrentVersion(nameNoExt.c_str(), voidHandle); + String expectedVersion = getExpectedPluginVersion(name.c_str()); + String currentVersion = getCurrentVersion(name.c_str(), voidHandle); if (currentVersion.empty() || (expectedVersion != currentVersion)) { LOG((CLOG_ERR @@ -92,7 +92,7 @@ ArchPluginWindows::load() filename.c_str(), currentVersion.c_str())); - m_pluginTable.insert(std::make_pair(nameNoExt, voidHandle)); + m_pluginTable.insert(std::make_pair(name, voidHandle)); } } From fedad2b8a1274e98424481918c750ecb999ddbf2 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Fri, 24 Jul 2015 20:44:48 +0100 Subject: [PATCH 059/572] Improved plugin version logging for Unix #4866 --- src/lib/arch/unix/ArchPluginUnix.cpp | 60 ++++++++++++++++++++++++------------ src/lib/arch/unix/ArchPluginUnix.h | 3 +- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/lib/arch/unix/ArchPluginUnix.cpp b/src/lib/arch/unix/ArchPluginUnix.cpp index a1f364110..caeaab487 100644 --- a/src/lib/arch/unix/ArchPluginUnix.cpp +++ b/src/lib/arch/unix/ArchPluginUnix.cpp @@ -73,32 +73,41 @@ ArchPluginUnix::load() std::vector::iterator it; for (it = plugins.begin(); it != plugins.end(); ++it) { - LOG((CLOG_DEBUG "loading plugin: %s", (*it).c_str())); - String path = String(getPluginsDir()).append("/").append(*it); - void* library = dlopen(path.c_str(), RTLD_LAZY); + String filename = *it; + String path = synergy::string::sprintf( + "%s/%s", pluginsDir.c_str(), filename.c_str()); + String name = synergy::string::removeFileExt(filename.substr(3)); - if (library == NULL) { - LOG((CLOG_ERR "failed to load plugin '%s', error: %s", (*it).c_str(), dlerror())); + 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 filename = synergy::string::removeFileExt(*it); - size_t pos = filename.find("lib"); - String pluginName = filename.substr(pos + 3); - char* version = (char*)invoke(filename.c_str(), "version", NULL, library); - String expectedVersion(pluginVersion(pluginName.c_str())); - if (version != NULL && expectedVersion.compare(version) == 0) { - LOG((CLOG_DEBUG "loaded plugin: %s (%s)", (*it).c_str(), version)); - m_pluginTable.insert(std::make_pair(filename, library)); - } - else { - LOG((CLOG_ERR "plugin version doesn't match")); - LOG((CLOG_DEBUG "expected plugin version: %s actual plugin version: %s", - expectedVersion.c_str(), version)); - LOG((CLOG_ERR "skip plugin: %s", (*it).c_str())); - dlclose(library); + 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)); } } @@ -204,6 +213,17 @@ 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) { diff --git a/src/lib/arch/unix/ArchPluginUnix.h b/src/lib/arch/unix/ArchPluginUnix.h index 894c675b5..eeb14691b 100644 --- a/src/lib/arch/unix/ArchPluginUnix.h +++ b/src/lib/arch/unix/ArchPluginUnix.h @@ -41,8 +41,9 @@ class ArchPluginUnix : public IArchPlugin { void** args, void* library = NULL); - private: +private: String getPluginsDir(); + String getCurrentVersion(const String& name, void* handle); private: PluginTable m_pluginTable; From b105bc8f4282dc48a2dcf58900fa11d28006b8a1 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Fri, 24 Jul 2015 20:45:50 +0100 Subject: [PATCH 060/572] Unified secure socket name across platforms #4866 --- src/lib/client/Client.cpp | 4 ---- src/lib/net/TCPSocketFactory.cpp | 4 ---- src/lib/server/ClientListener.cpp | 4 ---- 3 files changed, 12 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 30c80ae1d..bc11a1e92 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -46,11 +46,7 @@ #include #include -#if defined _WIN32 static const char s_networkSecurity[] = { "ns" }; -#else -static const char s_networkSecurity[] = { "libns" }; -#endif // // Client diff --git a/src/lib/net/TCPSocketFactory.cpp b/src/lib/net/TCPSocketFactory.cpp index fdf276a2c..8a4acef23 100644 --- a/src/lib/net/TCPSocketFactory.cpp +++ b/src/lib/net/TCPSocketFactory.cpp @@ -27,11 +27,7 @@ // TCPSocketFactory // -#if defined _WIN32 static const char s_networkSecurity[] = { "ns" }; -#else -static const char s_networkSecurity[] = { "libns" }; -#endif TCPSocketFactory::TCPSocketFactory(IEventQueue* events, SocketMultiplexer* socketMultiplexer) : m_events(events), diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index 874997a88..e335f575d 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -33,11 +33,7 @@ // ClientListener // -#if defined _WIN32 static const char s_networkSecurity[] = { "ns" }; -#else -static const char s_networkSecurity[] = { "libns" }; -#endif ClientListener::ClientListener(const NetworkAddress& address, ISocketFactory* socketFactory, From 945ccfdb753db176239a33df47757ad267007d14 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 24 Jul 2015 13:17:18 -0700 Subject: [PATCH 061/572] Refactored plugin names #4866 --- src/lib/client/Client.cpp | 7 +++---- src/lib/common/PluginVersion.cpp | 5 +---- src/lib/common/PluginVersion.h | 9 +++++++++ src/lib/net/TCPSocketFactory.cpp | 7 +++---- src/lib/plugin/ns/ns.cpp | 3 +-- src/lib/server/ClientListener.cpp | 7 +++---- 6 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index bc11a1e92..33e2a6cab 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -39,6 +39,7 @@ #include "base/IEventQueue.h" #include "base/TMethodEventJob.h" #include "base/TMethodJob.h" +#include "common/PluginVersion.h" #include "common/stdexcept.h" #include @@ -46,8 +47,6 @@ #include #include -static const char s_networkSecurity[] = { "ns" }; - // // Client // @@ -103,7 +102,7 @@ Client::Client( } if (m_args.m_enableCrypto) { - m_useSecureNetwork = ARCH->plugin().exists(s_networkSecurity); + m_useSecureNetwork = ARCH->plugin().exists(s_pluginNames[kSecureSocket]); if (m_useSecureNetwork == false) { LOG((CLOG_NOTE "crypto disabled because of ns plugin not available")); } @@ -585,7 +584,7 @@ Client::cleanupStream() // we need to tell the dynamic lib that allocated this object // to do the deletion. if (m_useSecureNetwork) { - ARCH->plugin().invoke(s_networkSecurity, "deleteSocket", NULL); + ARCH->plugin().invoke(s_pluginNames[kSecureSocket], "deleteSocket", NULL); } } diff --git a/src/lib/common/PluginVersion.cpp b/src/lib/common/PluginVersion.cpp index a34fd9ad0..656b80fc2 100644 --- a/src/lib/common/PluginVersion.cpp +++ b/src/lib/common/PluginVersion.cpp @@ -19,14 +19,11 @@ #include -static const int kpluginCount = 1; static const char kUnknownVersion[] = "unknown"; -static const char* s_pluginNames[] = { "ns" }; -static const char* s_pluginVersions[] = { "1.2" }; const char* getExpectedPluginVersion(const char* name) { - for (int i = 0; i < kpluginCount; i++) { + for (int i = 0; i < kPluginCount; i++) { if (strcmp(name, s_pluginNames[i]) == 0) { return s_pluginVersions[i]; break; diff --git a/src/lib/common/PluginVersion.h b/src/lib/common/PluginVersion.h index 738afef9c..02839ef65 100644 --- a/src/lib/common/PluginVersion.h +++ b/src/lib/common/PluginVersion.h @@ -17,6 +17,15 @@ #pragma once +enum EPluginType { + kSecureSocket, + kPluginCount +}; + +static const char* s_pluginNames[] = { "ns" }; +static const char* s_pluginVersions[] = { "1.2" }; + + //! Get expected plugin version /*! Returns the plugin version expected by the plugin loader. diff --git a/src/lib/net/TCPSocketFactory.cpp b/src/lib/net/TCPSocketFactory.cpp index 8a4acef23..0f92ffb9a 100644 --- a/src/lib/net/TCPSocketFactory.cpp +++ b/src/lib/net/TCPSocketFactory.cpp @@ -21,14 +21,13 @@ #include "net/TCPSocket.h" #include "net/TCPListenSocket.h" #include "arch/Arch.h" +#include "common/PluginVersion.h" #include "base/Log.h" // // TCPSocketFactory // -static const char s_networkSecurity[] = { "ns" }; - TCPSocketFactory::TCPSocketFactory(IEventQueue* events, SocketMultiplexer* socketMultiplexer) : m_events(events), m_socketMultiplexer(socketMultiplexer) @@ -51,7 +50,7 @@ TCPSocketFactory::create(bool secure) const m_socketMultiplexer }; socket = static_cast( - ARCH->plugin().invoke(s_networkSecurity, "getSocket", args)); + ARCH->plugin().invoke(s_pluginNames[kSecureSocket], "getSocket", args)); } else { socket = new TCPSocket(m_events, m_socketMultiplexer); @@ -70,7 +69,7 @@ TCPSocketFactory::createListen(bool secure) const m_socketMultiplexer }; socket = static_cast( - ARCH->plugin().invoke(s_networkSecurity, "getListenSocket", args)); + ARCH->plugin().invoke(s_pluginNames[kSecureSocket], "getListenSocket", args)); } else { socket = new TCPListenSocket(m_events, m_socketMultiplexer); diff --git a/src/lib/plugin/ns/ns.cpp b/src/lib/plugin/ns/ns.cpp index a053d7052..e7ae164e6 100644 --- a/src/lib/plugin/ns/ns.cpp +++ b/src/lib/plugin/ns/ns.cpp @@ -32,7 +32,6 @@ SecureSocket* g_secureSocket = NULL; SecureListenSocket* g_secureListenSocket = NULL; Arch* g_arch = NULL; Log* g_log = NULL; -static const char kPluginName[] = "ns"; std::string helperGetLibsUsed(void) @@ -107,7 +106,7 @@ invoke(const char* command, void** args) } } else if (strcmp(command, "version") == 0) { - return (void*)getExpectedPluginVersion(kPluginName); + return (void*)getExpectedPluginVersion(s_pluginNames[kSecureSocket]); } return NULL; diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index e335f575d..9b103493b 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -25,6 +25,7 @@ #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" @@ -33,8 +34,6 @@ // ClientListener // -static const char s_networkSecurity[] = { "ns" }; - ClientListener::ClientListener(const NetworkAddress& address, ISocketFactory* socketFactory, IEventQueue* events, @@ -49,7 +48,7 @@ ClientListener::ClientListener(const NetworkAddress& address, try { // create listen socket if (enableCrypto) { - m_useSecureNetwork = ARCH->plugin().exists(s_networkSecurity); + m_useSecureNetwork = ARCH->plugin().exists(s_pluginNames[kSecureSocket]); if (m_useSecureNetwork == false) { LOG((CLOG_NOTE "crypto disabled because of ns plugin not available")); } @@ -249,7 +248,7 @@ ClientListener::cleanupListenSocket() } else { ARCH->plugin().invoke( - s_networkSecurity, + s_pluginNames[kSecureSocket], "deleteListenSocket", NULL); } From 0f4837aa2138decdd6690dd5e0a95e9360367ac6 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 24 Jul 2015 13:34:22 -0700 Subject: [PATCH 062/572] Fixed warning on Mac 10.6 #4866 --- src/lib/common/PluginVersion.cpp | 2 ++ src/lib/common/PluginVersion.h | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/common/PluginVersion.cpp b/src/lib/common/PluginVersion.cpp index 656b80fc2..1885d2a1a 100644 --- a/src/lib/common/PluginVersion.cpp +++ b/src/lib/common/PluginVersion.cpp @@ -20,6 +20,8 @@ #include static const char kUnknownVersion[] = "unknown"; +const char* s_pluginNames[] = { "ns" }; +static const char* s_pluginVersions[] = { "1.2" }; const char* getExpectedPluginVersion(const char* name) { diff --git a/src/lib/common/PluginVersion.h b/src/lib/common/PluginVersion.h index 02839ef65..c0b2d0d7d 100644 --- a/src/lib/common/PluginVersion.h +++ b/src/lib/common/PluginVersion.h @@ -22,9 +22,7 @@ enum EPluginType { kPluginCount }; -static const char* s_pluginNames[] = { "ns" }; -static const char* s_pluginVersions[] = { "1.2" }; - +extern const char* s_pluginNames[]; //! Get expected plugin version /*! From c55cf1936b2e54191bd04b0680e3ac010b8a9fb2 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 3 Aug 2015 16:28:13 -0700 Subject: [PATCH 063/572] Added plugin version header in GUI project #4866 --- src/gui/gui.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.pro b/src/gui/gui.pro index fdee073cf..28d1ee6ee 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -107,7 +107,7 @@ HEADERS += src/MainWindow.h \ src/SslCertificate.h \ src/FileSysClient.h \ src/Plugin.h \ - src/WebClient.h + src/WebClient.h \ ../lib/common/PluginVersion.h RESOURCES += res/Synergy.qrc From 5b1e7acca9db18b6602bde1fd98b733e7ba87918 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 3 Aug 2015 16:29:36 -0700 Subject: [PATCH 064/572] Plugin version to 1.3 --- src/lib/common/PluginVersion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/common/PluginVersion.cpp b/src/lib/common/PluginVersion.cpp index 1885d2a1a..25ebaf83c 100644 --- a/src/lib/common/PluginVersion.cpp +++ b/src/lib/common/PluginVersion.cpp @@ -21,7 +21,7 @@ static const char kUnknownVersion[] = "unknown"; const char* s_pluginNames[] = { "ns" }; -static const char* s_pluginVersions[] = { "1.2" }; +static const char* s_pluginVersions[] = { "1.3" }; const char* getExpectedPluginVersion(const char* name) { From 95c9ffe7e8abed29ef952650eb4d3c24777e8ea5 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Mon, 3 Aug 2015 16:38:42 -0700 Subject: [PATCH 065/572] Removed unused variable #4866 --- src/lib/arch/unix/ArchPluginUnix.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/arch/unix/ArchPluginUnix.cpp b/src/lib/arch/unix/ArchPluginUnix.cpp index caeaab487..46f6a4765 100644 --- a/src/lib/arch/unix/ArchPluginUnix.cpp +++ b/src/lib/arch/unix/ArchPluginUnix.cpp @@ -36,7 +36,6 @@ typedef void (*cleanupFunc)(); void* g_eventTarget = NULL; IEventQueue* g_events = NULL; -static const char kPre174Plugin[] = "Pre-1.7.4"; ArchPluginUnix::ArchPluginUnix() { From 1b8f055ff282afe1618afb99dfafaa867a01a705 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 5 Aug 2015 11:26:58 -0700 Subject: [PATCH 066/572] ChangeLog for v1.7.4-stable --- ChangeLog | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2ed63bfd5..9fd0c1821 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,22 +1,15 @@ v1.7.4-stable ============= -Bug #4809 - Intermittent freeze caused by mutex deadlock in logging code -Bug #4721 - High CPU usage for Windows service on client -Bug #4712 - Unable to send clipboard with size above 1KB when using SSL +Bug #4721 - High CPU usage for Windows service +Bug #4750 - SSL connect error 'passive ssl error limit' Bug #4584 - Drag and drop with SSL causes crash -Bug #3774 - Missing MinGW dependencies after install on Windows Bug #4749 - Clipboard thread race condition causes assertion failure -Bug #4723 - Waiting for active desktop result freezes Windows service -Bug #4690 - Log line 'activeDesktop' does not use logging system Bug #4720 - Plugin download shows 'Could not get Linux package type' error -Bug #4737 - Using error log level does not show SSL fingerprint dialog -Bug #451 - Fast cursor on any client with Mac server -Bug #4810 - Non-existent file listed in Qt gui.pro file -Enhancement #4696 - Include 'ns' plugin in installers instead of wizard download -Enhancement #4796 - Improve secure socket intensive try operations -Enhancement #4327 - GUI setting to disable drag and drop feature -Enhancement #4745 - Tray icon notification for clipboard data transfer progress -Enhancement #4793 - Additional logging to output OpenSSL version +Bug #4712 - Unable to send clipboard with size above 1KB when using SSL +Bug #4642 - Connecting causes SSL23_GET_SERVER_HELLO error +Bug #4690 - Log line 'activeDesktop' does not use logging system +Bug #4866 - Wrong ns plugin version can be loaded +Enhancement #4901 - Auto restart when running from GUI in desktop mode Enhancement #4845 - Add timestamp to log output v1.7.3-stable From 9b09703c1addb5bbfda7ca1cbf5e79a7a9b99703 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Wed, 5 Aug 2015 17:51:41 +0100 Subject: [PATCH 067/572] Revert "Allowed reconnect after SSL fatal error #4857" This reverts commit cbd63e9d67ead4199d5ab49b17d68a7583421d92. --- src/lib/plugin/ns/SecureSocket.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 034a7f778..80bad6dcc 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -516,6 +516,7 @@ SecureSocket::getError() void SecureSocket::disconnect() { + sendEvent(getEvents()->forISocket().stopRetry()); sendEvent(getEvents()->forISocket().disconnected()); sendEvent(getEvents()->forIStream().inputShutdown()); } From d2c94bfb0467478acdb5cecf609ed49f3db3ca6d Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 6 Aug 2015 12:57:57 -0700 Subject: [PATCH 068/572] Revert "Removed mutex locking from get and set of log level #4809" This reverts commit 3eef49d5c983e036c8e5cc6fa2bd16c44d7217de. --- src/lib/base/Log.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/base/Log.cpp b/src/lib/base/Log.cpp index 257088f3e..7ad8ae1ba 100644 --- a/src/lib/base/Log.cpp +++ b/src/lib/base/Log.cpp @@ -260,12 +260,14 @@ Log::setFilter(const char* maxPriority) void Log::setFilter(int maxPriority) { + ArchMutexLock lock(m_mutex); m_maxPriority = maxPriority; } int Log::getFilter() const { + ArchMutexLock lock(m_mutex); return m_maxPriority; } From 588fb4b805dd452556d05dbc03fe29ea5b4e43c0 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 7 Aug 2015 14:12:33 -0700 Subject: [PATCH 069/572] ChangeLog for v1.7.4-stable --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 9fd0c1821..76285f2d7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,7 @@ Bug #4690 - Log line 'activeDesktop' does not use logging system Bug #4866 - Wrong ns plugin version can be loaded Enhancement #4901 - Auto restart when running from GUI in desktop mode Enhancement #4845 - Add timestamp to log output +Enhancement #4898 - Move version stage name to build config v1.7.3-stable ============= From 5da7290242e37aea3c96a56d61c245e4e2290b14 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 7 Aug 2015 15:55:58 -0700 Subject: [PATCH 070/572] Stopped process before showing the fingerprint message #4901 --- src/gui/src/MainWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 7f3753edd..daf535a55 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -439,6 +439,8 @@ void MainWindow::checkFingerprint(const QString& line) static bool messageBoxAlreadyShown = false; if (!messageBoxAlreadyShown) { + stopSynergy(); + messageBoxAlreadyShown = true; QMessageBox::StandardButton fingerprintReply = QMessageBox::information( @@ -455,8 +457,6 @@ void MainWindow::checkFingerprint(const QString& line) .arg(fingerprint), QMessageBox::Yes | QMessageBox::No); - stopSynergy(); - if (fingerprintReply == QMessageBox::Yes) { // restart core process after trusting fingerprint. Fingerprint::trustedServers().trust(fingerprint); From f00f60a8fe7930227fbcd5a84c71c0fd56acf202 Mon Sep 17 00:00:00 2001 From: Robby Stahl Date: Thu, 10 Sep 2015 14:04:01 -0700 Subject: [PATCH 071/572] 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 faa170b40dbc152123b39a29ec63424f83841baa Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 13 Oct 2015 15:19:44 -0700 Subject: [PATCH 072/572] Suppress unit tests and integtests warning for Mac 10.11 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 00cc15967..618902dc6 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") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wno-unused-local-typedef") if (NOT APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") From 6320156279e6890bb9bcba12c0595fabd15b8f6b Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Tue, 15 Sep 2015 14:55:58 -0700 Subject: [PATCH 073/572] Add stage and revision info in about page #4893 --- ext/toolchain/commands1.py | 4 +++- src/gui/gui.pro | 2 ++ src/gui/src/AboutDialog.cpp | 4 +++- src/gui/src/VersionChecker.cpp | 2 ++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 0a3002994..da0af1aa7 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -508,6 +508,8 @@ def configureGui(self, target="", extraArgs=""): qmake_cmd_string += " QMAKE_MAC_SDK=" + shortForm qmake_cmd_string += " QMAKE_MAC_SDK." + shortForm + ".path=" + sdkDir + qmake_cmd_string += " QMAKE_VERSION_STAGE=" + self.getVersionStage() + qmake_cmd_string += " QMAKE_VERSION_REVISION=" + self.getGitRevision() print "QMake command: " + qmake_cmd_string # run qmake from the gui dir @@ -957,7 +959,7 @@ def find_revision(self): def getGitRevision(self): if sys.version_info < (2, 4): raise Exception("Python 2.4 or greater required.") - + p = subprocess.Popen( ["git", "log", "--pretty=format:%h", "-n", "1"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 28d1ee6ee..6cf5d3991 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -2,6 +2,8 @@ QT += widgets \ network TEMPLATE = app TARGET = synergy +DEFINES += VERSION_STAGE=\\\"$$QMAKE_VERSION_STAGE\\\" +DEFINES += VERSION_REVISION=\\\"$$QMAKE_VERSION_REVISION\\\" DEPENDPATH += . \ res INCLUDEPATH += . \ diff --git a/src/gui/src/AboutDialog.cpp b/src/gui/src/AboutDialog.cpp index f2d69d3b6..ee528e9c9 100644 --- a/src/gui/src/AboutDialog.cpp +++ b/src/gui/src/AboutDialog.cpp @@ -28,7 +28,9 @@ AboutDialog::AboutDialog(QWidget* parent, const QString& synergyApp) : setupUi(this); m_versionChecker.setApp(synergyApp); - m_pLabelSynergyVersion->setText(m_versionChecker.getVersion()); + QString version = m_versionChecker.getVersion(); + version = version + '-' + VERSION_STAGE + '-' + VERSION_REVISION; + m_pLabelSynergyVersion->setText(version); // change default size based on os #if defined(Q_OS_MAC) diff --git a/src/gui/src/VersionChecker.cpp b/src/gui/src/VersionChecker.cpp index 26543eb6b..dcb5720fc 100644 --- a/src/gui/src/VersionChecker.cpp +++ b/src/gui/src/VersionChecker.cpp @@ -96,7 +96,9 @@ QString VersionChecker::getVersion() QRegExp rx(VERSION_REGEX); QString text = process.readLine(); if (rx.indexIn(text) != -1) + { return rx.cap(1); + } } return tr("Unknown"); From d61b5327051fce428f02542994387ba71367084f Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 19 Oct 2015 16:06:02 -0700 Subject: [PATCH 074/572] Add built date #4893 --- src/gui/res/AboutDialogBase.ui | 31 +++++++++++++++++++++++++++++++ src/gui/src/AboutDialog.cpp | 2 ++ 2 files changed, 33 insertions(+) diff --git a/src/gui/res/AboutDialogBase.ui b/src/gui/res/AboutDialogBase.ui index dae5394b7..bd2e993d7 100644 --- a/src/gui/res/AboutDialogBase.ui +++ b/src/gui/res/AboutDialogBase.ui @@ -157,6 +157,37 @@ Visit our website for help and info (synergy-project.org).
+ +
+ + + + + + Build Date: + + + + + + + Unknown + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + diff --git a/src/gui/src/AboutDialog.cpp b/src/gui/src/AboutDialog.cpp index ee528e9c9..aefd3b72c 100644 --- a/src/gui/src/AboutDialog.cpp +++ b/src/gui/src/AboutDialog.cpp @@ -32,6 +32,8 @@ AboutDialog::AboutDialog(QWidget* parent, const QString& synergyApp) : version = version + '-' + VERSION_STAGE + '-' + VERSION_REVISION; m_pLabelSynergyVersion->setText(version); + m_pLabelBuildDate->setText(QDate::currentDate().toString()); + // change default size based on os #if defined(Q_OS_MAC) QSize size(600, 380); From 4197fb7a0e1ac43b609a6f0a250995ca45fb35c2 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 19 Oct 2015 16:59:25 -0700 Subject: [PATCH 075/572] Remove unused viable #4696 --- src/gui/src/PluginWizardPage.cpp | 21 +++++++++------------ src/gui/src/PluginWizardPage.h | 2 -- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/gui/src/PluginWizardPage.cpp b/src/gui/src/PluginWizardPage.cpp index 27328fbc5..26da661f0 100644 --- a/src/gui/src/PluginWizardPage.cpp +++ b/src/gui/src/PluginWizardPage.cpp @@ -20,7 +20,6 @@ #include "SslCertificate.h" #include "FileSysClient.h" -#include "WebClient.h" #include "PluginManager.h" #include "MainWindow.h" @@ -178,20 +177,18 @@ bool PluginWizardPage::isComplete() const void PluginWizardPage::initializePage() { QWizardPage::initializePage(); - if (m_pFileSysClient == NULL) { - if (m_Email.isEmpty() || - m_Password.isEmpty()) { - updateStatus(tr("Setup complete.")); - showFinished(); - return; - } - m_pLabelSpinning->show(); + if (m_Email.isEmpty() || + m_Password.isEmpty()) { + updateStatus(tr("Setup complete.")); + showFinished(); + return; + } + if (m_pFileSysClient == NULL) { m_pFileSysClient = new FileSysClient(); - m_pWebClient = new WebClient(); - m_pWebClient->setEmail(m_Email); - m_pWebClient->setPassword(m_Password); + + m_pLabelSpinning->show(); QThread* thread = new QThread; diff --git a/src/gui/src/PluginWizardPage.h b/src/gui/src/PluginWizardPage.h index 8b2006b79..d8cef4cce 100644 --- a/src/gui/src/PluginWizardPage.h +++ b/src/gui/src/PluginWizardPage.h @@ -25,7 +25,6 @@ #include class FileSysClient; -class WebClient; class SslCertificate; class MainWindow; @@ -62,7 +61,6 @@ protected slots: bool m_Finished; QString m_Email; QString m_Password; - WebClient* m_pWebClient; FileSysClient* m_pFileSysClient; PluginManager m_PluginManager; SslCertificate* m_pSslCertificate; From 6c4ee29649c5c88ed3bd21966562cf1a5994825f Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 19 Oct 2015 17:02:59 -0700 Subject: [PATCH 076/572] Remove unused functions in WebClient #4696 --- src/gui/src/WebClient.cpp | 50 ----------------------------------------------- src/gui/src/WebClient.h | 6 ------ 2 files changed, 56 deletions(-) diff --git a/src/gui/src/WebClient.cpp b/src/gui/src/WebClient.cpp index 8f2e77ed7..f9ae18430 100644 --- a/src/gui/src/WebClient.cpp +++ b/src/gui/src/WebClient.cpp @@ -89,56 +89,6 @@ int WebClient::getEdition( return edition; } -void WebClient::queryPluginList() -{ - QString responseJson; - try { - QStringList args("--get-plugin-list"); - responseJson = request(m_Email, m_Password, args); - } - catch (std::exception& e) - { - emit error(tr("An error occurred while trying to query the " - "plugin list. Please contact the help desk, and " - "provide the following details.\n\n%1").arg(e.what())); - return; - } - - QRegExp resultRegex(".*\"result\".*:.*(true|false).*"); - if (resultRegex.exactMatch(responseJson)) { - QString boolString = resultRegex.cap(1); - if (boolString == "true") { - QRegExp editionRegex(".*\"plugins\".*:.*\"([^\"]+)\".*"); - if (editionRegex.exactMatch(responseJson)) { - QString e = editionRegex.cap(1); - m_PluginList = e.split(","); - } - emit queryPluginDone(); - return; - } - else if (boolString == "false") { - emit error(tr("Get plugin list failed, invalid user email " - "or password.")); - return; - } - } - else { - QRegExp errorRegex(".*\"error\".*:.*\"([^\"]+)\".*"); - if (errorRegex.exactMatch(responseJson)) { - - // replace "\n" with real new lines. - QString e = errorRegex.cap(1).replace("\\n", "\n"); - emit error(tr("Get plugin list failed, an error occurred." - "\n\n%1").arg(e)); - return; - } - } - - emit error(tr("Get plugin list failed, an error occurred.\n\n" - "Server response:\n\n%1").arg(responseJson)); - return; -} - QString WebClient::request( const QString& email, const QString& password, diff --git a/src/gui/src/WebClient.h b/src/gui/src/WebClient.h index b36162daf..7eecc8454 100644 --- a/src/gui/src/WebClient.h +++ b/src/gui/src/WebClient.h @@ -39,14 +39,9 @@ class WebClient : public QObject QWidget* w); void setEmail(QString& e) { m_Email = e; } void setPassword(QString& p) { m_Password = p; } - QStringList& getPluginList() { return m_PluginList; } - -public slots: - void queryPluginList(); signals: void error(QString e); - void queryPluginDone(); private: QString request(const QString& email, @@ -56,7 +51,6 @@ public slots: private: QString m_Email; QString m_Password; - QStringList m_PluginList; CoreInterface m_CoreInterface; }; From 084e2c1e05ab7ad8e161551df5a88233acefb74c Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 19 Oct 2015 18:07:08 -0700 Subject: [PATCH 077/572] Refactor FileSysClient into PluginManager #4696 --- src/gui/src/FileSysClient.cpp | 54 ------------ src/gui/src/FileSysClient.h | 63 -------------- src/gui/src/PluginManager.cpp | 53 +++++++++--- src/gui/src/PluginManager.h | 13 ++- src/gui/src/PluginWizardPage.cpp | 179 ++++++++++++++++++--------------------- src/gui/src/PluginWizardPage.h | 2 +- 6 files changed, 133 insertions(+), 231 deletions(-) delete mode 100644 src/gui/src/FileSysClient.cpp delete mode 100644 src/gui/src/FileSysClient.h diff --git a/src/gui/src/FileSysClient.cpp b/src/gui/src/FileSysClient.cpp deleted file mode 100644 index 61f09c570..000000000 --- a/src/gui/src/FileSysClient.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si, Std. - * - * 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 "FileSysClient.h" - -#include "EditionType.h" -#include "QUtility.h" - -#include -#include -#include -#include -#include - -void FileSysClient::queryPluginList() -{ - try { - isDone(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); - isDone(true); - } - catch (std::exception& e) - { - isDone(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/FileSysClient.h b/src/gui/src/FileSysClient.h deleted file mode 100644 index dfe7ee955..000000000 --- a/src/gui/src/FileSysClient.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si, Std. - * - * 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 FileSysClient_H -#define FileSysClient_H - -#include -#include -#include - -#include "Plugin.h" -#include "CoreInterface.h" - -class QMessageBox; -class QWidget; -class QStringList; - -class FileSysClient : public QObject -{ - Q_OBJECT - -public: - QStringList& getPluginList() { return m_PluginList; } - bool isDone() { return done; } - int count() { return copyCount; } - -public slots: - void queryPluginList(); - -signals: - void error(QString e); - void queryPluginDone(); - -private: - void isDone(bool b) { done = b; } - QString request(const QString& email, - const QString& password, - QStringList& args); - Plugin plugin; - void count(int i) { copyCount = i; } - -private: - int copyCount; - bool done; - QStringList m_PluginList; - CoreInterface m_CoreInterface; -}; - -#endif // FileSysClient_H diff --git a/src/gui/src/PluginManager.cpp b/src/gui/src/PluginManager.cpp index 92b9f9102..a39fef0b1 100644 --- a/src/gui/src/PluginManager.cpp +++ b/src/gui/src/PluginManager.cpp @@ -35,15 +35,17 @@ PluginManager::PluginManager() : - m_FileSysPluginList() + m_PluginList() { + init(); } -void PluginManager::initFromFileSys(QStringList pluginList) +PluginManager::~PluginManager() { - m_FileSysPluginList.clear(); - m_FileSysPluginList.append(pluginList); +} +void PluginManager::init() +{ m_PluginDir = m_CoreInterface.getPluginDir(); if (m_PluginDir.isEmpty()) { emit error(tr("Failed to get plugin directory.")); @@ -60,10 +62,6 @@ void PluginManager::initFromFileSys(QStringList pluginList) } } -PluginManager::~PluginManager() -{ -} - bool PluginManager::exist(QString name) { CoreInterface coreInterface; @@ -106,12 +104,12 @@ void PluginManager::copyPlugins() destDir.mkpath("."); } // Run through the list of plugins and copy them - for ( int i = 0 ; i < m_FileSysPluginList.size() ; i++ ) { + 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_FileSysPluginList.at(i)); + QFile file(srcDirName + QDir::separator() + m_PluginList.at(i)); // construct the destination file name - QString newName(destDirName + QDir::separator() + m_FileSysPluginList.at(i)); + QString newName(destDirName + QDir::separator() + m_PluginList.at(i)); // Check to see if the plugin already exists QFile newFile(newName); @@ -136,7 +134,7 @@ void PluginManager::copyPlugins() emit error( tr("Failed to copy plugin '%1' to: %2\n%3\n" "Please stop synergy and run the wizard again.") - .arg(m_FileSysPluginList.at(i)) + .arg(m_PluginList.at(i)) .arg(newName) .arg(file.errorString())); return; @@ -144,9 +142,9 @@ void PluginManager::copyPlugins() else { emit info( tr("Copying '%1' plugin (%2/%3)...") - .arg(m_FileSysPluginList.at(i)) + .arg(m_PluginList.at(i)) .arg(i+1) - .arg(m_FileSysPluginList.size())); + .arg(m_PluginList.size())); } } } @@ -160,3 +158,30 @@ void PluginManager::copyPlugins() 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 index cb9ef4af9..397153099 100644 --- a/src/gui/src/PluginManager.h +++ b/src/gui/src/PluginManager.h @@ -35,15 +35,18 @@ class PluginManager : public QObject PluginManager(); ~PluginManager(); - void initFromWeb(QStringList pluginList); - void initFromFileSys(QStringList pluginList); + void init(); - int pluginCount() { return m_FileSysPluginList.count(); } + 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); @@ -59,14 +62,16 @@ public slots: void info(QString i); void updateCopyStatus(int); void copyFinished(); + void queryPluginDone(); private: - QStringList m_FileSysPluginList; + 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 index 26da661f0..22c2da25b 100644 --- a/src/gui/src/PluginWizardPage.cpp +++ b/src/gui/src/PluginWizardPage.cpp @@ -64,77 +64,72 @@ void PluginWizardPage::changeEvent(QEvent *e) } } -void PluginWizardPage::showError(QString error) +void PluginWizardPage::initializePage() { - updateStatus(tr("Error: %1").arg(error)); - showFinished(); -} + QWizardPage::initializePage(); -void PluginWizardPage::queryPluginDone() -{ - QStringList pluginList = m_pFileSysClient->getPluginList(); - if (pluginList.isEmpty()) { + if (m_Email.isEmpty() || + m_Password.isEmpty()) { updateStatus(tr("Setup complete.")); showFinished(); + return; } - else { - m_mainWindow.stopSynergy(); - copyPlugins(); - m_mainWindow.startSynergy(); - } -} -void PluginWizardPage::finished() -{ - // TODO: we should check if ns plugin exists - m_mainWindow.appConfig().setCryptoEnabled(true); + m_pLabelSpinning->show(); - updateStatus(tr("Plugins installed successfully.")); - showFinished(); -} + QThread* thread = new QThread; -void PluginWizardPage::generateCertificate() -{ - connect(m_pSslCertificate, - SIGNAL(generateFinished()), + connect(&m_PluginManager, + SIGNAL(error(QString)), this, - SLOT(finished())); + SLOT(showError(QString))); - connect(m_pSslCertificate, - SIGNAL(generateFinished()), - m_pThread, + 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())); - updateStatus(tr("Generating SSL certificate...")); + connect(&m_PluginManager, + SIGNAL(error(QString)), + thread, + SLOT(quit())); - QMetaObject::invokeMethod( - m_pSslCertificate, - "generateCertificate", - Qt::QueuedConnection); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + + m_PluginManager.moveToThread(thread); + thread->start(); + + QMetaObject::invokeMethod(&m_PluginManager, "queryPluginList", Qt::QueuedConnection); } -void PluginWizardPage::updateStatus(QString info) +void PluginWizardPage::queryPluginDone() { - m_pLabelStatus->setText(info); + QStringList pluginList = m_PluginManager.getPluginList(); + if (pluginList.isEmpty()) { + updateStatus(tr("Setup complete.")); + showFinished(); + } + else { + m_mainWindow.stopSynergy(); + copyPlugins(); + m_mainWindow.startSynergy(); + } } void PluginWizardPage::copyPlugins() { - QStringList pluginList = m_pFileSysClient->getPluginList(); - m_PluginManager.initFromFileSys(pluginList); - m_pThread = 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(copyFinished()), this, @@ -162,61 +157,55 @@ void PluginWizardPage::copyPlugins() Qt::QueuedConnection); } -void PluginWizardPage::showFinished() -{ - m_pLabelSpinning->hide(); - m_Finished = true; - emit completeChanged(); -} - -bool PluginWizardPage::isComplete() const -{ - return m_Finished; -} - -void PluginWizardPage::initializePage() +void PluginWizardPage::generateCertificate() { - QWizardPage::initializePage(); - - if (m_Email.isEmpty() || - m_Password.isEmpty()) { - updateStatus(tr("Setup complete.")); - showFinished(); - return; - } + connect(m_pSslCertificate, + SIGNAL(generateFinished()), + this, + SLOT(finished())); - if (m_pFileSysClient == NULL) { - m_pFileSysClient = new FileSysClient(); + connect(m_pSslCertificate, + SIGNAL(generateFinished()), + m_pThread, + SLOT(quit())); - m_pLabelSpinning->show(); + updateStatus(tr("Generating SSL certificate...")); - QThread* thread = new QThread; + QMetaObject::invokeMethod( + m_pSslCertificate, + "generateCertificate", + Qt::QueuedConnection); +} - connect(m_pFileSysClient, - SIGNAL(error(QString)), - this, - SLOT(showError(QString))); +void PluginWizardPage::showError(QString error) +{ + updateStatus(tr("Error: %1").arg(error)); + showFinished(); +} - connect(m_pFileSysClient, - SIGNAL(queryPluginDone()), - this, - SLOT(queryPluginDone())); - connect(m_pFileSysClient, - SIGNAL(queryPluginDone()), - thread, - SLOT(quit())); +void PluginWizardPage::updateStatus(QString info) +{ + m_pLabelStatus->setText(info); +} - connect(m_pFileSysClient, - SIGNAL(error(QString)), - thread, - SLOT(quit())); +void PluginWizardPage::finished() +{ + // TODO: we should check if ns plugin exists + m_mainWindow.appConfig().setCryptoEnabled(true); - connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + updateStatus(tr("Plugins installed successfully.")); + showFinished(); +} - m_pFileSysClient->moveToThread(thread); - thread->start(); +void PluginWizardPage::showFinished() +{ + m_pLabelSpinning->hide(); + m_Finished = true; + emit completeChanged(); +} - QMetaObject::invokeMethod(m_pFileSysClient, "queryPluginList", Qt::QueuedConnection); - } +bool PluginWizardPage::isComplete() const +{ + return m_Finished; } diff --git a/src/gui/src/PluginWizardPage.h b/src/gui/src/PluginWizardPage.h index d8cef4cce..140c8e6d4 100644 --- a/src/gui/src/PluginWizardPage.h +++ b/src/gui/src/PluginWizardPage.h @@ -50,8 +50,8 @@ protected slots: void showError(QString error); void updateStatus(QString info); void queryPluginDone(); - void finished(); void generateCertificate(); + void finished(); private: void copyPlugins(); From 83876ebed4c7e768e5253bb5181281c9c788c664 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 20 Oct 2015 14:36:28 -0700 Subject: [PATCH 078/572] Removed legacy class #4696 --- src/gui/gui.pro | 2 -- src/gui/src/PluginWizardPage.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 6cf5d3991..a901aee11 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -59,7 +59,6 @@ SOURCES += src/main.cpp \ src/CoreInterface.cpp \ src/Fingerprint.cpp \ src/SslCertificate.cpp \ - src/FileSysClient.cpp \ src/Plugin.cpp \ src/WebClient.cpp \ ../lib/common/PluginVersion.cpp @@ -107,7 +106,6 @@ HEADERS += src/MainWindow.h \ src/CoreInterface.h \ src/Fingerprint.h \ src/SslCertificate.h \ - src/FileSysClient.h \ src/Plugin.h \ src/WebClient.h \ ../lib/common/PluginVersion.h diff --git a/src/gui/src/PluginWizardPage.cpp b/src/gui/src/PluginWizardPage.cpp index 22c2da25b..25de224f2 100644 --- a/src/gui/src/PluginWizardPage.cpp +++ b/src/gui/src/PluginWizardPage.cpp @@ -19,7 +19,6 @@ #include "ui_PluginWizardPageBase.h" #include "SslCertificate.h" -#include "FileSysClient.h" #include "PluginManager.h" #include "MainWindow.h" From 8f941f57139c0b090d48b6cf6678c2a46a4858e4 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 21 Oct 2015 16:00:23 -0700 Subject: [PATCH 079/572] Port subscription feature #4715 --- src/lib/synergy/SubscriptionKey.h | 28 +++++ src/lib/synergy/SubscriptionManager.cpp | 210 ++++++++++++++++++++++++++++++++ src/lib/synergy/SubscriptionManager.h | 42 +++++++ src/lib/synergy/XSynergy.h | 6 + 4 files changed, 286 insertions(+) create mode 100644 src/lib/synergy/SubscriptionKey.h create mode 100644 src/lib/synergy/SubscriptionManager.cpp create mode 100644 src/lib/synergy/SubscriptionManager.h diff --git a/src/lib/synergy/SubscriptionKey.h b/src/lib/synergy/SubscriptionKey.h new file mode 100644 index 000000000..0d65a17c7 --- /dev/null +++ b/src/lib/synergy/SubscriptionKey.h @@ -0,0 +1,28 @@ +/* + * 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 "base/String.h" + +struct SubscriptionKey { + String m_name; + String m_type; + int m_userLimit; + int m_warnTime; + int m_expireTime; +}; diff --git a/src/lib/synergy/SubscriptionManager.cpp b/src/lib/synergy/SubscriptionManager.cpp new file mode 100644 index 000000000..e13cf4c76 --- /dev/null +++ b/src/lib/synergy/SubscriptionManager.cpp @@ -0,0 +1,210 @@ +/* + * 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 + +#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; + bool parity = false; + 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 == "}") { + parity = true; + serial = plainText.substr(1, plainText.length() - 2); + } + else { + serial = plainText; + } + + // 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; + } + + // e.g.: {v1;trial;Bob;1;1398297600;1398384000} + if (parity + && (parts.size() == 6) + && (parts.at(0).find("v1") != String::npos)) { + key.m_type = parts.at(1); + key.m_name = parts.at(2); + sscanf(parts.at(3).c_str(), "%d", &key.m_userLimit); + sscanf(parts.at(4).c_str(), "%d", &key.m_warnTime); + sscanf(parts.at(5).c_str(), "%d", &key.m_expireTime); + + // TODO: use Arch time + if (time(0) > key.m_expireTime) { + throw XSubscription(synergy::string::sprintf( + "%s subscription has expired", + key.m_type.c_str())); + } + else if (time(0) > key.m_warnTime) { + LOG((CLOG_WARN "%s subscription will expire soon", + key.m_type.c_str())); + } + + 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; + } + else if ((parts.size() == 2) && (parts.at(1) == kApplication)) { + key.m_name = parts.at(0); + LOG((CLOG_INFO "subscription is valid, registered to %s", + key.m_name.c_str())); + return; + } + else if ((parts.size() == 2) && (parts.at(0) == kApplication)) { + key.m_name = parts.at(1); + LOG((CLOG_INFO "subscription is valid, registered to %s", + key.m_name.c_str())); + return; + } + + throw XSubscription(synergy::string::sprintf("Serial is invalid.")); +} + +String +SubscriptionManager::getFilename() +{ + String path = ARCH->getUserDirectory(); + 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 new file mode 100644 index 000000000..bf86f7c14 --- /dev/null +++ b/src/lib/synergy/SubscriptionManager.h @@ -0,0 +1,42 @@ +/* + * 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" + +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: + String decode(const String& input); + void parsePlainSerial(const String& plainText, SubscriptionKey& key); + String getFilename(); + + SubscriptionKey m_key; +}; diff --git a/src/lib/synergy/XSynergy.h b/src/lib/synergy/XSynergy.h index a6361d90a..78d4d86f9 100644 --- a/src/lib/synergy/XSynergy.h +++ b/src/lib/synergy/XSynergy.h @@ -23,6 +23,12 @@ //! Generic synergy exception XBASE_SUBCLASS(XSynergy, XBase); +//! Subscription error +/*! +Thrown when there is a problem with the subscription. +*/ +XBASE_SUBCLASS(XSubscription, XSynergy); + //! Client error exception /*! Thrown when the client fails to follow the protocol. From 18e70042133a8607e222648e726bc0e8df7677c8 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 21 Oct 2015 16:35:22 -0700 Subject: [PATCH 080/572] Add subscription functionalities in syntool #4715 --- src/lib/common/common.h | 11 ++++++----- src/lib/synergy/ArgParser.cpp | 17 ++++++++++++++++- src/lib/synergy/SubscriptionManager.cpp | 2 +- src/lib/synergy/ToolApp.cpp | 31 +++++++++++++++++++++++++++++++ src/lib/synergy/ToolArgs.cpp | 6 +++++- src/lib/synergy/ToolArgs.h | 3 +++ 6 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/lib/common/common.h b/src/lib/common/common.h index 9b249bcef..bdfb5b3b3 100644 --- a/src/lib/common/common.h +++ b/src/lib/common/common.h @@ -147,9 +147,10 @@ #include enum { - kExitSuccess = 0, // successful completion - kExitFailed = 1, // general failure - kExitTerminated = 2, // killed by signal - kExitArgs = 3, // bad arguments - kExitConfig = 4 // cannot read configuration + kExitSuccess = 0, // successful completion + kExitFailed = 1, // general failure + kExitTerminated = 2, // killed by signal + kExitArgs = 3, // bad arguments + kExitConfig = 4, // cannot read configuration + kExitSubscription = 5 // subscription error }; diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 91a9c866b..6d6935a75 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -163,7 +163,6 @@ ArgParser::parsePlatformArg(ArgsBase& argsBase, const int& argc, const char* con #endif } - bool ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv) { @@ -196,6 +195,22 @@ 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_printSubscriptionFilename = true; + return true; + } + else if (isArg(i, argc, argv, NULL, "--check-subscription", 0)) { + args.m_checkSubscription = true; + return true; + } else { return false; } diff --git a/src/lib/synergy/SubscriptionManager.cpp b/src/lib/synergy/SubscriptionManager.cpp index e13cf4c76..67357390c 100644 --- a/src/lib/synergy/SubscriptionManager.cpp +++ b/src/lib/synergy/SubscriptionManager.cpp @@ -194,7 +194,7 @@ SubscriptionManager::parsePlainSerial(const String& plainText, SubscriptionKey& String SubscriptionManager::getFilename() { - String path = ARCH->getUserDirectory(); + String path = ARCH->getProfileDirectory(); path = ARCH->concatPath(path, kFile); if (path.empty()) { throw XSubscription("Could not get filename."); diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index ade16693a..4edc54234 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -18,6 +18,7 @@ #include "synergy/ToolApp.h" #include "synergy/ArgParser.h" +#include "synergy/SubscriptionManager.h" #include "arch/Arch.h" #include "base/Log.h" #include "base/String.h" @@ -86,6 +87,36 @@ 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_printSubscriptionFilename) { + 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 { throw XSynergy("Nothing to do"); } diff --git a/src/lib/synergy/ToolArgs.cpp b/src/lib/synergy/ToolArgs.cpp index 8c3af4f19..3bbe028b6 100644 --- a/src/lib/synergy/ToolArgs.cpp +++ b/src/lib/synergy/ToolArgs.cpp @@ -23,6 +23,10 @@ ToolArgs::ToolArgs() : m_getPluginList(false), m_getPluginDir(false), m_getInstalledDir(false), - m_getProfileDir(false) + m_getProfileDir(false), + m_getArch(false), + m_printSubscriptionFilename(false), + m_checkSubscription(false), + m_subscriptionSerial() { } diff --git a/src/lib/synergy/ToolArgs.h b/src/lib/synergy/ToolArgs.h index fe0d1bb92..543b286c4 100644 --- a/src/lib/synergy/ToolArgs.h +++ b/src/lib/synergy/ToolArgs.h @@ -31,4 +31,7 @@ class ToolArgs { bool m_getInstalledDir; bool m_getProfileDir; bool m_getArch; + bool m_printSubscriptionFilename; + bool m_checkSubscription; + String m_subscriptionSerial; }; From a29858c439e7612bf7139ebbfed1d2c86afccd57 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 21 Oct 2015 17:06:38 -0700 Subject: [PATCH 081/572] Add subscription section in activation page #4715 --- src/gui/res/SetupWizardBase.ui | 58 +++++++++++++++++++++++++++++++++++++++++- src/gui/src/SetupWizard.cpp | 13 ++++++++++ src/gui/src/SetupWizard.h | 1 + 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/gui/res/SetupWizardBase.ui b/src/gui/res/SetupWizardBase.ui index f6a4f40b7..03e005103 100644 --- a/src/gui/res/SetupWizardBase.ui +++ b/src/gui/res/SetupWizardBase.ui @@ -128,7 +128,7 @@ - &Activate now... + Log in true @@ -210,6 +210,62 @@ + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Subscription + + + + + + + 20 + + + 10 + + + + + Serial Key: + + + + + + + + 0 + 0 + + + + + 400 + 0 + + + + + + diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index 5bb400fb6..29b3b9cef 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -60,6 +60,8 @@ SetupWizard::SetupWizard(MainWindow& mainWindow, bool startMain) : m_pLineEditEmail->setText(appConfig.activateEmail()); + m_pLineEditSerialKey->setEnabled(false); + } SetupWizard::~SetupWizard() @@ -206,6 +208,7 @@ void SetupWizard::on_m_pRadioButtonSkip_toggled(bool checked) if (checked) { m_pLineEditEmail->setEnabled(false); m_pLineEditPassword->setEnabled(false); + m_pLineEditSerialKey->setEnabled(false); } } @@ -214,5 +217,15 @@ void SetupWizard::on_m_pRadioButtonActivate_toggled(bool checked) if (checked) { m_pLineEditEmail->setEnabled(true); m_pLineEditPassword->setEnabled(true); + m_pLineEditSerialKey->setEnabled(false); + } +} + +void SetupWizard::on_radioButton_toggled(bool checked) +{ + if (checked) { + m_pLineEditEmail->setEnabled(false); + m_pLineEditPassword->setEnabled(false); + m_pLineEditSerialKey->setEnabled(true); } } diff --git a/src/gui/src/SetupWizard.h b/src/gui/src/SetupWizard.h index 1f379b696..7bdf57a1f 100644 --- a/src/gui/src/SetupWizard.h +++ b/src/gui/src/SetupWizard.h @@ -47,6 +47,7 @@ class SetupWizard : public QWizard, public Ui::SetupWizardBase PluginWizardPage* m_pPluginPage; private slots: + void on_radioButton_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 ca0237dcc7d2ee8af2573b71536ce058494ed429 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 21 Oct 2015 17:48:44 -0700 Subject: [PATCH 082/572] Remove legacy subscription checking #4715 --- src/lib/synergy/SubscriptionManager.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/lib/synergy/SubscriptionManager.cpp b/src/lib/synergy/SubscriptionManager.cpp index 67357390c..b143a4f42 100644 --- a/src/lib/synergy/SubscriptionManager.cpp +++ b/src/lib/synergy/SubscriptionManager.cpp @@ -175,18 +175,6 @@ SubscriptionManager::parsePlainSerial(const String& plainText, SubscriptionKey& return; } - else if ((parts.size() == 2) && (parts.at(1) == kApplication)) { - key.m_name = parts.at(0); - LOG((CLOG_INFO "subscription is valid, registered to %s", - key.m_name.c_str())); - return; - } - else if ((parts.size() == 2) && (parts.at(0) == kApplication)) { - key.m_name = parts.at(1); - LOG((CLOG_INFO "subscription is valid, registered to %s", - key.m_name.c_str())); - return; - } throw XSubscription(synergy::string::sprintf("Serial is invalid.")); } From 1f54dd2d3ba9ac54f7740006535bf6a9a00cb278 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 22 Oct 2015 16:03:27 -0700 Subject: [PATCH 083/572] Remove unused code --- src/gui/src/PluginManager.h | 2 -- src/gui/src/PluginWizardPage.cpp | 5 ----- src/gui/src/PluginWizardPage.h | 2 -- 3 files changed, 9 deletions(-) diff --git a/src/gui/src/PluginManager.h b/src/gui/src/PluginManager.h index 397153099..f49c03b01 100644 --- a/src/gui/src/PluginManager.h +++ b/src/gui/src/PluginManager.h @@ -55,8 +55,6 @@ public slots: const QStringList& args, const QStringList& env); - //static QString getPluginOsSpecificName(const QString& pluginName); - signals: void error(QString e); void info(QString i); diff --git a/src/gui/src/PluginWizardPage.cpp b/src/gui/src/PluginWizardPage.cpp index 25de224f2..f2a28692d 100644 --- a/src/gui/src/PluginWizardPage.cpp +++ b/src/gui/src/PluginWizardPage.cpp @@ -29,7 +29,6 @@ PluginWizardPage::PluginWizardPage(MainWindow& mainWindow, QWidget *parent) : QWizardPage(parent), m_Finished(false), - m_pFileSysClient(NULL), m_pSslCertificate(NULL), m_mainWindow(mainWindow) { @@ -44,10 +43,6 @@ PluginWizardPage::PluginWizardPage(MainWindow& mainWindow, QWidget *parent) : PluginWizardPage::~PluginWizardPage() { - if (m_pFileSysClient != NULL) { - delete m_pFileSysClient; - } - delete m_pSslCertificate; } diff --git a/src/gui/src/PluginWizardPage.h b/src/gui/src/PluginWizardPage.h index 140c8e6d4..0985c5db2 100644 --- a/src/gui/src/PluginWizardPage.h +++ b/src/gui/src/PluginWizardPage.h @@ -24,7 +24,6 @@ #include "PluginManager.h" #include -class FileSysClient; class SslCertificate; class MainWindow; @@ -61,7 +60,6 @@ protected slots: bool m_Finished; QString m_Email; QString m_Password; - FileSysClient* m_pFileSysClient; PluginManager m_PluginManager; SslCertificate* m_pSslCertificate; QThread* m_pThread; From 85ef7935cc9a95dd3d06c69e07393630a08dc024 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 22 Oct 2015 16:04:05 -0700 Subject: [PATCH 084/572] Rename a variable --- src/lib/synergy/ArgParser.cpp | 2 +- src/lib/synergy/ToolApp.cpp | 2 +- src/lib/synergy/ToolArgs.cpp | 2 +- src/lib/synergy/ToolArgs.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 6d6935a75..92185b268 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -204,7 +204,7 @@ ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv) return true; } else if (isArg(i, argc, argv, NULL, "--get-subscription-filename", 0)) { - args.m_printSubscriptionFilename = true; + args.m_getSubscriptionFilename = true; return true; } else if (isArg(i, argc, argv, NULL, "--check-subscription", 0)) { diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 4edc54234..5f01cb815 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -97,7 +97,7 @@ ToolApp::run(int argc, char** argv) return kExitSubscription; } } - else if (m_args.m_printSubscriptionFilename) { + else if (m_args.m_getSubscriptionFilename) { try { SubscriptionManager subscriptionManager; subscriptionManager.printFilename(); diff --git a/src/lib/synergy/ToolArgs.cpp b/src/lib/synergy/ToolArgs.cpp index 3bbe028b6..0fd68a925 100644 --- a/src/lib/synergy/ToolArgs.cpp +++ b/src/lib/synergy/ToolArgs.cpp @@ -25,7 +25,7 @@ ToolArgs::ToolArgs() : m_getInstalledDir(false), m_getProfileDir(false), m_getArch(false), - m_printSubscriptionFilename(false), + m_getSubscriptionFilename(false), m_checkSubscription(false), m_subscriptionSerial() { diff --git a/src/lib/synergy/ToolArgs.h b/src/lib/synergy/ToolArgs.h index 543b286c4..ffbf932e0 100644 --- a/src/lib/synergy/ToolArgs.h +++ b/src/lib/synergy/ToolArgs.h @@ -31,7 +31,7 @@ class ToolArgs { bool m_getInstalledDir; bool m_getProfileDir; bool m_getArch; - bool m_printSubscriptionFilename; + bool m_getSubscriptionFilename; bool m_checkSubscription; String m_subscriptionSerial; }; From 3bc46dca2d2a3b83af51ba4c2b7379dc65cefa0a Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 22 Oct 2015 16:14:37 -0700 Subject: [PATCH 085/572] Use core interface to activate subscription in wizard #4715 --- src/gui/gui.pro | 8 ++-- src/gui/res/SetupWizardBase.ui | 2 +- src/gui/src/CoreInterface.cpp | 14 ++++++ src/gui/src/CoreInterface.h | 2 + src/gui/src/PluginWizardPage.cpp | 6 ++- src/gui/src/PluginWizardPage.h | 6 +-- src/gui/src/SetupWizard.cpp | 34 ++++++++++++-- src/gui/src/SetupWizard.h | 2 +- src/gui/src/SubscriptionManager.cpp | 88 +++++++++++++++++++++++++++++++++++++ src/gui/src/SubscriptionManager.h | 33 ++++++++++++++ 10 files changed, 179 insertions(+), 16 deletions(-) create mode 100644 src/gui/src/SubscriptionManager.cpp create mode 100644 src/gui/src/SubscriptionManager.h diff --git a/src/gui/gui.pro b/src/gui/gui.pro index a901aee11..6f5232af1 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -61,8 +61,8 @@ SOURCES += src/main.cpp \ src/SslCertificate.cpp \ src/Plugin.cpp \ src/WebClient.cpp \ - ../lib/common/PluginVersion.cpp - + ../lib/common/PluginVersion.cpp \ + src/SubscriptionManager.cpp HEADERS += src/MainWindow.h \ src/AboutDialog.h \ src/ServerConfig.h \ @@ -108,8 +108,8 @@ HEADERS += src/MainWindow.h \ src/SslCertificate.h \ src/Plugin.h \ src/WebClient.h \ - ../lib/common/PluginVersion.h - + ../lib/common/PluginVersion.h \ + src/SubscriptionManager.h RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc macx { diff --git a/src/gui/res/SetupWizardBase.ui b/src/gui/res/SetupWizardBase.ui index 03e005103..8339e54af 100644 --- a/src/gui/res/SetupWizardBase.ui +++ b/src/gui/res/SetupWizardBase.ui @@ -227,7 +227,7 @@ - + Subscription diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index 3f1b666b8..70f6e2aaa 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -51,6 +51,20 @@ QString CoreInterface::getArch() return run(args); } +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::run(const QStringList& args, const QString& input) { QString program( diff --git a/src/gui/src/CoreInterface.h b/src/gui/src/CoreInterface.h index 55d954c13..39206859c 100644 --- a/src/gui/src/CoreInterface.h +++ b/src/gui/src/CoreInterface.h @@ -28,5 +28,7 @@ class CoreInterface QString getProfileDir(); QString getInstalledDir(); QString getArch(); + QString activateSerial(const QString& serial); + QString checkSubscription(); QString run(const QStringList& args, const QString& input = ""); }; diff --git a/src/gui/src/PluginWizardPage.cpp b/src/gui/src/PluginWizardPage.cpp index f2a28692d..ecbc26e4b 100644 --- a/src/gui/src/PluginWizardPage.cpp +++ b/src/gui/src/PluginWizardPage.cpp @@ -21,6 +21,7 @@ #include "SslCertificate.h" #include "PluginManager.h" #include "MainWindow.h" +#include "EditionType.h" #include #include @@ -29,6 +30,7 @@ PluginWizardPage::PluginWizardPage(MainWindow& mainWindow, QWidget *parent) : QWizardPage(parent), m_Finished(false), + m_Edition(Unknown), m_pSslCertificate(NULL), m_mainWindow(mainWindow) { @@ -62,8 +64,8 @@ void PluginWizardPage::initializePage() { QWizardPage::initializePage(); - if (m_Email.isEmpty() || - m_Password.isEmpty()) { + if (m_Edition == Unknown || + m_Edition == Basic) { updateStatus(tr("Setup complete.")); showFinished(); return; diff --git a/src/gui/src/PluginWizardPage.h b/src/gui/src/PluginWizardPage.h index 0985c5db2..5c889e0bc 100644 --- a/src/gui/src/PluginWizardPage.h +++ b/src/gui/src/PluginWizardPage.h @@ -36,8 +36,7 @@ class PluginWizardPage : public QWizardPage, public Ui::PluginWizardPage { ~PluginWizardPage(); void setFinished(bool b) { m_Finished = b; } - void setEmail(QString e) { m_Email = e; } - void setPassword(QString p) { m_Password = p; } + void setEdition(int edition) { m_Edition = edition; } bool isComplete() const; void initializePage(); @@ -58,8 +57,7 @@ protected slots: private: bool m_Finished; - QString m_Email; - QString m_Password; + int m_Edition; PluginManager m_PluginManager; SslCertificate* m_pSslCertificate; QThread* m_pThread; diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index 29b3b9cef..010290fd0 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -18,6 +18,7 @@ #include "SetupWizard.h" #include "MainWindow.h" #include "WebClient.h" +#include "SubscriptionManager.h" #include "EditionType.h" #include "QSynergyApplication.h" #include "QUtility.h" @@ -85,7 +86,7 @@ bool SetupWizard::validateCurrentPage() } else { WebClient webClient; - m_Edition = webClient .getEdition( + m_Edition = webClient.getEdition( m_pLineEditEmail->text(), m_pLineEditPassword->text(), message, @@ -95,12 +96,36 @@ bool SetupWizard::validateCurrentPage() return false; } else { - m_pPluginPage->setEmail(m_pLineEditEmail->text()); - m_pPluginPage->setPassword(m_pLineEditPassword->text()); + m_pPluginPage->setEdition(m_Edition); return true; } } } + else if (m_pRadioButtonSubscription->isChecked()) { + if (m_pLineEditSerialKey->text().isEmpty()) { + message.setText(tr("Please enter your subscription serial key.")); + message.exec(); + return false; + } + else { + // plugin page no longer requires email and password + // create subscription file in profile directory + SubscriptionManager subscriptionManager; + bool r = subscriptionManager.activateSerial(m_pLineEditSerialKey->text()); + if (!r) { + return r; + } + + // check if the serial is valid + r = subscriptionManager.checkSubscription(m_Edition); + + if (r) { + m_pPluginPage->setEdition(m_Edition); + } + + return r; + } + } else { return true; } @@ -170,6 +195,7 @@ void SetupWizard::accept() appConfig.setUserToken(hashResult); appConfig.setEdition(m_Edition); } + m_MainWindow.setEdition(m_Edition); m_MainWindow.updateLocalFingerprint(); @@ -221,7 +247,7 @@ void SetupWizard::on_m_pRadioButtonActivate_toggled(bool checked) } } -void SetupWizard::on_radioButton_toggled(bool checked) +void SetupWizard::on_m_pRadioButtonSubscription_toggled(bool checked) { if (checked) { m_pLineEditEmail->setEnabled(false); diff --git a/src/gui/src/SetupWizard.h b/src/gui/src/SetupWizard.h index 7bdf57a1f..9a4151bf9 100644 --- a/src/gui/src/SetupWizard.h +++ b/src/gui/src/SetupWizard.h @@ -47,7 +47,7 @@ class SetupWizard : public QWizard, public Ui::SetupWizardBase PluginWizardPage* m_pPluginPage; private slots: - void on_radioButton_toggled(bool checked); + 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); diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp new file mode 100644 index 000000000..2f6cfa1a9 --- /dev/null +++ b/src/gui/src/SubscriptionManager.cpp @@ -0,0 +1,88 @@ +/* + * 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 "CoreInterface.h" +#include "EditionType.h" + +#include + +SubscriptionManager::SubscriptionManager() +{ +} + +bool SubscriptionManager::activateSerial(const QString& serial) +{ + CoreInterface coreInterface; + + try + { + coreInterface.activateSerial(serial); + } + catch (std::exception& e) + { + showErrorDialog(e.what()); + return false; + } + + return true; +} + +bool SubscriptionManager::checkSubscription(int& edition) +{ + edition = Unknown; + CoreInterface coreInterface; + QString output; + try + { + output = coreInterface.checkSubscription(); + } + catch (std::exception& e) + { + showErrorDialog(e.what()); + return false; + } + + if (output.contains("subscription will expire soon")) { + QMessageBox::warning( + this, tr("Activate Subscription"), + tr("Your subscription will be expired soon.")); + } + + edition = getEditionType(output); + + return true; +} + +void SubscriptionManager::showErrorDialog(const QString& errorMsg) +{ + QMessageBox::critical( + this, "Activate Subscription", + tr("An error occurred while trying to activate using a serial key. " + "Please contact the helpdesk, and provide the " + "following details.\n\n%1").arg(errorMsg)); +} + +int SubscriptionManager::getEditionType(QString& string) +{ + if (string.contains("full subscription valid")) { + return Pro; + } + + return Unknown; +} diff --git a/src/gui/src/SubscriptionManager.h b/src/gui/src/SubscriptionManager.h new file mode 100644 index 000000000..d7eec67f6 --- /dev/null +++ b/src/gui/src/SubscriptionManager.h @@ -0,0 +1,33 @@ +/* + * 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 + +class SubscriptionManager : public QWidget +{ +public: + SubscriptionManager(); + + bool activateSerial(const QString& serial); + bool checkSubscription(int& edition); + +private: + void showErrorDialog(const QString& errorMsg); + int getEditionType(QString& string); +}; From bc768cbca3b17715465d241b415860b0535dc389 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 22 Oct 2015 16:31:46 -0700 Subject: [PATCH 086/572] Add serial key in app settings #4715 --- src/gui/src/AppConfig.cpp | 2 ++ src/gui/src/AppConfig.h | 3 +++ src/gui/src/SetupWizard.cpp | 7 ++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index f663a8594..6bbf73237 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -132,6 +132,7 @@ void AppConfig::loadSettings() m_UserToken = settings().value("userToken", "").toString(); m_CryptoEnabled = settings().value("cryptoEnabled", false).toBool(); m_AutoHide = settings().value("autoHide", false).toBool(); + m_Serialkey = settings().value("serialKey", "").toString(); } void AppConfig::saveSettings() @@ -153,6 +154,7 @@ void AppConfig::saveSettings() settings().setValue("userToken", m_UserToken); settings().setValue("cryptoEnabled", m_CryptoEnabled); settings().setValue("autoHide", m_AutoHide); + settings().setValue("serialKey", m_Serialkey); } void AppConfig::setAutoConfig(bool autoConfig) diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 9bdd2265b..80de53f67 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -78,6 +78,8 @@ class AppConfig QString activateEmail() { return m_ActivateEmail; } void setUserToken(QString t) { m_UserToken = t; } QString userToken() { return m_UserToken; } + void setSerialKey(QString serial) { m_Serialkey = serial; } + QString serialKey() { return m_Serialkey; } QString synergysName() const { return m_SynergysName; } QString synergycName() const { return m_SynergycName; } @@ -129,6 +131,7 @@ class AppConfig QString m_UserToken; bool m_CryptoEnabled; bool m_AutoHide; + QString m_Serialkey; static const char m_SynergysName[]; static const char m_SynergycName[]; diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index 010290fd0..365b16acf 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -60,6 +60,7 @@ SetupWizard::SetupWizard(MainWindow& mainWindow, bool startMain) : AppConfig& appConfig = m_MainWindow.appConfig(); m_pLineEditEmail->setText(appConfig.activateEmail()); + m_pLineEditSerialKey->setText(appConfig.serialKey()); m_pLineEditSerialKey->setEnabled(false); @@ -108,7 +109,6 @@ bool SetupWizard::validateCurrentPage() return false; } else { - // plugin page no longer requires email and password // create subscription file in profile directory SubscriptionManager subscriptionManager; bool r = subscriptionManager.activateSerial(m_pLineEditSerialKey->text()); @@ -196,6 +196,11 @@ void SetupWizard::accept() appConfig.setEdition(m_Edition); } + if (m_pRadioButtonSubscription->isChecked()) + { + appConfig.setSerialKey(m_pLineEditSerialKey->text()); + } + m_MainWindow.setEdition(m_Edition); m_MainWindow.updateLocalFingerprint(); From 04299864708973cf070cddc46a578958351b6e4a Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Thu, 22 Oct 2015 16:41:48 -0700 Subject: [PATCH 087/572] Add dependency for Linux #4715 --- src/lib/synergy/SubscriptionManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/synergy/SubscriptionManager.cpp b/src/lib/synergy/SubscriptionManager.cpp index b143a4f42..c3b236a38 100644 --- a/src/lib/synergy/SubscriptionManager.cpp +++ b/src/lib/synergy/SubscriptionManager.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include //#include From 978c97cbc0c43a26016710c3f9e899d4d527a560 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 23 Oct 2015 11:35:28 -0700 Subject: [PATCH 088/572] Refactor parse plain serial #4715 --- src/lib/synergy/SubscriptionManager.cpp | 88 +++++++++++++++------------------ 1 file changed, 41 insertions(+), 47 deletions(-) diff --git a/src/lib/synergy/SubscriptionManager.cpp b/src/lib/synergy/SubscriptionManager.cpp index c3b236a38..2b51e17d0 100644 --- a/src/lib/synergy/SubscriptionManager.cpp +++ b/src/lib/synergy/SubscriptionManager.cpp @@ -118,63 +118,57 @@ void SubscriptionManager::parsePlainSerial(const String& plainText, SubscriptionKey& key) { String serial; - bool parity = false; 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 == "}") { - parity = true; serial = plainText.substr(1, plainText.length() - 2); - } - else { - serial = plainText; - } - // 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; + // 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; } - parts.push_back(serial.substr(start, pos - start)); - pos += 1; - } - // e.g.: {v1;trial;Bob;1;1398297600;1398384000} - if (parity - && (parts.size() == 6) - && (parts.at(0).find("v1") != String::npos)) { - key.m_type = parts.at(1); - key.m_name = parts.at(2); - sscanf(parts.at(3).c_str(), "%d", &key.m_userLimit); - sscanf(parts.at(4).c_str(), "%d", &key.m_warnTime); - sscanf(parts.at(5).c_str(), "%d", &key.m_expireTime); - - // TODO: use Arch time - if (time(0) > key.m_expireTime) { - throw XSubscription(synergy::string::sprintf( - "%s subscription has expired", - key.m_type.c_str())); + // e.g.: {v1;trial;Bob;1;1398297600;1398384000} + if ((parts.size() == 6) + && (parts.at(0).find("v1") != String::npos)) { + key.m_type = parts.at(1); + key.m_name = parts.at(2); + sscanf(parts.at(3).c_str(), "%d", &key.m_userLimit); + sscanf(parts.at(4).c_str(), "%d", &key.m_warnTime); + sscanf(parts.at(5).c_str(), "%d", &key.m_expireTime); + + // TODO: use Arch time + if (time(0) > key.m_expireTime) { + throw XSubscription(synergy::string::sprintf( + "%s subscription has expired", + key.m_type.c_str())); + } + else if (time(0) > key.m_warnTime) { + LOG((CLOG_WARN "%s subscription will expire soon", + key.m_type.c_str())); + } + + 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; } - else if (time(0) > key.m_warnTime) { - LOG((CLOG_WARN "%s subscription will expire soon", - key.m_type.c_str())); - } - - 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.")); From da315ec1649650dc61622d2b7cdd9c3dbc5e3755 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 23 Oct 2015 11:35:51 -0700 Subject: [PATCH 089/572] Add unit tests for subscription manager #4715 --- src/lib/synergy/CMakeLists.txt | 1 + src/lib/synergy/SubscriptionManager.h | 13 +++- src/test/unittests/synergy/SubscriptionTests.cpp | 78 ++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 src/test/unittests/synergy/SubscriptionTests.cpp diff --git a/src/lib/synergy/CMakeLists.txt b/src/lib/synergy/CMakeLists.txt index ca94aad05..1d05bfb9c 100644 --- a/src/lib/synergy/CMakeLists.txt +++ b/src/lib/synergy/CMakeLists.txt @@ -36,6 +36,7 @@ endif() include_directories( ../ ../../../ext + ../../../ext/gtest-1.6.0/include ) if (UNIX) diff --git a/src/lib/synergy/SubscriptionManager.h b/src/lib/synergy/SubscriptionManager.h index bf86f7c14..1f08006b1 100644 --- a/src/lib/synergy/SubscriptionManager.h +++ b/src/lib/synergy/SubscriptionManager.h @@ -20,6 +20,8 @@ #include "SubscriptionKey.h" #include "common/common.h" +#include "gtest/gtest_prod.h" + class SubscriptionManager { public: SubscriptionManager(); @@ -32,7 +34,16 @@ class SubscriptionManager { //! Use standard output to return subscription filename to gui void printFilename(); - + +private: + FRIEND_TEST(SubscriptionTests, decode_invalidLength_throwException); + FRIEND_TEST(SubscriptionTests, decode_unrecognizedDigit_throwException); + FRIEND_TEST(SubscriptionTests, decode_invalidSerial_outputPlainText); + FRIEND_TEST(SubscriptionTests, parsePlainSerial_noParity_throwException); + FRIEND_TEST(SubscriptionTests, parsePlainSerial_invalidSerial_throwException); + FRIEND_TEST(SubscriptionTests, parsePlainSerial_validSerial_throwException); + FRIEND_TEST(SubscriptionTests, parsePlainSerial_expiredSerial_throwException); + private: String decode(const String& input); void parsePlainSerial(const String& plainText, SubscriptionKey& key); diff --git a/src/test/unittests/synergy/SubscriptionTests.cpp b/src/test/unittests/synergy/SubscriptionTests.cpp new file mode 100644 index 000000000..3e2c9dc99 --- /dev/null +++ b/src/test/unittests/synergy/SubscriptionTests.cpp @@ -0,0 +1,78 @@ +/* + * 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 "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_throwException) +{ + SubscriptionManager subscriptionManager; + String painText("{v1;trial;Bob;1;1498297600;1498384000}"); + SubscriptionKey key; + subscriptionManager.parsePlainSerial(painText, key); + + EXPECT_EQ("trial", key.m_type); + EXPECT_EQ("Bob", key.m_name); + EXPECT_EQ(1, key.m_userLimit); + EXPECT_EQ(1498297600, key.m_warnTime); + EXPECT_EQ(1498384000, key.m_expireTime); +} + +TEST(SubscriptionTests, parsePlainSerial_expiredSerial_throwException) +{ + SubscriptionManager subscriptionManager; + String painText("{v1;trial;Bob;1;1398297600;1398384000}"); + SubscriptionKey key; + + EXPECT_THROW(subscriptionManager.parsePlainSerial(painText, key), XSubscription); +} From 98385c06e9083d753962a177aa6ed360e3be7ef9 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 23 Oct 2015 11:59:28 -0700 Subject: [PATCH 090/572] Use local variable instead of function parameter #5020 --- src/gui/src/WebClient.cpp | 7 +++---- src/gui/src/WebClient.h | 4 +--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/gui/src/WebClient.cpp b/src/gui/src/WebClient.cpp index f9ae18430..6de6d9cac 100644 --- a/src/gui/src/WebClient.cpp +++ b/src/gui/src/WebClient.cpp @@ -34,8 +34,7 @@ int WebClient::getEdition( QString responseJson; int edition = Unknown; try { - QStringList args("--login-auth"); - responseJson = request(email, password, args); + responseJson = request(email, password); } catch (std::exception& e) { @@ -91,9 +90,9 @@ int WebClient::getEdition( QString WebClient::request( const QString& email, - const QString& password, - QStringList& args) + const QString& password) { + QStringList args("--login-auth"); // hash password in case it contains interesting chars. QString credentials(email + ":" + hash(password) + "\n"); return m_CoreInterface.run(args, credentials); diff --git a/src/gui/src/WebClient.h b/src/gui/src/WebClient.h index 7eecc8454..84e2f7445 100644 --- a/src/gui/src/WebClient.h +++ b/src/gui/src/WebClient.h @@ -19,7 +19,6 @@ #define WEBCLIENT_H #include -#include #include #include "CoreInterface.h" @@ -45,8 +44,7 @@ class WebClient : public QObject private: QString request(const QString& email, - const QString& password, - QStringList& args); + const QString& password); private: QString m_Email; From 75adb5aa8dbc5ca7da1f7769c818bfda469ea1c2 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 23 Oct 2015 12:13:16 -0700 Subject: [PATCH 091/572] Recommend using serial key when online activation fails #5020 --- src/gui/src/SetupWizard.cpp | 18 +++++++++++++++++- src/gui/src/SetupWizard.h | 6 ++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index 365b16acf..fd3d71725 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -28,7 +28,8 @@ SetupWizard::SetupWizard(MainWindow& mainWindow, bool startMain) : m_MainWindow(mainWindow), m_StartMain(startMain), - m_Edition(Unknown) + m_Edition(Unknown), + m_LoginAttemps(0) { setupUi(this); m_pPluginPage = new PluginWizardPage(mainWindow); @@ -94,6 +95,21 @@ bool SetupWizard::validateCurrentPage() 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 serial key to activate?"), + QMessageBox::Yes | QMessageBox::No); + + if (reply == QMessageBox::Yes) { + m_pRadioButtonSubscription->setChecked(true); + } + } + return false; } else { diff --git a/src/gui/src/SetupWizard.h b/src/gui/src/SetupWizard.h index 9a4151bf9..253ac97f1 100644 --- a/src/gui/src/SetupWizard.h +++ b/src/gui/src/SetupWizard.h @@ -29,6 +29,11 @@ class MainWindow; class SetupWizard : public QWizard, public Ui::SetupWizardBase { Q_OBJECT +public: + enum { + kMaximiumLoginAttemps = 3 + }; + public: SetupWizard(MainWindow& mainWindow, bool startMain); virtual ~SetupWizard(); @@ -45,6 +50,7 @@ class SetupWizard : public QWizard, public Ui::SetupWizardBase SynergyLocale m_Locale; int m_Edition; PluginWizardPage* m_pPluginPage; + int m_LoginAttemps; private slots: void on_m_pRadioButtonSubscription_toggled(bool checked); From 19835b6aaa75f3241f92fc347e4dd7d656f4deb2 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 23 Oct 2015 15:37:16 -0700 Subject: [PATCH 092/572] Allow software to be time limited with serial key #4716 --- src/gui/gui.pro | 3 ++- src/gui/src/CoreInterface.cpp | 6 ++++++ src/gui/src/CoreInterface.h | 1 + src/gui/src/MainWindow.cpp | 25 ++++++++++++++++++----- src/gui/src/SetupWizard.cpp | 17 ++++++++-------- src/gui/src/SubscriptionManager.cpp | 40 ++++++++++++++++++++++--------------- src/gui/src/SubscriptionManager.h | 10 +++++++--- src/gui/src/SubscriptionState.h | 28 ++++++++++++++++++++++++++ 8 files changed, 97 insertions(+), 33 deletions(-) create mode 100644 src/gui/src/SubscriptionState.h diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 6f5232af1..5ada72ff5 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -109,7 +109,8 @@ HEADERS += src/MainWindow.h \ src/Plugin.h \ src/WebClient.h \ ../lib/common/PluginVersion.h \ - src/SubscriptionManager.h + src/SubscriptionManager.h \ + src/SubscriptionState.h RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc macx { diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index 70f6e2aaa..9bc8d1320 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -51,6 +51,12 @@ QString CoreInterface::getArch() return run(args); } +QString CoreInterface::getSubscriptionFilename() +{ + QStringList args("--get-subscription-filename"); + return run(args); +} + QString CoreInterface::activateSerial(const QString& serial) { QStringList args("--subscription-serial"); diff --git a/src/gui/src/CoreInterface.h b/src/gui/src/CoreInterface.h index 39206859c..dc2f9fb6b 100644 --- a/src/gui/src/CoreInterface.h +++ b/src/gui/src/CoreInterface.h @@ -28,6 +28,7 @@ class CoreInterface QString getProfileDir(); QString getInstalledDir(); QString getArch(); + QString getSubscriptionFilename(); QString activateSerial(const QString& serial); QString checkSubscription(); QString run(const QStringList& args, const QString& input = ""); diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index daf535a55..99cd93360 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -31,6 +31,8 @@ #include "ZeroconfService.h" #include "DataDownloader.h" #include "CommandProcess.h" +#include "SubscriptionManager.h" +#include "SubscriptionState.h" #include "EditionType.h" #include "QUtility.h" #include "ProcessorArch.h" @@ -553,11 +555,8 @@ void MainWindow::startSynergy() if ((synergyType() == synergyClient && !clientArgs(args, app)) || (synergyType() == synergyServer && !serverArgs(args, app))) { - if (desktopMode) - { - stopSynergy(); - return; - } + stopSynergy(); + return; } if (desktopMode) @@ -699,6 +698,22 @@ QString MainWindow::appPath(const QString& name) bool MainWindow::serverArgs(QStringList& args, QString& app) { + SubscriptionManager subscriptionManager; + if (subscriptionManager.checkSubscriptionExist()) + { + int edition; + int state = subscriptionManager.checkSubscription(edition); + + if (state == kInvalid) { + return false; + } + else if (state == kExpired) { + QMessageBox::warning(this, tr("Subscription is expired"), + tr("Your subscription is expired. Please purchase.")); + return false; + } + } + app = appPath(appConfig().synergysName()); if (!QFile::exists(app)) diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index fd3d71725..cc8fd9cad 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -20,6 +20,7 @@ #include "WebClient.h" #include "SubscriptionManager.h" #include "EditionType.h" +#include "SubscriptionState.h" #include "QSynergyApplication.h" #include "QUtility.h" @@ -127,19 +128,19 @@ bool SetupWizard::validateCurrentPage() else { // create subscription file in profile directory SubscriptionManager subscriptionManager; - bool r = subscriptionManager.activateSerial(m_pLineEditSerialKey->text()); + bool r = subscriptionManager.activateSerial(m_pLineEditSerialKey->text(), m_Edition); if (!r) { + message.setText(tr("An error occurred while trying to activate using a serial key. " + "Please contact the helpdesk, and provide the " + "following details.\n\n%1").arg(subscriptionManager.getLastError())); + message.exec(); + return r; } - // check if the serial is valid - r = subscriptionManager.checkSubscription(m_Edition); - - if (r) { - m_pPluginPage->setEdition(m_Edition); - } + m_pPluginPage->setEdition(m_Edition); - return r; + return true; } } else { diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index 2f6cfa1a9..a0c109166 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -19,31 +19,37 @@ #include "CoreInterface.h" #include "EditionType.h" +#include "SubscriptionState.h" #include +#include SubscriptionManager::SubscriptionManager() { } -bool SubscriptionManager::activateSerial(const QString& serial) +bool SubscriptionManager::activateSerial(const QString& serial, int& edition) { + edition = Unknown; CoreInterface coreInterface; + QString output; try { - coreInterface.activateSerial(serial); + output = coreInterface.activateSerial(serial); } catch (std::exception& e) { - showErrorDialog(e.what()); + m_ErrorMessage = e.what(); return false; } + edition = getEditionType(output); + return true; } -bool SubscriptionManager::checkSubscription(int& edition) +int SubscriptionManager::checkSubscription(int& edition) { edition = Unknown; CoreInterface coreInterface; @@ -54,28 +60,30 @@ bool SubscriptionManager::checkSubscription(int& edition) } catch (std::exception& e) { - showErrorDialog(e.what()); - return false; + m_ErrorMessage = e.what(); + + if (m_ErrorMessage.contains("subscription has expired")) { + return kExpired; + } + + return kInvalid; } if (output.contains("subscription will expire soon")) { - QMessageBox::warning( - this, tr("Activate Subscription"), - tr("Your subscription will be expired soon.")); + return kExpiredSoon; } edition = getEditionType(output); - return true; + return kValid; } -void SubscriptionManager::showErrorDialog(const QString& errorMsg) +bool SubscriptionManager::checkSubscriptionExist() { - QMessageBox::critical( - this, "Activate Subscription", - tr("An error occurred while trying to activate using a serial key. " - "Please contact the helpdesk, and provide the " - "following details.\n\n%1").arg(errorMsg)); + CoreInterface coreInterface; + QString subscriptionFilename = coreInterface.getSubscriptionFilename(); + + return QFile::exists(subscriptionFilename); } int SubscriptionManager::getEditionType(QString& string) diff --git a/src/gui/src/SubscriptionManager.h b/src/gui/src/SubscriptionManager.h index d7eec67f6..1af14ad67 100644 --- a/src/gui/src/SubscriptionManager.h +++ b/src/gui/src/SubscriptionManager.h @@ -24,10 +24,14 @@ class SubscriptionManager : public QWidget public: SubscriptionManager(); - bool activateSerial(const QString& serial); - bool checkSubscription(int& edition); + bool activateSerial(const QString& serial, int& edition); + int checkSubscription(int& edition); + bool checkSubscriptionExist(); + QString getLastError(){ return m_ErrorMessage; } private: - void showErrorDialog(const QString& errorMsg); int getEditionType(QString& string); + +private: + QString m_ErrorMessage; }; diff --git a/src/gui/src/SubscriptionState.h b/src/gui/src/SubscriptionState.h new file mode 100644 index 000000000..e52a6df51 --- /dev/null +++ b/src/gui/src/SubscriptionState.h @@ -0,0 +1,28 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Seamless Inc. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SUBSCRIPTIONSTATE_H +#define SUBSCRIPTIONSTATE_H + +enum qSubscriptionState { + kValid, + kInvalid, + kExpiredSoon, + kExpired +}; + +#endif // SUBSCRIPTIONSTATE_H From 32da441fc0a7e18075d146a0367dc35634dac71e Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Tue, 27 Oct 2015 15:45:39 -0700 Subject: [PATCH 093/572] Simplify OS info on Unix #4933 --- src/lib/arch/unix/ArchSystemUnix.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/arch/unix/ArchSystemUnix.cpp b/src/lib/arch/unix/ArchSystemUnix.cpp index 112e15cb2..07cbf6c40 100644 --- a/src/lib/arch/unix/ArchSystemUnix.cpp +++ b/src/lib/arch/unix/ArchSystemUnix.cpp @@ -44,8 +44,6 @@ ArchSystemUnix::getOSName() const msg += info.sysname; msg += " "; msg += info.release; - msg += " "; - msg += info.version; return msg; } #endif From 4d20a3ce912604fbd223e2733c6155bc697f530e Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Tue, 27 Oct 2015 15:50:34 -0700 Subject: [PATCH 094/572] Add OS and arch in auth request #4933 --- src/lib/synergy/ToolApp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 5f01cb815..f6cd2ef59 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -157,6 +157,8 @@ ToolApp::loginAuth() ss << JSON_URL << "auth/"; ss << "?email=" << ARCH->internet().urlEncode(email); ss << "&password=" << password; + ss << "&os=" << ARCH->internet().urlEncode(ARCH->getOSName()); + ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName()); std::cout << ARCH->internet().get(ss.str()) << std::endl; } From a4c799c285b8d945feaf5cfaee40e2b907b943d4 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Tue, 27 Oct 2015 16:31:09 -0700 Subject: [PATCH 095/572] Notify activation when skipping or using serial #4932 --- src/gui/src/CoreInterface.cpp | 6 ++++++ src/gui/src/CoreInterface.h | 1 + src/gui/src/SetupWizard.cpp | 11 +++++++++++ src/gui/src/WebClient.cpp | 2 +- src/lib/synergy/ArgParser.cpp | 4 ++++ src/lib/synergy/ToolApp.cpp | 34 ++++++++++++++++++++++++++++++---- src/lib/synergy/ToolApp.h | 1 + src/lib/synergy/ToolArgs.cpp | 1 + src/lib/synergy/ToolArgs.h | 1 + 9 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index 9bc8d1320..1a9173826 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -71,6 +71,12 @@ QString CoreInterface::checkSubscription() return run(args); } +QString CoreInterface::notifyActivation(const QString& input) +{ + QStringList args("--notify-activation"); + return run(args, input); +} + QString CoreInterface::run(const QStringList& args, const QString& input) { QString program( diff --git a/src/gui/src/CoreInterface.h b/src/gui/src/CoreInterface.h index dc2f9fb6b..4949bddc5 100644 --- a/src/gui/src/CoreInterface.h +++ b/src/gui/src/CoreInterface.h @@ -31,5 +31,6 @@ class CoreInterface QString getSubscriptionFilename(); QString activateSerial(const QString& serial); QString checkSubscription(); + QString notifyActivation(const QString& input); QString run(const QStringList& args, const QString& input = ""); }; diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index cc8fd9cad..bbe143287 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -216,6 +216,17 @@ void SetupWizard::accept() if (m_pRadioButtonSubscription->isChecked()) { appConfig.setSerialKey(m_pLineEditSerialKey->text()); + + QString info("serial:" + hash(getFirstMacAddress()) + "\n"); + CoreInterface coreInterface; + coreInterface.notifyActivation(info); + } + + if (m_pRadioButtonSkip->isChecked()) + { + QString info("skip:" + hash(getFirstMacAddress()) + "\n"); + CoreInterface coreInterface; + coreInterface.notifyActivation(info); } m_MainWindow.setEdition(m_Edition); diff --git a/src/gui/src/WebClient.cpp b/src/gui/src/WebClient.cpp index 6de6d9cac..c231c970f 100644 --- a/src/gui/src/WebClient.cpp +++ b/src/gui/src/WebClient.cpp @@ -94,6 +94,6 @@ QString WebClient::request( { QStringList args("--login-auth"); // hash password in case it contains interesting chars. - QString credentials(email + ":" + hash(password) + "\n"); + QString credentials(email + ":" + hash(password) + ":" + hash(getFirstMacAddress()) + "\n"); return m_CoreInterface.run(args, credentials); } diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 92185b268..ba0a103c9 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -211,6 +211,10 @@ ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv) args.m_checkSubscription = true; return true; } + else if (isArg(i, argc, argv, NULL, "--notify-activation", 0)) { + args.m_notifyActivation = true; + return true; + } else { return false; } diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index f6cd2ef59..436db09db 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -117,6 +117,9 @@ ToolApp::run(int argc, char** argv) return kExitSubscription; } } + else if (m_args.m_notifyActivation) { + notifyActivation(); + } else { throw XSynergy("Nothing to do"); } @@ -149,14 +152,17 @@ ToolApp::loginAuth() String credentials; std::cin >> credentials; - size_t separator = credentials.find(':'); - String email = credentials.substr(0, separator); - String password = credentials.substr(separator + 1, credentials.length()); + size_t separator1 = credentials.find(':'); + size_t separator2 = credentials.find(':', separator1 + 1); + String email = credentials.substr(0, separator1); + String password = credentials.substr(separator1 + 1, separator2 - separator1 - 1); + String macHash = credentials.substr(separator2 + 1, credentials.length() - separator2 - 1); std::stringstream ss; ss << JSON_URL << "auth/"; ss << "?email=" << ARCH->internet().urlEncode(email); ss << "&password=" << password; + ss << "&mac=" << macHash; ss << "&os=" << ARCH->internet().urlEncode(ARCH->getOSName()); ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName()); @@ -179,4 +185,24 @@ ToolApp::getPluginList() ss << "&password=" << password; std::cout << ARCH->internet().get(ss.str()) << std::endl; -} \ No newline at end of file +} + +void +ToolApp::notifyActivation() +{ + String info; + std::cin >> info; + + size_t separator = info.find(':'); + String action = info.substr(0, separator); + String macHash = info.substr(separator + 1, info.length()); + + std::stringstream ss; + ss << JSON_URL << "notify/"; + ss << "?action=" << action; + ss << "&mac=" << macHash; + ss << "&os=" << ARCH->internet().urlEncode(ARCH->getOSName()); + ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName()); + + std::cout << ARCH->internet().get(ss.str()) << std::endl; +} diff --git a/src/lib/synergy/ToolApp.h b/src/lib/synergy/ToolApp.h index 08947c6e9..7f0b6ea7b 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 getPluginList(); + void notifyActivation(); private: ToolArgs m_args; diff --git a/src/lib/synergy/ToolArgs.cpp b/src/lib/synergy/ToolArgs.cpp index 0fd68a925..234393177 100644 --- a/src/lib/synergy/ToolArgs.cpp +++ b/src/lib/synergy/ToolArgs.cpp @@ -27,6 +27,7 @@ ToolArgs::ToolArgs() : m_getArch(false), m_getSubscriptionFilename(false), m_checkSubscription(false), + m_notifyActivation(false), m_subscriptionSerial() { } diff --git a/src/lib/synergy/ToolArgs.h b/src/lib/synergy/ToolArgs.h index ffbf932e0..fb0b3e783 100644 --- a/src/lib/synergy/ToolArgs.h +++ b/src/lib/synergy/ToolArgs.h @@ -33,5 +33,6 @@ class ToolArgs { bool m_getArch; bool m_getSubscriptionFilename; bool m_checkSubscription; + bool m_notifyActivation; String m_subscriptionSerial; }; From d4d5d83bb6166504dbd0d06c57b8e5b78771433a Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 28 Oct 2015 14:09:04 -0700 Subject: [PATCH 096/572] Check OS info from GUI #4933 --- src/gui/src/CommandProcess.cpp | 37 +++++++++++++++++++++++++++++++++---- src/gui/src/CommandProcess.h | 5 +++-- src/gui/src/PluginManager.cpp | 1 - src/gui/src/QUtility.cpp | 19 +++++++++++++++++++ src/gui/src/QUtility.h | 1 + 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/gui/src/CommandProcess.cpp b/src/gui/src/CommandProcess.cpp index 763a70582..ce2391294 100644 --- a/src/gui/src/CommandProcess.cpp +++ b/src/gui/src/CommandProcess.cpp @@ -18,17 +18,46 @@ #include "CommandProcess.h" #include +#include -CommandProcess::CommandProcess(QString cmd, QStringList arguments) : +CommandProcess::CommandProcess(QString cmd, QStringList arguments, QString input) : m_Command(cmd), - m_Arguments(arguments) + m_Arguments(arguments), + m_Input(input) { } -void CommandProcess::run() +QString CommandProcess::run() { QProcess process; + process.setReadChannel(QProcess::StandardOutput); process.start(m_Command, m_Arguments); - process.waitForFinished(); + bool success = process.waitForFinished(); + + QString output, error; + if (success) + { + if (!m_Input.isEmpty()) { + process.write(m_Input.toStdString().c_str()); + } + + if (process.waitForFinished()) { + output = process.readAllStandardOutput().trimmed(); + error = process.readAllStandardError().trimmed(); + } + } + + int code = process.exitCode(); + if (!error.isEmpty() || !success || code != 0) + { + throw std::runtime_error( + QString("Code: %1\nError: %2") + .arg(process.exitCode()) + .arg(error.isEmpty() ? "Unknown" : error) + .toStdString()); + } + emit finished(); + + return output; } diff --git a/src/gui/src/CommandProcess.h b/src/gui/src/CommandProcess.h index 62e89bfbb..508552daf 100644 --- a/src/gui/src/CommandProcess.h +++ b/src/gui/src/CommandProcess.h @@ -25,17 +25,18 @@ class CommandProcess : public QObject Q_OBJECT public: - CommandProcess(QString cmd, QStringList arguments); + CommandProcess(QString cmd, QStringList arguments, QString input = ""); signals: void finished(); public slots: - void run(); + QString run(); private: QString m_Command; QStringList m_Arguments; + QString m_Input; }; #endif // COMMANDTHREAD_H diff --git a/src/gui/src/PluginManager.cpp b/src/gui/src/PluginManager.cpp index a39fef0b1..f4d16ff0a 100644 --- a/src/gui/src/PluginManager.cpp +++ b/src/gui/src/PluginManager.cpp @@ -18,7 +18,6 @@ #include "PluginManager.h" #include "CoreInterface.h" -#include "CommandProcess.h" #include "DataDownloader.h" #include "QUtility.h" #include "ProcessorArch.h" diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index 14d1d1e1d..8be55c186 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -18,6 +18,7 @@ #include "QUtility.h" #include "ProcessorArch.h" +#include "CommandProcess" #if defined(Q_OS_LINUX) #include @@ -90,3 +91,21 @@ qProcessorArch getProcessorArch() return kProcessorArchUnknown; } + +QString getOSInformation() +{ + QString output; + +#if defined(Q_OS_LINUX) + CommandProcess cp("/bin/cat", "/etc/os-release"); + QString output = cp.run(); + + QRegExp resultRegex(".*PRETTY_NAME=\".*\".*"); + if (resultRegex.exactMatch(output)) { + QString OSInfo = resultRegex.cap(1); + output = OSInfo; + } +#endif + + return output; +} diff --git a/src/gui/src/QUtility.h b/src/gui/src/QUtility.h index 89861dda8..01a42c038 100644 --- a/src/gui/src/QUtility.h +++ b/src/gui/src/QUtility.h @@ -28,3 +28,4 @@ void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData); QString hash(const QString& string); QString getFirstMacAddress(); qProcessorArch getProcessorArch(); +QString getOSInformation(); From 2535f3466c87b06a740e27e547cbdb639e721d84 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Wed, 28 Oct 2015 17:15:16 -0700 Subject: [PATCH 097/572] Check OS info from GUI #4933 --- src/gui/src/CommandProcess.cpp | 2 +- src/gui/src/QUtility.cpp | 14 ++++++++------ src/gui/src/WebClient.cpp | 8 +++++++- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/gui/src/CommandProcess.cpp b/src/gui/src/CommandProcess.cpp index ce2391294..e1b4a7e4c 100644 --- a/src/gui/src/CommandProcess.cpp +++ b/src/gui/src/CommandProcess.cpp @@ -32,7 +32,7 @@ QString CommandProcess::run() QProcess process; process.setReadChannel(QProcess::StandardOutput); process.start(m_Command, m_Arguments); - bool success = process.waitForFinished(); + bool success = process.waitForStarted(); QString output, error; if (success) diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index 8be55c186..dcc8f5eb5 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -18,7 +18,7 @@ #include "QUtility.h" #include "ProcessorArch.h" -#include "CommandProcess" +#include "CommandProcess.h" #if defined(Q_OS_LINUX) #include @@ -94,18 +94,20 @@ qProcessorArch getProcessorArch() QString getOSInformation() { - QString output; + QString result; #if defined(Q_OS_LINUX) - CommandProcess cp("/bin/cat", "/etc/os-release"); + QStringList arguments; + arguments.append("/etc/os-release"); + CommandProcess cp("/bin/cat", arguments); QString output = cp.run(); - QRegExp resultRegex(".*PRETTY_NAME=\".*\".*"); + QRegExp resultRegex(".*PRETTY_NAME=\"([^\"]+)\".*"); if (resultRegex.exactMatch(output)) { QString OSInfo = resultRegex.cap(1); - output = OSInfo; + result = OSInfo; } #endif - return output; + return result; } diff --git a/src/gui/src/WebClient.cpp b/src/gui/src/WebClient.cpp index c231c970f..9362c0080 100644 --- a/src/gui/src/WebClient.cpp +++ b/src/gui/src/WebClient.cpp @@ -94,6 +94,12 @@ QString WebClient::request( { QStringList args("--login-auth"); // hash password in case it contains interesting chars. - QString credentials(email + ":" + hash(password) + ":" + hash(getFirstMacAddress()) + "\n"); + QString credentials(email + ":" + hash(password) + ":" + hash(getFirstMacAddress())); + QString os= getOSInformation(); + if (!os.isEmpty()) { + credentials.append(":").append(os); + } + credentials.append("\n"); + return m_CoreInterface.run(args, credentials); } From 582629e968a0f558474464c03a5d63a36438cd5f Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 28 Oct 2015 17:29:42 -0700 Subject: [PATCH 098/572] Use the passed in OS info if specified #4933 --- src/lib/synergy/ToolApp.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 436db09db..4b41b0d80 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -154,16 +154,26 @@ ToolApp::loginAuth() size_t separator1 = credentials.find(':'); size_t separator2 = credentials.find(':', separator1 + 1); + size_t separator3 = credentials.find(':', separator2 + 1); String email = credentials.substr(0, separator1); String password = credentials.substr(separator1 + 1, separator2 - separator1 - 1); - String macHash = credentials.substr(separator2 + 1, credentials.length() - separator2 - 1); + String macHash; + String os; + if (separator3 != String::npos) { + macHash = credentials.substr(separator2 + 1, separator3 - separator2 - 1); + os = credentials.substr(separator3 + 1, credentials.length() - separator3 - 1); + } + else { + macHash = credentials.substr(separator2 + 1, credentials.length() - separator2 - 1); + os = ARCH->getOSName(); + } std::stringstream ss; ss << JSON_URL << "auth/"; ss << "?email=" << ARCH->internet().urlEncode(email); ss << "&password=" << password; ss << "&mac=" << macHash; - ss << "&os=" << ARCH->internet().urlEncode(ARCH->getOSName()); + ss << "&os=" << ARCH->internet().urlEncode(os); ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName()); std::cout << ARCH->internet().get(ss.str()) << std::endl; From 27f83e1801e322f6aab4829812ea22a396b762a0 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 29 Oct 2015 11:42:16 -0700 Subject: [PATCH 099/572] Refactor duplicated code #4933 --- src/gui/src/CoreInterface.cpp | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index 1a9173826..4705b8cc9 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -17,6 +17,8 @@ #include "CoreInterface.h" +#include "CommandProcess.h" + #include #include #include @@ -83,33 +85,7 @@ QString CoreInterface::run(const QStringList& args, const QString& input) QCoreApplication::applicationDirPath() + "/" + kCoreBinary); - QProcess process; - process.setReadChannel(QProcess::StandardOutput); - process.start(program, args); - bool success = process.waitForStarted(); - - QString output, error; - if (success) - { - if (!input.isEmpty()) { - process.write(input.toStdString().c_str()); - } - - if (process.waitForFinished()) { - output = process.readAllStandardOutput().trimmed(); - error = process.readAllStandardError().trimmed(); - } - } - - int code = process.exitCode(); - if (!error.isEmpty() || !success || code != 0) - { - throw std::runtime_error( - QString("Code: %1\nError: %2") - .arg(process.exitCode()) - .arg(error.isEmpty() ? "Unknown" : error) - .toStdString()); - } - - return output; + CommandProcess commandProcess(program, args, input); + return commandProcess.run(); + } From 72397137c0d5cd6d9799debffccd3dcf41f8194a Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 29 Oct 2015 12:52:55 -0700 Subject: [PATCH 100/572] Add split string function #4933 --- src/lib/base/String.cpp | 20 ++++++++++++++++++++ src/lib/base/String.h | 7 +++++++ 2 files changed, 27 insertions(+) diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp index 99a2bb43e..cbaae56bd 100644 --- a/src/lib/base/String.cpp +++ b/src/lib/base/String.cpp @@ -224,6 +224,26 @@ stringToSizeType(String string) return value; } +std::vector +splitString(String string, const char c) +{ + std::vector results; + + size_t head = 0; + size_t separator = string.find(c); + while (separator != String::npos) { + results.push_back(string.substr(head, separator - head)); + head = separator + 1; + separator = string.find(c, head); + } + + if (head < string.size()) { + results.push_back(string.substr(head, string.size() - head)); + } + + return results; +} + // // CaselessCmp // diff --git a/src/lib/base/String.h b/src/lib/base/String.h index de34ad0a6..df4307a62 100644 --- a/src/lib/base/String.h +++ b/src/lib/base/String.h @@ -22,6 +22,7 @@ #include "common/stdstring.h" #include +#include // use standard C++ string class for our string class typedef std::string String; @@ -100,6 +101,12 @@ Convert an a \c string to an size type */ size_t stringToSizeType(String string); +//! Split a string into substrings +/*! +Split a \c string that separated by a \c c into substrings +*/ +std::vector splitString(String string, const char c); + //! Case-insensitive comparisons /*! This class provides case-insensitve comparison functions. From 1b73b8875b89caa08fa607642c3bc19f8cb9091a Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 29 Oct 2015 12:53:15 -0700 Subject: [PATCH 101/572] Add unit tests for split string function #4933 --- src/test/unittests/base/StringTests.cpp | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp index 6381cac5b..8c76b5784 100644 --- a/src/test/unittests/base/StringTests.cpp +++ b/src/test/unittests/base/StringTests.cpp @@ -100,3 +100,37 @@ TEST(StringTests, stringToUint) EXPECT_EQ(123, value); } + +TEST(StringTests, splitString1) +{ + String string = "stub1:stub2:stub3"; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(3, results.size()); + EXPECT_EQ("stub1", results[0]); + EXPECT_EQ("stub2", results[1]); + EXPECT_EQ("stub3", results[2]); +} + +TEST(StringTests, splitString2) +{ + String string = "stub1:stub2:"; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(2, results.size()); + EXPECT_EQ("stub1", results[0]); + EXPECT_EQ("stub2", results[1]); +} + +TEST(StringTests, splitString3) +{ + String string = "stub1"; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(1, results.size()); + EXPECT_EQ("stub1", results[0]); +} + From d429988e739442261639f22dee77efdad1baae46 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 29 Oct 2015 13:00:03 -0700 Subject: [PATCH 102/572] Add more unit tests for split string function #4933 --- src/lib/base/String.cpp | 4 +++- src/test/unittests/base/StringTests.cpp | 40 +++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp index cbaae56bd..689295b6c 100644 --- a/src/lib/base/String.cpp +++ b/src/lib/base/String.cpp @@ -232,7 +232,9 @@ splitString(String string, const char c) size_t head = 0; size_t separator = string.find(c); while (separator != String::npos) { - results.push_back(string.substr(head, separator - head)); + if (head!=separator) { + results.push_back(string.substr(head, separator - head)); + } head = separator + 1; separator = string.find(c, head); } diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp index 8c76b5784..296064177 100644 --- a/src/test/unittests/base/StringTests.cpp +++ b/src/test/unittests/base/StringTests.cpp @@ -101,7 +101,7 @@ TEST(StringTests, stringToUint) EXPECT_EQ(123, value); } -TEST(StringTests, splitString1) +TEST(StringTests, splitString_twoSeparator_returnThreeParts) { String string = "stub1:stub2:stub3"; @@ -113,9 +113,9 @@ TEST(StringTests, splitString1) EXPECT_EQ("stub3", results[2]); } -TEST(StringTests, splitString2) +TEST(StringTests, splitString_oneSeparator_returnTwoParts) { - String string = "stub1:stub2:"; + String string = "stub1:stub2"; std::vector results = string::splitString(string, ':'); @@ -124,7 +124,7 @@ TEST(StringTests, splitString2) EXPECT_EQ("stub2", results[1]); } -TEST(StringTests, splitString3) +TEST(StringTests, splitString_noSeparator_returnOriginalString) { String string = "stub1"; @@ -134,3 +134,35 @@ TEST(StringTests, splitString3) EXPECT_EQ("stub1", results[0]); } +TEST(StringTests, splitString_tailSeparator_returnTwoParts) +{ + String string = "stub1:stub2:"; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(2, results.size()); + EXPECT_EQ("stub1", results[0]); + EXPECT_EQ("stub2", results[1]); +} + +TEST(StringTests, splitString_headSeparator_returnTwoParts) +{ + String string = ":stub1:stub2"; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(2, results.size()); + EXPECT_EQ("stub1", results[0]); + EXPECT_EQ("stub2", results[1]); +} + +TEST(StringTests, splitString_headAndTailSeparators_returnTwoParts) +{ + String string = ":stub1:stub2:"; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(2, results.size()); + EXPECT_EQ("stub1", results[0]); + EXPECT_EQ("stub2", results[1]); +} From c148fbb7f4ea94200bdc07c48d0256c3a13c6bf5 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 29 Oct 2015 13:08:00 -0700 Subject: [PATCH 103/572] Fix unit tests name convention #4933 --- src/test/unittests/base/StringTests.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp index 296064177..55acb914d 100644 --- a/src/test/unittests/base/StringTests.cpp +++ b/src/test/unittests/base/StringTests.cpp @@ -21,7 +21,7 @@ using namespace synergy; -TEST(StringTests, format) +TEST(StringTests, format_formatWithArguments_formatedString) { const char* format = "%%%{1}=%{2}"; const char* arg1 = "answer"; @@ -32,7 +32,7 @@ TEST(StringTests, format) EXPECT_EQ("%answer=42", result); } -TEST(StringTests, findReplaceAll) +TEST(StringTests, findReplaceAll_inputString_replacedString) { String subject = "foobar"; String find = "bar"; @@ -43,7 +43,7 @@ TEST(StringTests, findReplaceAll) EXPECT_EQ("foobaz", subject); } -TEST(StringTests, sprintf) +TEST(StringTests, sprintf_formatWithArgument_formatedString) { const char* format = "%s=%d"; const char* arg1 = "answer"; @@ -54,7 +54,7 @@ TEST(StringTests, sprintf) EXPECT_EQ("answer=42", result); } -TEST(StringTests, toHex) +TEST(StringTests, toHex_plaintext_hexString) { String subject = "foobar"; int width = 2; @@ -64,7 +64,7 @@ TEST(StringTests, toHex) EXPECT_EQ("666f6f626172", subject); } -TEST(StringTests, uppercase) +TEST(StringTests, uppercase_lowercaseInput_uppercaseOutput) { String subject = "12foo3BaR"; @@ -73,7 +73,7 @@ TEST(StringTests, uppercase) EXPECT_EQ("12FOO3BAR", subject); } -TEST(StringTests, removeChar) +TEST(StringTests, removeChar_inputString_removeAllSpecifiedCharactors) { String subject = "foobar"; const char c = 'o'; @@ -83,7 +83,7 @@ TEST(StringTests, removeChar) EXPECT_EQ("fbar", subject); } -TEST(StringTests, intToString) +TEST(StringTests, intToString_inputInt_outputString) { size_t value = 123; @@ -92,7 +92,7 @@ TEST(StringTests, intToString) EXPECT_EQ("123", number); } -TEST(StringTests, stringToUint) +TEST(StringTests, stringToUint_inputString_outputInt) { String number = "123"; From 942dcabdcd028f2cf18102dccfe7e90dcf352fba Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 29 Oct 2015 15:11:09 -0700 Subject: [PATCH 104/572] Add split empty string unit test #4933 --- src/test/unittests/base/StringTests.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp index 55acb914d..f34e917a8 100644 --- a/src/test/unittests/base/StringTests.cpp +++ b/src/test/unittests/base/StringTests.cpp @@ -134,6 +134,15 @@ TEST(StringTests, splitString_noSeparator_returnOriginalString) EXPECT_EQ("stub1", results[0]); } +TEST(StringTests, splitString_emptyString_returnEmptyVector) +{ + String string; + + std::vector results = string::splitString(string, ':'); + + EXPECT_EQ(0, results.size()); +} + TEST(StringTests, splitString_tailSeparator_returnTwoParts) { String string = "stub1:stub2:"; From b4563a42c49a70973e76ccb25315220445165398 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 29 Oct 2015 15:47:10 -0700 Subject: [PATCH 105/572] Use string split function in syntool #4933 --- src/lib/synergy/ToolApp.cpp | 83 ++++++++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 4b41b0d80..7b5a5715e 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -152,31 +152,35 @@ ToolApp::loginAuth() String credentials; std::cin >> credentials; - size_t separator1 = credentials.find(':'); - size_t separator2 = credentials.find(':', separator1 + 1); - size_t separator3 = credentials.find(':', separator2 + 1); - String email = credentials.substr(0, separator1); - String password = credentials.substr(separator1 + 1, separator2 - separator1 - 1); - String macHash; - String os; - if (separator3 != String::npos) { - macHash = credentials.substr(separator2 + 1, separator3 - separator2 - 1); - os = credentials.substr(separator3 + 1, credentials.length() - separator3 - 1); + std::vector parts = synergy::string::splitString(credentials, ':'); + int count = parts.size(); + + if (count == 3 || count == 4) { + String email = parts[0]; + String password = parts[1]; + String macHash = parts[2]; + String os; + + if (count == 4) { + os = parts[3]; + } + else { + os = ARCH->getOSName(); + } + + std::stringstream ss; + ss << JSON_URL << "auth/"; + ss << "?email=" << ARCH->internet().urlEncode(email); + ss << "&password=" << password; + ss << "&mac=" << macHash; + ss << "&os=" << ARCH->internet().urlEncode(os); + ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName()); + + std::cout << ARCH->internet().get(ss.str()) << std::endl; } else { - macHash = credentials.substr(separator2 + 1, credentials.length() - separator2 - 1); - os = ARCH->getOSName(); + throw XSynergy("Invalid credentials."); } - - std::stringstream ss; - ss << JSON_URL << "auth/"; - ss << "?email=" << ARCH->internet().urlEncode(email); - ss << "&password=" << password; - ss << "&mac=" << macHash; - ss << "&os=" << ARCH->internet().urlEncode(os); - ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName()); - - std::cout << ARCH->internet().get(ss.str()) << std::endl; } void @@ -203,16 +207,31 @@ ToolApp::notifyActivation() String info; std::cin >> info; - size_t separator = info.find(':'); - String action = info.substr(0, separator); - String macHash = info.substr(separator + 1, info.length()); + std::vector parts = synergy::string::splitString(info, ':'); + int count = parts.size(); - std::stringstream ss; - ss << JSON_URL << "notify/"; - ss << "?action=" << action; - ss << "&mac=" << macHash; - ss << "&os=" << ARCH->internet().urlEncode(ARCH->getOSName()); - ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName()); + if (count == 2 || count == 3) { + String action = parts[0]; + String macHash = parts[1]; + String os; - std::cout << ARCH->internet().get(ss.str()) << std::endl; + if (count == 3) { + os = parts[2]; + } + else { + os = ARCH->getOSName(); + } + + std::stringstream ss; + ss << JSON_URL << "notify/"; + ss << "?action=" << action; + ss << "&mac=" << macHash; + ss << "&os=" << ARCH->internet().urlEncode(ARCH->getOSName()); + ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName()); + + std::cout << ARCH->internet().get(ss.str()) << std::endl; + } + else { + throw XSynergy("Invalid credentials."); + } } From f03b453c6f9167dc51e7a24aa8485b35a0354b43 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 29 Oct 2015 15:55:55 -0700 Subject: [PATCH 106/572] Pass Linux OS info during notification #4932 --- src/gui/src/CoreInterface.cpp | 11 ++++++++++- src/gui/src/CoreInterface.h | 2 +- src/gui/src/SetupWizard.cpp | 6 ++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index 4705b8cc9..9d24a3df7 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -18,6 +18,7 @@ #include "CoreInterface.h" #include "CommandProcess.h" +#include "QUtility.h" #include #include @@ -73,9 +74,17 @@ QString CoreInterface::checkSubscription() return run(args); } -QString CoreInterface::notifyActivation(const QString& input) +QString CoreInterface::notifyActivation(const QString& action) { QStringList args("--notify-activation"); + + QString input(action + ":" + hash(getFirstMacAddress())); + QString os= getOSInformation(); + if (!os.isEmpty()) { + input.append(":").append(os); + } + input.append("\n"); + return run(args, input); } diff --git a/src/gui/src/CoreInterface.h b/src/gui/src/CoreInterface.h index 4949bddc5..004a3928d 100644 --- a/src/gui/src/CoreInterface.h +++ b/src/gui/src/CoreInterface.h @@ -31,6 +31,6 @@ class CoreInterface QString getSubscriptionFilename(); QString activateSerial(const QString& serial); QString checkSubscription(); - QString notifyActivation(const QString& input); + QString notifyActivation(const QString& action); QString run(const QStringList& args, const QString& input = ""); }; diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index bbe143287..c45f30a06 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -217,16 +217,14 @@ void SetupWizard::accept() { appConfig.setSerialKey(m_pLineEditSerialKey->text()); - QString info("serial:" + hash(getFirstMacAddress()) + "\n"); CoreInterface coreInterface; - coreInterface.notifyActivation(info); + coreInterface.notifyActivation("serial"); } if (m_pRadioButtonSkip->isChecked()) { - QString info("skip:" + hash(getFirstMacAddress()) + "\n"); CoreInterface coreInterface; - coreInterface.notifyActivation(info); + coreInterface.notifyActivation("skip"); } m_MainWindow.setEdition(m_Edition); From 0a1c4395357e6afe9751541f093ea68a4a6991f5 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 29 Oct 2015 16:50:23 -0700 Subject: [PATCH 107/572] Only send notify request on wizard finish #4932 --- src/gui/src/CoreInterface.cpp | 4 ++-- src/gui/src/CoreInterface.h | 2 +- src/gui/src/SetupWizard.cpp | 11 +++++++++-- src/gui/src/WebClient.cpp | 7 +------ src/lib/synergy/ToolApp.cpp | 24 +++++++----------------- 5 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index 9d24a3df7..12703ae7c 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -74,11 +74,11 @@ QString CoreInterface::checkSubscription() return run(args); } -QString CoreInterface::notifyActivation(const QString& action) +QString CoreInterface::notifyActivation(const QString& identity) { QStringList args("--notify-activation"); - QString input(action + ":" + hash(getFirstMacAddress())); + QString input(identity + ":" + hash(getFirstMacAddress())); QString os= getOSInformation(); if (!os.isEmpty()) { input.append(":").append(os); diff --git a/src/gui/src/CoreInterface.h b/src/gui/src/CoreInterface.h index 004a3928d..f65348af8 100644 --- a/src/gui/src/CoreInterface.h +++ b/src/gui/src/CoreInterface.h @@ -31,6 +31,6 @@ class CoreInterface QString getSubscriptionFilename(); QString activateSerial(const QString& serial); QString checkSubscription(); - QString notifyActivation(const QString& action); + QString notifyActivation(const QString& identity); QString run(const QStringList& args, const QString& input = ""); }; diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index c45f30a06..ecacab650 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -211,6 +211,9 @@ void SetupWizard::accept() QString hashResult = hash(hashSrc); appConfig.setUserToken(hashResult); appConfig.setEdition(m_Edition); + + CoreInterface coreInterface; + coreInterface.notifyActivation("login:" + m_pLineEditEmail->text()); } if (m_pRadioButtonSubscription->isChecked()) @@ -218,13 +221,13 @@ void SetupWizard::accept() appConfig.setSerialKey(m_pLineEditSerialKey->text()); CoreInterface coreInterface; - coreInterface.notifyActivation("serial"); + coreInterface.notifyActivation("serial:" + m_pLineEditSerialKey->text()); } if (m_pRadioButtonSkip->isChecked()) { CoreInterface coreInterface; - coreInterface.notifyActivation("skip"); + coreInterface.notifyActivation("skip:unknown"); } m_MainWindow.setEdition(m_Edition); @@ -251,6 +254,10 @@ void SetupWizard::reject() m_MainWindow.open(); } + // treat cancel as skip + CoreInterface coreInterface; + coreInterface.notifyActivation("skip:unknown"); + QWizard::reject(); } diff --git a/src/gui/src/WebClient.cpp b/src/gui/src/WebClient.cpp index 9362c0080..451bf47f9 100644 --- a/src/gui/src/WebClient.cpp +++ b/src/gui/src/WebClient.cpp @@ -94,12 +94,7 @@ QString WebClient::request( { QStringList args("--login-auth"); // hash password in case it contains interesting chars. - QString credentials(email + ":" + hash(password) + ":" + hash(getFirstMacAddress())); - QString os= getOSInformation(); - if (!os.isEmpty()) { - credentials.append(":").append(os); - } - credentials.append("\n"); + QString credentials(email + ":" + hash(password) + "\n"); return m_CoreInterface.run(args, credentials); } diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 7b5a5715e..0b4978b71 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -155,26 +155,14 @@ ToolApp::loginAuth() std::vector parts = synergy::string::splitString(credentials, ':'); int count = parts.size(); - if (count == 3 || count == 4) { + if (count == 2 ) { String email = parts[0]; String password = parts[1]; - String macHash = parts[2]; - String os; - - if (count == 4) { - os = parts[3]; - } - else { - os = ARCH->getOSName(); - } std::stringstream ss; ss << JSON_URL << "auth/"; ss << "?email=" << ARCH->internet().urlEncode(email); ss << "&password=" << password; - ss << "&mac=" << macHash; - ss << "&os=" << ARCH->internet().urlEncode(os); - ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName()); std::cout << ARCH->internet().get(ss.str()) << std::endl; } @@ -210,13 +198,14 @@ ToolApp::notifyActivation() std::vector parts = synergy::string::splitString(info, ':'); int count = parts.size(); - if (count == 2 || count == 3) { + if (count == 3 || count == 4) { String action = parts[0]; - String macHash = parts[1]; + String identity = parts[1]; + String macHash = parts[2]; String os; - if (count == 3) { - os = parts[2]; + if (count == 4) { + os = parts[3]; } else { os = ARCH->getOSName(); @@ -225,6 +214,7 @@ ToolApp::notifyActivation() std::stringstream ss; ss << JSON_URL << "notify/"; ss << "?action=" << action; + ss << "&identity=" << identity; ss << "&mac=" << macHash; ss << "&os=" << ARCH->internet().urlEncode(ARCH->getOSName()); ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName()); From 068b3b98ed68a6e2bdda4dc681d4ce99a40b53de Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 30 Oct 2015 11:25:16 -0700 Subject: [PATCH 108/572] Fix compile warnning #4933 --- src/lib/synergy/ToolApp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 0b4978b71..6a156b269 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -153,7 +153,7 @@ ToolApp::loginAuth() std::cin >> credentials; std::vector parts = synergy::string::splitString(credentials, ':'); - int count = parts.size(); + size_t count = parts.size(); if (count == 2 ) { String email = parts[0]; @@ -196,7 +196,7 @@ ToolApp::notifyActivation() std::cin >> info; std::vector parts = synergy::string::splitString(info, ':'); - int count = parts.size(); + size_t count = parts.size(); if (count == 3 || count == 4) { String action = parts[0]; From 73ac3df22d9799ac8ddcbea79c24aa456d57a56c Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 30 Oct 2015 11:41:51 -0700 Subject: [PATCH 109/572] Limit expired time to trial version #4716 --- src/lib/synergy/SubscriptionManager.cpp | 6 ++++-- src/lib/synergy/SubscriptionManager.h | 5 +++-- src/test/unittests/synergy/SubscriptionTests.cpp | 25 +++++++++++++++++++----- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/lib/synergy/SubscriptionManager.cpp b/src/lib/synergy/SubscriptionManager.cpp index 2b51e17d0..ecc272c3c 100644 --- a/src/lib/synergy/SubscriptionManager.cpp +++ b/src/lib/synergy/SubscriptionManager.cpp @@ -150,12 +150,14 @@ SubscriptionManager::parsePlainSerial(const String& plainText, SubscriptionKey& sscanf(parts.at(5).c_str(), "%d", &key.m_expireTime); // TODO: use Arch time - if (time(0) > key.m_expireTime) { + if (time(0) > key.m_expireTime && + key.m_type == "trial") { throw XSubscription(synergy::string::sprintf( "%s subscription has expired", key.m_type.c_str())); } - else if (time(0) > key.m_warnTime) { + else if (time(0) > key.m_warnTime && + key.m_type == "trial") { LOG((CLOG_WARN "%s subscription will expire soon", key.m_type.c_str())); } diff --git a/src/lib/synergy/SubscriptionManager.h b/src/lib/synergy/SubscriptionManager.h index 1f08006b1..2817f7683 100644 --- a/src/lib/synergy/SubscriptionManager.h +++ b/src/lib/synergy/SubscriptionManager.h @@ -41,8 +41,9 @@ class SubscriptionManager { FRIEND_TEST(SubscriptionTests, decode_invalidSerial_outputPlainText); FRIEND_TEST(SubscriptionTests, parsePlainSerial_noParity_throwException); FRIEND_TEST(SubscriptionTests, parsePlainSerial_invalidSerial_throwException); - FRIEND_TEST(SubscriptionTests, parsePlainSerial_validSerial_throwException); - FRIEND_TEST(SubscriptionTests, parsePlainSerial_expiredSerial_throwException); + FRIEND_TEST(SubscriptionTests, parsePlainSerial_validSerial_validSubscriptionKey); + FRIEND_TEST(SubscriptionTests, parsePlainSerial_expiredTrialSerial_throwException); + FRIEND_TEST(SubscriptionTests, parsePlainSerial_expiredBasicSerial_validSubscriptionKey); private: String decode(const String& input); diff --git a/src/test/unittests/synergy/SubscriptionTests.cpp b/src/test/unittests/synergy/SubscriptionTests.cpp index 3e2c9dc99..368542ae0 100644 --- a/src/test/unittests/synergy/SubscriptionTests.cpp +++ b/src/test/unittests/synergy/SubscriptionTests.cpp @@ -54,21 +54,22 @@ TEST(SubscriptionTests, parsePlainSerial_invalidSerial_throwException) EXPECT_THROW(subscriptionManager.parsePlainSerial(painText, key), XSubscription); } -TEST(SubscriptionTests, parsePlainSerial_validSerial_throwException) +TEST(SubscriptionTests, parsePlainSerial_validSerial_validSubscriptionKey) { + // valid until 2 March 2049 SubscriptionManager subscriptionManager; - String painText("{v1;trial;Bob;1;1498297600;1498384000}"); + String painText("{v1;trial;Bob;1;2147483647;2147483647}"); SubscriptionKey key; subscriptionManager.parsePlainSerial(painText, key); EXPECT_EQ("trial", key.m_type); EXPECT_EQ("Bob", key.m_name); EXPECT_EQ(1, key.m_userLimit); - EXPECT_EQ(1498297600, key.m_warnTime); - EXPECT_EQ(1498384000, key.m_expireTime); + EXPECT_EQ(2147483647, key.m_warnTime); + EXPECT_EQ(2147483647, key.m_expireTime); } -TEST(SubscriptionTests, parsePlainSerial_expiredSerial_throwException) +TEST(SubscriptionTests, parsePlainSerial_expiredTrialSerial_throwException) { SubscriptionManager subscriptionManager; String painText("{v1;trial;Bob;1;1398297600;1398384000}"); @@ -76,3 +77,17 @@ TEST(SubscriptionTests, parsePlainSerial_expiredSerial_throwException) EXPECT_THROW(subscriptionManager.parsePlainSerial(painText, key), XSubscription); } + +TEST(SubscriptionTests, parsePlainSerial_expiredBasicSerial_validSubscriptionKey) +{ + SubscriptionManager subscriptionManager; + String painText("{v1;basic;Bob;1;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(1398297600, key.m_warnTime); + EXPECT_EQ(1398384000, key.m_expireTime); +} From bdc02677ec776ceb28802a522ac9378d48510e85 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 30 Oct 2015 11:57:12 -0700 Subject: [PATCH 110/572] Add trial edition type #4716 --- src/gui/src/EditionType.h | 1 + src/gui/src/MainWindow.cpp | 3 +++ src/gui/src/PluginWizardPage.cpp | 3 +-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/gui/src/EditionType.h b/src/gui/src/EditionType.h index ec465a6c4..2229cd231 100644 --- a/src/gui/src/EditionType.h +++ b/src/gui/src/EditionType.h @@ -21,6 +21,7 @@ enum qEditionType { Basic, Pro, + Trial, Unknown }; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 99cd93360..572695bc6 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -992,6 +992,9 @@ void MainWindow::setEdition(int type) else if (type == Pro) { title = "Synergy Pro"; } + else if (type == Trial) { + title = "Synergy Trial"; + } else { title = "Synergy (UNREGISTERED)"; } diff --git a/src/gui/src/PluginWizardPage.cpp b/src/gui/src/PluginWizardPage.cpp index ecbc26e4b..ace607da6 100644 --- a/src/gui/src/PluginWizardPage.cpp +++ b/src/gui/src/PluginWizardPage.cpp @@ -64,8 +64,7 @@ void PluginWizardPage::initializePage() { QWizardPage::initializePage(); - if (m_Edition == Unknown || - m_Edition == Basic) { + if (m_Edition != Pro) { updateStatus(tr("Setup complete.")); showFinished(); return; From 382607129c439b8a16a6f6558a2e97de54057383 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 30 Oct 2015 12:43:28 -0700 Subject: [PATCH 111/572] Make notify failure silent #4932 --- 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 6a156b269..a8f617146 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -222,6 +222,6 @@ ToolApp::notifyActivation() std::cout << ARCH->internet().get(ss.str()) << std::endl; } else { - throw XSynergy("Invalid credentials."); + LOG((CLOG_WARN "notify activation failed")); } } From 10e3917b617c690e4c7f0bd293a420f3ed23c466 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 30 Oct 2015 13:42:05 -0700 Subject: [PATCH 112/572] Change notify failure log level #4932 --- 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 a8f617146..b4e19a840 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -222,6 +222,6 @@ ToolApp::notifyActivation() std::cout << ARCH->internet().get(ss.str()) << std::endl; } else { - LOG((CLOG_WARN "notify activation failed")); + LOG((CLOG_DEBUG "notify activation failed")); } } From 4c1b6f3f2340cb03ecf581605ec1c8b1d81659fd Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 30 Oct 2015 13:49:18 -0700 Subject: [PATCH 113/572] Use test website #4932 --- 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 b4e19a840..74d1cacf4 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -30,7 +30,7 @@ #include "platform/MSWindowsSession.h" #endif -#define JSON_URL "https://synergy-project.org/premium/json/" +#define JSON_URL "https://test.synergy-project.org/premium/json/" enum { kErrorOk, From 9eec29927833aee4d5436912e94655a053610c83 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 12 Nov 2015 09:50:30 -0800 Subject: [PATCH 114/572] Check user type from serial key #4715 --- src/gui/src/SubscriptionManager.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index a0c109166..0173248a7 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -88,9 +88,15 @@ bool SubscriptionManager::checkSubscriptionExist() int SubscriptionManager::getEditionType(QString& string) { - if (string.contains("full subscription valid")) { + if (string.contains("pro subscription valid")) { return Pro; } + else if (string.contains("basic subscription valid")) { + return Basic; + } + else if (string.contains("trial subscription valid")) { + return Trial; + } return Unknown; } From 55df81b1a66804b87fb392a9d258f7d81e94eab0 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 12 Nov 2015 10:08:35 -0800 Subject: [PATCH 115/572] Add company and email into serial key #4715 --- src/lib/synergy/SubscriptionKey.h | 2 ++ src/lib/synergy/SubscriptionManager.cpp | 10 ++++++---- src/lib/synergy/SubscriptionManager.h | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/lib/synergy/SubscriptionKey.h b/src/lib/synergy/SubscriptionKey.h index 0d65a17c7..28744fed5 100644 --- a/src/lib/synergy/SubscriptionKey.h +++ b/src/lib/synergy/SubscriptionKey.h @@ -22,6 +22,8 @@ struct SubscriptionKey { String m_name; String m_type; + String m_email; + String m_company; int m_userLimit; int m_warnTime; int m_expireTime; diff --git a/src/lib/synergy/SubscriptionManager.cpp b/src/lib/synergy/SubscriptionManager.cpp index ecc272c3c..a657a21eb 100644 --- a/src/lib/synergy/SubscriptionManager.cpp +++ b/src/lib/synergy/SubscriptionManager.cpp @@ -140,14 +140,16 @@ SubscriptionManager::parsePlainSerial(const String& plainText, SubscriptionKey& pos += 1; } - // e.g.: {v1;trial;Bob;1;1398297600;1398384000} - if ((parts.size() == 6) + // e.g.: {v1;trial;Bob;1;email;company name;1398297600;1398384000} + if ((parts.size() == 8) && (parts.at(0).find("v1") != String::npos)) { key.m_type = parts.at(1); key.m_name = parts.at(2); sscanf(parts.at(3).c_str(), "%d", &key.m_userLimit); - sscanf(parts.at(4).c_str(), "%d", &key.m_warnTime); - sscanf(parts.at(5).c_str(), "%d", &key.m_expireTime); + key.m_email = parts.at(4); + key.m_company = parts.at(5); + sscanf(parts.at(6).c_str(), "%d", &key.m_warnTime); + sscanf(parts.at(7).c_str(), "%d", &key.m_expireTime); // TODO: use Arch time if (time(0) > key.m_expireTime && diff --git a/src/lib/synergy/SubscriptionManager.h b/src/lib/synergy/SubscriptionManager.h index 2817f7683..fb52701b1 100644 --- a/src/lib/synergy/SubscriptionManager.h +++ b/src/lib/synergy/SubscriptionManager.h @@ -37,13 +37,14 @@ class SubscriptionManager { private: FRIEND_TEST(SubscriptionTests, decode_invalidLength_throwException); - FRIEND_TEST(SubscriptionTests, decode_unrecognizedDigit_throwException); FRIEND_TEST(SubscriptionTests, decode_invalidSerial_outputPlainText); + FRIEND_TEST(SubscriptionTests, decode_unrecognizedDigit_throwException); FRIEND_TEST(SubscriptionTests, parsePlainSerial_noParity_throwException); FRIEND_TEST(SubscriptionTests, parsePlainSerial_invalidSerial_throwException); FRIEND_TEST(SubscriptionTests, parsePlainSerial_validSerial_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); From 8c4ccdf1487c1ea3b4a29fb680890c2c9c5d088f Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 12 Nov 2015 10:08:55 -0800 Subject: [PATCH 116/572] Unit test for new fields in serial key #4715 --- src/test/unittests/synergy/SubscriptionTests.cpp | 25 ++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/test/unittests/synergy/SubscriptionTests.cpp b/src/test/unittests/synergy/SubscriptionTests.cpp index 368542ae0..eeda3e7be 100644 --- a/src/test/unittests/synergy/SubscriptionTests.cpp +++ b/src/test/unittests/synergy/SubscriptionTests.cpp @@ -58,13 +58,32 @@ TEST(SubscriptionTests, parsePlainSerial_validSerial_validSubscriptionKey) { // valid until 2 March 2049 SubscriptionManager subscriptionManager; - String painText("{v1;trial;Bob;1;2147483647;2147483647}"); + 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); } @@ -81,13 +100,15 @@ TEST(SubscriptionTests, parsePlainSerial_expiredTrialSerial_throwException) TEST(SubscriptionTests, parsePlainSerial_expiredBasicSerial_validSubscriptionKey) { SubscriptionManager subscriptionManager; - String painText("{v1;basic;Bob;1;1398297600;1398384000}"); + 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); } From 4b7796d215f98a1a937e291a3946519fd2705a9d Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 12 Nov 2015 12:05:26 -0800 Subject: [PATCH 117/572] Catch exception from notification request #4932 --- src/lib/synergy/ToolApp.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 74d1cacf4..0549c396f 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -30,7 +30,7 @@ #include "platform/MSWindowsSession.h" #endif -#define JSON_URL "https://test.synergy-project.org/premium/json/" +#define JSON_URL "http://test.synergy-project.org/premium/json/" enum { kErrorOk, @@ -219,9 +219,17 @@ ToolApp::notifyActivation() ss << "&os=" << ARCH->internet().urlEncode(ARCH->getOSName()); ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName()); - std::cout << ARCH->internet().get(ss.str()) << std::endl; + try { + std::cout << ARCH->internet().get(ss.str()) << std::endl; + } + catch (std::exception& e) { + LOG((CLOG_CRIT "An error occurred during notification: %s\n", e.what())); + } + catch (...) { + LOG((CLOG_CRIT "An unknown error occurred during notification.\n")); + } } else { - LOG((CLOG_DEBUG "notify activation failed")); + LOG((CLOG_DEBUG "notification failed")); } } From ad16599fa59e01bbcf8496bd9c02d4bf62307c5f Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 12 Nov 2015 12:05:37 -0800 Subject: [PATCH 118/572] Fix code style --- src/gui/src/CoreInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index 12703ae7c..13560c1b8 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -96,5 +96,4 @@ QString CoreInterface::run(const QStringList& args, const QString& input) CommandProcess commandProcess(program, args, input); return commandProcess.run(); - } From 2fd0d5c68f70ede82f11b7084de143ce70a73422 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 13 Nov 2015 11:04:23 -0800 Subject: [PATCH 119/572] Modify regex to recognize edition from jason #4933 --- src/gui/src/WebClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/src/WebClient.cpp b/src/gui/src/WebClient.cpp index 451bf47f9..cd07b882a 100644 --- a/src/gui/src/WebClient.cpp +++ b/src/gui/src/WebClient.cpp @@ -50,7 +50,7 @@ int WebClient::getEdition( if (resultRegex.exactMatch(responseJson)) { QString boolString = resultRegex.cap(1); if (boolString == "true") { - QRegExp editionRegex(".*\"edition\".*:.*\"([^\"]+)\".*"); + QRegExp editionRegex(".*\"edition\":(0|1|2|3).*"); if (editionRegex.exactMatch(responseJson)) { QString e = editionRegex.cap(1); edition = e.toInt(); From 97809f9040b28351190f7278f251672470d25b7f Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 16 Nov 2015 09:24:49 -0800 Subject: [PATCH 120/572] Revert "Modify regex to recognize edition from jason #4933" This reverts commit 2fd0d5c68f70ede82f11b7084de143ce70a73422. --- src/gui/src/WebClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/src/WebClient.cpp b/src/gui/src/WebClient.cpp index cd07b882a..451bf47f9 100644 --- a/src/gui/src/WebClient.cpp +++ b/src/gui/src/WebClient.cpp @@ -50,7 +50,7 @@ int WebClient::getEdition( if (resultRegex.exactMatch(responseJson)) { QString boolString = resultRegex.cap(1); if (boolString == "true") { - QRegExp editionRegex(".*\"edition\":(0|1|2|3).*"); + QRegExp editionRegex(".*\"edition\".*:.*\"([^\"]+)\".*"); if (editionRegex.exactMatch(responseJson)) { QString e = editionRegex.cap(1); edition = e.toInt(); From 18d23d6f89652b8f8a82478cb57d82f20691a3a1 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 17 Nov 2015 10:41:24 -0800 Subject: [PATCH 121/572] Rephrase using serial recommendation #5020 --- src/gui/src/SetupWizard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index ecacab650..ae41c89be 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -103,7 +103,7 @@ bool SetupWizard::validateCurrentPage() QMessageBox::StandardButton reply = QMessageBox::information( this, tr("Setup Synergy"), - tr("Would you like to use serial key to activate?"), + tr("Would you like to use your serial key instead?"), QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { From f8e9047c36e5db0ebd42445248e86ca3c61cb72e Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 17 Nov 2015 14:17:00 -0800 Subject: [PATCH 122/572] Rephrase and refactor serial warning message #4716 --- src/gui/src/MainWindow.cpp | 15 +++--- src/gui/src/SetupWizard.cpp | 12 ++--- src/gui/src/SubscriptionManager.cpp | 82 ++++++++++++++++++++++----------- src/gui/src/SubscriptionManager.h | 13 ++++-- src/lib/synergy/SubscriptionManager.cpp | 24 +++++----- 5 files changed, 87 insertions(+), 59 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 572695bc6..2576c0e09 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -698,22 +698,19 @@ QString MainWindow::appPath(const QString& name) bool MainWindow::serverArgs(QStringList& args, QString& app) { - SubscriptionManager subscriptionManager; + int edition; + SubscriptionManager subscriptionManager(this, edition); if (subscriptionManager.checkSubscriptionExist()) { - int edition; - int state = subscriptionManager.checkSubscription(edition); - - if (state == kInvalid) { + if (!subscriptionManager.checkSubscription()) { return false; } - else if (state == kExpired) { - QMessageBox::warning(this, tr("Subscription is expired"), - tr("Your subscription is expired. Please purchase.")); - return false; + else { + setEdition(edition); } } + app = appPath(appConfig().synergysName()); if (!QFile::exists(app)) diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index ae41c89be..e97030d81 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -127,15 +127,9 @@ bool SetupWizard::validateCurrentPage() } else { // create subscription file in profile directory - SubscriptionManager subscriptionManager; - bool r = subscriptionManager.activateSerial(m_pLineEditSerialKey->text(), m_Edition); - if (!r) { - message.setText(tr("An error occurred while trying to activate using a serial key. " - "Please contact the helpdesk, and provide the " - "following details.\n\n%1").arg(subscriptionManager.getLastError())); - message.exec(); - - return r; + SubscriptionManager subscriptionManager(this, m_Edition); + if (!subscriptionManager.activateSerial(m_pLineEditSerialKey->text())) { + return false; } m_pPluginPage->setEdition(m_Edition); diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index 0173248a7..b9b0f5ff5 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -24,13 +24,16 @@ #include #include -SubscriptionManager::SubscriptionManager() +SubscriptionManager::SubscriptionManager(QWidget* parent, int& edition) : + m_pParent(parent), + m_Edition(edition) { + } -bool SubscriptionManager::activateSerial(const QString& serial, int& edition) +bool SubscriptionManager::activateSerial(const QString& serial) { - edition = Unknown; + m_Edition = Unknown; CoreInterface coreInterface; QString output; @@ -41,17 +44,18 @@ bool SubscriptionManager::activateSerial(const QString& serial, int& edition) catch (std::exception& e) { m_ErrorMessage = e.what(); + checkError(m_ErrorMessage); return false; } - edition = getEditionType(output); + checkOutput(output); return true; } -int SubscriptionManager::checkSubscription(int& edition) +bool SubscriptionManager::checkSubscription() { - edition = Unknown; + m_Edition = Unknown; CoreInterface coreInterface; QString output; try @@ -61,21 +65,13 @@ int SubscriptionManager::checkSubscription(int& edition) catch (std::exception& e) { m_ErrorMessage = e.what(); - - if (m_ErrorMessage.contains("subscription has expired")) { - return kExpired; - } - - return kInvalid; - } - - if (output.contains("subscription will expire soon")) { - return kExpiredSoon; + checkError(m_ErrorMessage); + return false; } - edition = getEditionType(output); + checkOutput(output); - return kValid; + return true; } bool SubscriptionManager::checkSubscriptionExist() @@ -86,17 +82,51 @@ bool SubscriptionManager::checkSubscriptionExist() return QFile::exists(subscriptionFilename); } -int SubscriptionManager::getEditionType(QString& string) +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")); + } + else { + QMessageBox::warning(m_pParent, tr("Subscription error"), + tr("An error occurred while trying to activate using a serial key. " + "Please contact the helpdesk, and provide the " + "following details.\n\n%1").arg(error)); + } +} + +void SubscriptionManager::checkOutput(QString& output) +{ + getEditionType(output); + checkExpiring(output); +} + +void SubscriptionManager::getEditionType(QString& output) { - if (string.contains("pro subscription valid")) { - return Pro; + if (output.contains("pro subscription valid")) { + m_Edition = Pro; } - else if (string.contains("basic subscription valid")) { - return Basic; + else if (output.contains("basic subscription valid")) { + m_Edition = Basic; } - else if (string.contains("trial subscription valid")) { - return Trial; + else if (output.contains("trial subscription valid")) { + m_Edition = Trial; } +} - return Unknown; +void SubscriptionManager::checkExpiring(QString& output) +{ + if (output.contains("trial will end in")) { + QRegExp dayLeftRegex(".*trial will end in ([0-9]+) day.*"); + if (dayLeftRegex.exactMatch(output)) { + QString dayLeft = dayLeftRegex.cap(1); + + // TODO: warn user once a day + 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")); + } + } } diff --git a/src/gui/src/SubscriptionManager.h b/src/gui/src/SubscriptionManager.h index 1af14ad67..bc272ef40 100644 --- a/src/gui/src/SubscriptionManager.h +++ b/src/gui/src/SubscriptionManager.h @@ -22,16 +22,21 @@ class SubscriptionManager : public QWidget { public: - SubscriptionManager(); + SubscriptionManager(QWidget* parent, int& edition); - bool activateSerial(const QString& serial, int& edition); - int checkSubscription(int& edition); + bool activateSerial(const QString& serial); + bool checkSubscription(); bool checkSubscriptionExist(); QString getLastError(){ return m_ErrorMessage; } private: - int getEditionType(QString& string); + void checkError(QString& error); + void checkOutput(QString& output); + void getEditionType(QString& output); + void checkExpiring(QString& output); private: QString m_ErrorMessage; + QWidget* m_pParent; + int& m_Edition; }; diff --git a/src/lib/synergy/SubscriptionManager.cpp b/src/lib/synergy/SubscriptionManager.cpp index a657a21eb..78207dc25 100644 --- a/src/lib/synergy/SubscriptionManager.cpp +++ b/src/lib/synergy/SubscriptionManager.cpp @@ -151,17 +151,19 @@ SubscriptionManager::parsePlainSerial(const String& plainText, SubscriptionKey& sscanf(parts.at(6).c_str(), "%d", &key.m_warnTime); sscanf(parts.at(7).c_str(), "%d", &key.m_expireTime); - // TODO: use Arch time - if (time(0) > key.m_expireTime && - key.m_type == "trial") { - throw XSubscription(synergy::string::sprintf( - "%s subscription has expired", - key.m_type.c_str())); - } - else if (time(0) > key.m_warnTime && - key.m_type == "trial") { - LOG((CLOG_WARN "%s subscription will expire soon", - key.m_type.c_str())); + // only limit to trial version + if (key.m_type == "trial") { + if (time(0) > key.m_expireTime) { + throw XSubscription("trial has expired"); + } + else if (time(0) > key.m_warnTime) { + int secLeft = key.m_expireTime - static_cast(time(0)); + const int spd = 60 * 60 * 24; + int dayLeft = secLeft / spd + 1; + LOG((CLOG_NOTE "trial will end in %d %s", + dayLeft, + dayLeft == 1 ? "day" : "days")); + } } const char* userText = (key.m_userLimit == 1) ? "user" : "users"; From 7ecc0457e042f2ce4c36f40a389994578411e32c Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 17 Nov 2015 14:51:04 -0800 Subject: [PATCH 123/572] Show subscription warning once a day #4716 --- src/gui/gui.pro | 3 +-- src/gui/src/AppConfig.cpp | 5 ++++- src/gui/src/AppConfig.h | 3 +++ src/gui/src/MainWindow.cpp | 3 +-- src/gui/src/SetupWizard.cpp | 3 +-- src/gui/src/SubscriptionManager.cpp | 27 +++++++++++++++++++++++---- src/gui/src/SubscriptionManager.h | 6 +++++- src/gui/src/SubscriptionState.h | 28 ---------------------------- 8 files changed, 38 insertions(+), 40 deletions(-) delete mode 100644 src/gui/src/SubscriptionState.h diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 5ada72ff5..6f5232af1 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -109,8 +109,7 @@ HEADERS += src/MainWindow.h \ src/Plugin.h \ src/WebClient.h \ ../lib/common/PluginVersion.h \ - src/SubscriptionManager.h \ - src/SubscriptionState.h + src/SubscriptionManager.h RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc macx { diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 6bbf73237..76e2234b6 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -58,7 +58,8 @@ AppConfig::AppConfig(QSettings* settings) : m_ElevateMode(false), m_AutoConfigPrompted(false), m_CryptoEnabled(false), - m_AutoHide(false) + m_AutoHide(false), + m_LastExpiringWarningTime(0) { Q_ASSERT(m_pSettings); @@ -133,6 +134,7 @@ void AppConfig::loadSettings() m_CryptoEnabled = settings().value("cryptoEnabled", false).toBool(); m_AutoHide = settings().value("autoHide", false).toBool(); m_Serialkey = settings().value("serialKey", "").toString(); + m_LastExpiringWarningTime = settings().value("lastExpiringWarningTime", 0).toInt(); } void AppConfig::saveSettings() @@ -155,6 +157,7 @@ void AppConfig::saveSettings() settings().setValue("cryptoEnabled", m_CryptoEnabled); settings().setValue("autoHide", m_AutoHide); settings().setValue("serialKey", m_Serialkey); + settings().setValue("lastExpiringWarningTime", m_LastExpiringWarningTime); } void AppConfig::setAutoConfig(bool autoConfig) diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 80de53f67..6a7c19054 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -80,6 +80,8 @@ class AppConfig QString userToken() { return m_UserToken; } void setSerialKey(QString serial) { m_Serialkey = serial; } QString serialKey() { return m_Serialkey; } + int lastExpiringWarningTime() const { return m_LastExpiringWarningTime; } + void setLastExpiringWarningTime(int t) { m_LastExpiringWarningTime = t; } QString synergysName() const { return m_SynergysName; } QString synergycName() const { return m_SynergycName; } @@ -132,6 +134,7 @@ class AppConfig bool m_CryptoEnabled; bool m_AutoHide; QString m_Serialkey; + int m_LastExpiringWarningTime; static const char m_SynergysName[]; static const char m_SynergycName[]; diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 2576c0e09..2f3ec96fd 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -32,7 +32,6 @@ #include "DataDownloader.h" #include "CommandProcess.h" #include "SubscriptionManager.h" -#include "SubscriptionState.h" #include "EditionType.h" #include "QUtility.h" #include "ProcessorArch.h" @@ -699,7 +698,7 @@ QString MainWindow::appPath(const QString& name) bool MainWindow::serverArgs(QStringList& args, QString& app) { int edition; - SubscriptionManager subscriptionManager(this, edition); + SubscriptionManager subscriptionManager(this, appConfig(), edition); if (subscriptionManager.checkSubscriptionExist()) { if (!subscriptionManager.checkSubscription()) { diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index e97030d81..130782208 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -20,7 +20,6 @@ #include "WebClient.h" #include "SubscriptionManager.h" #include "EditionType.h" -#include "SubscriptionState.h" #include "QSynergyApplication.h" #include "QUtility.h" @@ -127,7 +126,7 @@ bool SetupWizard::validateCurrentPage() } else { // create subscription file in profile directory - SubscriptionManager subscriptionManager(this, m_Edition); + SubscriptionManager subscriptionManager(this, m_MainWindow.appConfig(), m_Edition); if (!subscriptionManager.activateSerial(m_pLineEditSerialKey->text())) { return false; } diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index b9b0f5ff5..6820472e0 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -17,15 +17,19 @@ #include "SubscriptionManager.h" + #include "CoreInterface.h" #include "EditionType.h" -#include "SubscriptionState.h" +#include "AppConfig.h" #include #include +#include +#include -SubscriptionManager::SubscriptionManager(QWidget* parent, int& edition) : +SubscriptionManager::SubscriptionManager(QWidget* parent, AppConfig& appConfig, int& edition) : m_pParent(parent), + m_AppConfig(appConfig), m_Edition(edition) { @@ -117,12 +121,11 @@ void SubscriptionManager::getEditionType(QString& output) void SubscriptionManager::checkExpiring(QString& output) { - if (output.contains("trial will end in")) { + 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); - // TODO: warn user once a day QMessageBox::warning(m_pParent, tr("Subscription warning"), tr("Your trial will end in %1 %2. Click here to purchase") .arg(dayLeft) @@ -130,3 +133,19 @@ void SubscriptionManager::checkExpiring(QString& output) } } } + +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; +} diff --git a/src/gui/src/SubscriptionManager.h b/src/gui/src/SubscriptionManager.h index bc272ef40..54e7da34b 100644 --- a/src/gui/src/SubscriptionManager.h +++ b/src/gui/src/SubscriptionManager.h @@ -19,10 +19,12 @@ #include +class AppConfig; + class SubscriptionManager : public QWidget { public: - SubscriptionManager(QWidget* parent, int& edition); + SubscriptionManager(QWidget* parent, AppConfig& appConfig, int& edition); bool activateSerial(const QString& serial); bool checkSubscription(); @@ -34,9 +36,11 @@ class SubscriptionManager : public QWidget void checkOutput(QString& output); void getEditionType(QString& output); void checkExpiring(QString& output); + bool shouldWarnExpiring(); private: QString m_ErrorMessage; QWidget* m_pParent; + AppConfig& m_AppConfig; int& m_Edition; }; diff --git a/src/gui/src/SubscriptionState.h b/src/gui/src/SubscriptionState.h deleted file mode 100644 index e52a6df51..000000000 --- a/src/gui/src/SubscriptionState.h +++ /dev/null @@ -1,28 +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 . - */ - -#ifndef SUBSCRIPTIONSTATE_H -#define SUBSCRIPTIONSTATE_H - -enum qSubscriptionState { - kValid, - kInvalid, - kExpiredSoon, - kExpired -}; - -#endif // SUBSCRIPTIONSTATE_H From 5f73d339aae6c3663b2ca1a922ba880364994fc2 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 17 Nov 2015 15:20:22 -0800 Subject: [PATCH 124/572] Refactor purchase url #4716 --- src/gui/src/SubscriptionManager.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index 6820472e0..9794c3e11 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -27,6 +27,8 @@ #include #include +static const char purchaseURL[] = "https://synergy-project.org/account/"; + SubscriptionManager::SubscriptionManager(QWidget* parent, AppConfig& appConfig, int& edition) : m_pParent(parent), m_AppConfig(appConfig), @@ -90,7 +92,7 @@ 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")); + tr("Your trial has expired. Click here to purchase").arg(purchaseURL)); } else { QMessageBox::warning(m_pParent, tr("Subscription error"), @@ -127,9 +129,10 @@ void SubscriptionManager::checkExpiring(QString& 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") + tr("Your trial will end in %1 %2. Click here to purchase") .arg(dayLeft) - .arg(dayLeft == "1" ? "day" : "days")); + .arg(dayLeft == "1" ? "day" : "days") + .arg(purchaseURL)); } } } From 4698394e21f1b0113be13224314a03cd8a8dd8f0 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 18 Nov 2015 11:51:45 -0800 Subject: [PATCH 125/572] URL encode identity and MAC address --- src/lib/synergy/ToolApp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 0549c396f..fb2ca7ccc 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -214,8 +214,8 @@ ToolApp::notifyActivation() std::stringstream ss; ss << JSON_URL << "notify/"; ss << "?action=" << action; - ss << "&identity=" << identity; - ss << "&mac=" << macHash; + ss << "&identity=" << ARCH->internet().urlEncode(identity); + ss << "&mac=" << ARCH->internet().urlEncode(macHash); ss << "&os=" << ARCH->internet().urlEncode(ARCH->getOSName()); ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName()); From 20d4d4764811b07a7eaeb20614a2c4d8ce77b308 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 18 Nov 2015 15:03:06 -0800 Subject: [PATCH 126/572] Save edition type to settings after wizard finish #4715 --- src/gui/src/AppConfig.h | 3 ++- src/gui/src/SetupWizard.cpp | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 6a7c19054..398082f2f 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -97,6 +97,8 @@ class AppConfig void setAutoHide(bool b) { m_AutoHide = b; } bool getAutoHide() { return m_AutoHide; } + void saveSettings(); + protected: QSettings& settings() { return *m_pSettings; } void setScreenName(const QString& s) { m_ScreenName = s; } @@ -111,7 +113,6 @@ class AppConfig void setElevateMode(bool b) { m_ElevateMode = b; } void loadSettings(); - void saveSettings(); private: QSettings* m_pSettings; diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index 130782208..e2f4abcc8 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -223,9 +223,11 @@ void SetupWizard::accept() coreInterface.notifyActivation("skip:unknown"); } + appConfig.setEdition(m_Edition); m_MainWindow.setEdition(m_Edition); m_MainWindow.updateLocalFingerprint(); + appConfig.saveSettings(); settings.sync(); QWizard::accept(); From b1c5a83bfb62b5495615e3b79d714a434687a422 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 18 Nov 2015 15:05:05 -0800 Subject: [PATCH 127/572] Uthe edition value in app config as default #4715 --- src/gui/src/MainWindow.cpp | 18 ++---------------- src/gui/src/MainWindow.h | 1 - 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 2f3ec96fd..2417c7a47 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -134,7 +134,7 @@ MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) : m_pComboServerList->hide(); - updateEdition(); + setEdition(m_AppConfig.edition()); m_pLabelPadlock->hide(); @@ -939,7 +939,7 @@ void MainWindow::changeEvent(QEvent* event) retranslateUi(this); retranslateMenuBar(); - updateEdition(); + setEdition(m_AppConfig.edition()); break; } @@ -1285,20 +1285,6 @@ void MainWindow::promptAutoConfig() m_AppConfig.setAutoConfigPrompted(true); } -void MainWindow::updateEdition() -{ - QString mac = getFirstMacAddress(); - QString hashSrc = m_AppConfig.activateEmail() + mac; - QString hashResult = hash(hashSrc); - - if (hashResult == m_AppConfig.userToken()) { - setEdition(m_AppConfig.edition()); - } - else { - setEdition(Unknown); - } -} - void MainWindow::on_m_pComboServerList_currentIndexChanged(QString ) { if (m_pComboServerList->count() != 0) { diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index c159747a7..9c7aeebc4 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -172,7 +172,6 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase bool isBonjourRunning(); void downloadBonjour(); void promptAutoConfig(); - void updateEdition(); QString getProfileRootForArg(); void checkConnected(const QString& line); void checkFingerprint(const QString& line); From 0e58bfc5fe2454c2540b15b7598725eddff1ac40 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 18 Nov 2015 15:09:09 -0800 Subject: [PATCH 128/572] Remove user token #4715 --- src/gui/src/AppConfig.cpp | 2 -- src/gui/src/AppConfig.h | 3 --- src/gui/src/SetupWizard.cpp | 5 ----- 3 files changed, 10 deletions(-) diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 76e2234b6..69142d1d5 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -130,7 +130,6 @@ void AppConfig::loadSettings() m_AutoConfigPrompted = settings().value("autoConfigPrompted", false).toBool(); m_Edition = settings().value("edition", Unknown).toInt(); m_ActivateEmail = settings().value("activateEmail", "").toString(); - m_UserToken = settings().value("userToken", "").toString(); m_CryptoEnabled = settings().value("cryptoEnabled", false).toBool(); m_AutoHide = settings().value("autoHide", false).toBool(); m_Serialkey = settings().value("serialKey", "").toString(); @@ -153,7 +152,6 @@ void AppConfig::saveSettings() settings().setValue("autoConfigPrompted", m_AutoConfigPrompted); settings().setValue("edition", m_Edition); settings().setValue("activateEmail", m_ActivateEmail); - settings().setValue("userToken", m_UserToken); settings().setValue("cryptoEnabled", m_CryptoEnabled); settings().setValue("autoHide", m_AutoHide); settings().setValue("serialKey", m_Serialkey); diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 398082f2f..684c0015b 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -76,8 +76,6 @@ class AppConfig int edition() { return m_Edition; } void setActivateEmail(QString e) { m_ActivateEmail = e; } QString activateEmail() { return m_ActivateEmail; } - void setUserToken(QString t) { m_UserToken = t; } - QString userToken() { return m_UserToken; } void setSerialKey(QString serial) { m_Serialkey = serial; } QString serialKey() { return m_Serialkey; } int lastExpiringWarningTime() const { return m_LastExpiringWarningTime; } @@ -131,7 +129,6 @@ class AppConfig bool m_AutoConfigPrompted; int m_Edition; QString m_ActivateEmail; - QString m_UserToken; bool m_CryptoEnabled; bool m_AutoHide; QString m_Serialkey; diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index e2f4abcc8..e239b0159 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -199,11 +199,6 @@ void SetupWizard::accept() if (m_pRadioButtonActivate->isChecked()) { appConfig.setActivateEmail(m_pLineEditEmail->text()); - QString mac = getFirstMacAddress(); - QString hashSrc = m_pLineEditEmail->text() + mac; - QString hashResult = hash(hashSrc); - appConfig.setUserToken(hashResult); - appConfig.setEdition(m_Edition); CoreInterface coreInterface; coreInterface.notifyActivation("login:" + m_pLineEditEmail->text()); From cab8a992334c390b7d421a4b20ad514ee01b3b81 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 18 Nov 2015 16:03:32 -0800 Subject: [PATCH 129/572] Persist Profile folder #4715 --- src/gui/src/SubscriptionManager.cpp | 15 ++++++++++++++- src/gui/src/SubscriptionManager.h | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index 9794c3e11..6606cdf40 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -23,6 +23,7 @@ #include "AppConfig.h" #include +#include #include #include #include @@ -34,12 +35,12 @@ SubscriptionManager::SubscriptionManager(QWidget* parent, AppConfig& appConfig, m_AppConfig(appConfig), m_Edition(edition) { - } bool SubscriptionManager::activateSerial(const QString& serial) { m_Edition = Unknown; + persistDirectory(); CoreInterface coreInterface; QString output; @@ -62,6 +63,7 @@ bool SubscriptionManager::activateSerial(const QString& serial) bool SubscriptionManager::checkSubscription() { m_Edition = Unknown; + persistDirectory(); CoreInterface coreInterface; QString output; try @@ -152,3 +154,14 @@ bool SubscriptionManager::shouldWarnExpiring() return result; } + +void SubscriptionManager::persistDirectory() +{ + CoreInterface coreInterface; + QString profileDir = coreInterface.getProfileDir(); + + QDir dir(profileDir); + if (!dir.exists()) { + dir.mkpath("."); + } +} diff --git a/src/gui/src/SubscriptionManager.h b/src/gui/src/SubscriptionManager.h index 54e7da34b..646ff81a3 100644 --- a/src/gui/src/SubscriptionManager.h +++ b/src/gui/src/SubscriptionManager.h @@ -37,6 +37,7 @@ class SubscriptionManager : public QWidget void getEditionType(QString& output); void checkExpiring(QString& output); bool shouldWarnExpiring(); + void persistDirectory(); private: QString m_ErrorMessage; From 4b2fc8c4c968cd3dbf615e89175013d591101978 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 18 Nov 2015 16:05:08 -0800 Subject: [PATCH 130/572] Refactor function name #4715 --- src/gui/src/MainWindow.cpp | 2 +- src/gui/src/SubscriptionManager.cpp | 2 +- src/gui/src/SubscriptionManager.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 2417c7a47..e24c8ac1f 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -699,7 +699,7 @@ bool MainWindow::serverArgs(QStringList& args, QString& app) { int edition; SubscriptionManager subscriptionManager(this, appConfig(), edition); - if (subscriptionManager.checkSubscriptionExist()) + if (subscriptionManager.fileExists()) { if (!subscriptionManager.checkSubscription()) { return false; diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index 6606cdf40..f3f55611e 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -82,7 +82,7 @@ bool SubscriptionManager::checkSubscription() return true; } -bool SubscriptionManager::checkSubscriptionExist() +bool SubscriptionManager::fileExists() { CoreInterface coreInterface; QString subscriptionFilename = coreInterface.getSubscriptionFilename(); diff --git a/src/gui/src/SubscriptionManager.h b/src/gui/src/SubscriptionManager.h index 646ff81a3..594973521 100644 --- a/src/gui/src/SubscriptionManager.h +++ b/src/gui/src/SubscriptionManager.h @@ -28,7 +28,7 @@ class SubscriptionManager : public QWidget bool activateSerial(const QString& serial); bool checkSubscription(); - bool checkSubscriptionExist(); + bool fileExists(); QString getLastError(){ return m_ErrorMessage; } private: From e1e38bd3a20a2a3b947bbcb73a331e5ab68ae4e8 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 19 Nov 2015 10:01:50 -0800 Subject: [PATCH 131/572] Compress network error during notification #4932 --- src/lib/synergy/ToolApp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index fb2ca7ccc..048e51dd7 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -223,13 +223,13 @@ ToolApp::notifyActivation() std::cout << ARCH->internet().get(ss.str()) << std::endl; } catch (std::exception& e) { - LOG((CLOG_CRIT "An error occurred during notification: %s\n", e.what())); + LOG((CLOG_NOTE "An error occurred during notification: %s\n", e.what())); } catch (...) { - LOG((CLOG_CRIT "An unknown error occurred during notification.\n")); + LOG((CLOG_NOTE "An unknown error occurred during notification.\n")); } } else { - LOG((CLOG_DEBUG "notification failed")); + LOG((CLOG_NOTE "notification failed")); } } From 4be852c2f2752035325e18e207ca1651b029c867 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 19 Nov 2015 10:48:11 -0800 Subject: [PATCH 132/572] Move notify into thread #4932 --- src/gui/gui.pro | 6 ++++-- src/gui/src/ActivationNotifier.cpp | 36 +++++++++++++++++++++++++++++++++ src/gui/src/ActivationNotifier.h | 41 ++++++++++++++++++++++++++++++++++++++ src/gui/src/SetupWizard.cpp | 25 +++++++++++++++++------ src/gui/src/SetupWizard.h | 1 + 5 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 src/gui/src/ActivationNotifier.cpp create mode 100644 src/gui/src/ActivationNotifier.h diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 6f5232af1..73368b84d 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -62,7 +62,8 @@ SOURCES += src/main.cpp \ src/Plugin.cpp \ src/WebClient.cpp \ ../lib/common/PluginVersion.cpp \ - src/SubscriptionManager.cpp + src/SubscriptionManager.cpp \ + src/ActivationNotifier.cpp HEADERS += src/MainWindow.h \ src/AboutDialog.h \ src/ServerConfig.h \ @@ -109,7 +110,8 @@ HEADERS += src/MainWindow.h \ src/Plugin.h \ src/WebClient.h \ ../lib/common/PluginVersion.h \ - src/SubscriptionManager.h + src/SubscriptionManager.h \ + src/ActivationNotifier.h RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc macx { diff --git a/src/gui/src/ActivationNotifier.cpp b/src/gui/src/ActivationNotifier.cpp new file mode 100644 index 000000000..7efe4e82d --- /dev/null +++ b/src/gui/src/ActivationNotifier.cpp @@ -0,0 +1,36 @@ +/* + * 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 "ActivationNotifier.h" + +#include "CoreInterface.h" + +ActivationNotifier::ActivationNotifier(QObject *parent) : + QObject(parent) +{ +} + +void ActivationNotifier::setIdentity(QString identity) +{ + m_Identity = identity; +} + +void ActivationNotifier::notify() +{ + CoreInterface coreInterface; + coreInterface.notifyActivation(m_Identity); +} diff --git a/src/gui/src/ActivationNotifier.h b/src/gui/src/ActivationNotifier.h new file mode 100644 index 000000000..d245cd278 --- /dev/null +++ b/src/gui/src/ActivationNotifier.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Synergy Seamless Inc. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ACTIVATIONNOTIFIER_H +#define ACTIVATIONNOTIFIER_H + +#include + +class ActivationNotifier : public QObject +{ +Q_OBJECT +public: + explicit ActivationNotifier(QObject *parent = 0); + + void setIdentity(QString identity); + +public slots: + void notify(); + +signals: + void finished(); + +private: + QString m_Identity; +}; + +#endif // ACTIVATIONNOTIFIER_H diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index e239b0159..a3a081fc2 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -18,6 +18,7 @@ #include "SetupWizard.h" #include "MainWindow.h" #include "WebClient.h" +#include "ActivationNotifier.h" #include "SubscriptionManager.h" #include "EditionType.h" #include "QSynergyApplication.h" @@ -200,22 +201,19 @@ void SetupWizard::accept() if (m_pRadioButtonActivate->isChecked()) { appConfig.setActivateEmail(m_pLineEditEmail->text()); - CoreInterface coreInterface; - coreInterface.notifyActivation("login:" + m_pLineEditEmail->text()); + notifyActivation("login:" + m_pLineEditEmail->text()); } if (m_pRadioButtonSubscription->isChecked()) { appConfig.setSerialKey(m_pLineEditSerialKey->text()); - CoreInterface coreInterface; - coreInterface.notifyActivation("serial:" + m_pLineEditSerialKey->text()); + notifyActivation("serial:" + m_pLineEditSerialKey->text()); } if (m_pRadioButtonSkip->isChecked()) { - CoreInterface coreInterface; - coreInterface.notifyActivation("skip:unknown"); + notifyActivation("skip:unknown"); } appConfig.setEdition(m_Edition); @@ -251,6 +249,21 @@ void SetupWizard::reject() QWizard::reject(); } +void SetupWizard::notifyActivation(QString identity) +{ + ActivationNotifier* notifier = new ActivationNotifier(); + notifier->setIdentity(identity); + QThread* thread = new QThread; + connect(notifier, SIGNAL(finished()), thread, SLOT(quit())); + connect(notifier, SIGNAL(finished()), notifier, SLOT(deleteLater())); + connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + + notifier->moveToThread(thread); + thread->start(); + + QMetaObject::invokeMethod(notifier, "notify", Qt::QueuedConnection); +} + void SetupWizard::on_m_pComboLanguage_currentIndexChanged(int index) { QString ietfCode = m_pComboLanguage->itemData(index).toString(); diff --git a/src/gui/src/SetupWizard.h b/src/gui/src/SetupWizard.h index 253ac97f1..328de0ee7 100644 --- a/src/gui/src/SetupWizard.h +++ b/src/gui/src/SetupWizard.h @@ -43,6 +43,7 @@ class SetupWizard : public QWizard, public Ui::SetupWizardBase void changeEvent(QEvent* event); void accept(); void reject(); + void notifyActivation(QString identity); private: MainWindow& m_MainWindow; From 1ccc258455bebc02c38cc358f6905c3874ad91e4 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 19 Oct 2015 11:18:08 -0700 Subject: [PATCH 133/572] Add DPI helper #5030 --- src/lib/synergy/DpiHelper.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++ src/lib/synergy/DpiHelper.h | 36 +++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/lib/synergy/DpiHelper.cpp create mode 100644 src/lib/synergy/DpiHelper.h diff --git a/src/lib/synergy/DpiHelper.cpp b/src/lib/synergy/DpiHelper.cpp new file mode 100644 index 000000000..7b8effe74 --- /dev/null +++ b/src/lib/synergy/DpiHelper.cpp @@ -0,0 +1,49 @@ +/* + * 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) { + return; + } + + size_t dpiTest1 = s_resolutionWidth * 100 / width; + size_t dpiTest2 = s_resolutionHeight * 100 / height; + + assert(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 new file mode 100644 index 000000000..8f37de966 --- /dev/null +++ b/src/lib/synergy/DpiHelper.h @@ -0,0 +1,36 @@ +/* + * 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 + +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; +}; From 66335cd6f8ea9d12a5a82389b8d5cc671299bbdb Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 19 Oct 2015 11:18:43 -0700 Subject: [PATCH 134/572] Pass resolution and center into core on Windows #5030 --- src/gui/src/MainWindow.cpp | 19 +++++++++++++++++++ src/lib/synergy/ArgParser.cpp | 14 ++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 99cd93360..76ed7ecd8 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -742,6 +742,25 @@ bool MainWindow::serverArgs(QStringList& args, QString& app) #endif 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); + #endif return true; } diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 92185b268..3580ab5a2 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -23,7 +23,9 @@ #include "synergy/ClientArgs.h" #include "synergy/ToolArgs.h" #include "synergy/ArgsBase.h" +#include "synergy/DpiHelper.h" #include "base/Log.h" +#include "base/String.h" ArgsBase* ArgParser::m_argsBase = NULL; @@ -56,6 +58,18 @@ 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 { LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, args.m_pname, argv[i], args.m_pname)); return false; From a09bfc5f07088f06e4c97f4f8f355036edbc8043 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 19 Oct 2015 11:20:56 -0700 Subject: [PATCH 135/572] Fix high DPI breaking edge detection and mouse delta calculation #5030 --- src/lib/platform/MSWindowsScreen.cpp | 72 +++++++++++++++++++++++++----------- src/lib/server/BaseClientProxy.h | 6 +++ src/lib/server/PrimaryClient.h | 2 +- src/lib/server/Server.cpp | 13 ++++++- 4 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 5473feb39..602e5390b 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -31,6 +31,7 @@ #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" @@ -143,6 +144,11 @@ MSWindowsScreen::MSWindowsScreen( this, &MSWindowsScreen::updateKeysCB), 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"); @@ -1347,6 +1353,14 @@ MSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam) bool MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) { + SInt32 originalMX = mx; + SInt32 originalMY = my; + + if (DpiHelper::s_dpiScaled) { + mx = (SInt32)(mx / DpiHelper::getDpi()); + my = (SInt32)(my / DpiHelper::getDpi()); + } + // compute motion delta (relative to the last known // mouse position) SInt32 x = mx - m_xCursor; @@ -1370,7 +1384,7 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) // motion on primary screen sendEvent( m_events->forIPrimaryScreen().motionOnPrimary(), - MotionInfo::alloc(m_xCursor, m_yCursor)); + MotionInfo::alloc(originalMX, originalMY)); if (m_buttons[kButtonLeft] == true && m_draggingStarted == false) { m_draggingStarted = true; @@ -1527,20 +1541,25 @@ MSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y) POINT cursorPos; GetCursorPos(&cursorPos); - if ((cursorPos.x != x) && (cursorPos.y != y)) { - LOG((CLOG_DEBUG "SetCursorPos did not work; using fakeMouseMove instead")); - - // when at Vista/7 login screen, SetCursorPos does not work (which could be - // an MS security feature). instead we can use fakeMouseMove, which calls - // mouse_event. - // IMPORTANT: as of implementing this function, it has an annoying side - // effect; instead of the mouse returning to the correct exit point, it - // returns to the center of the screen. this could have something to do with - // the center screen warping technique used (see comments for onMouseMove - // definition). - fakeMouseMove(x, y); + // there is a bug or round error in SetCursorPos and GetCursorPos on + // a high DPI setting. The check here is for Vista/7 login screen. + // since this feature is mainly for client, so only check on client. + if (!isPrimary()) { + if ((cursorPos.x != x) && (cursorPos.y != y)) { + LOG((CLOG_DEBUG "SetCursorPos did not work; using fakeMouseMove instead")); + LOG((CLOG_DEBUG "cursor pos %d, %d expected pos %d, %d", cursorPos.x, cursorPos.y, x, y)); + // when at Vista/7 login screen, SetCursorPos does not work (which could be + // an MS security feature). instead we can use fakeMouseMove, which calls + // mouse_event. + // IMPORTANT: as of implementing this function, it has an annoying side + // effect; instead of the mouse returning to the correct exit point, it + // returns to the center of the screen. this could have something to do with + // the center screen warping technique used (see comments for onMouseMove + // definition). + fakeMouseMove(x, y); + } } - + // yield the CPU. there's a race condition when warping: // a hardware mouse event occurs // the mouse hook is not called because that process doesn't have the CPU @@ -1582,16 +1601,25 @@ MSWindowsScreen::ignore() const void MSWindowsScreen::updateScreenShape() { - // get shape - m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); - m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); - m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); - m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); + // get shape and center + if (DpiHelper::s_dpiScaled) { + m_w = (SInt32)DpiHelper::s_resolutionWidth; + m_h = (SInt32)DpiHelper::s_resolutionHeight; + + m_xCenter = (SInt32)DpiHelper::s_primaryWidthCenter; + m_yCenter = (SInt32)DpiHelper::s_primaryHeightCenter; + } + else { + m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); + m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); - // get center for cursor - 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); + m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); // check for multiple monitors m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || m_h != GetSystemMetrics(SM_CYSCREEN)); diff --git a/src/lib/server/BaseClientProxy.h b/src/lib/server/BaseClientProxy.h index 146b635ae..746f47972 100644 --- a/src/lib/server/BaseClientProxy.h +++ b/src/lib/server/BaseClientProxy.h @@ -51,6 +51,12 @@ class BaseClientProxy : public IClient { */ void getJumpCursorPos(SInt32& x, SInt32& y) const; + //! Get cursor position + /*! + Return if this proxy is for client or primary. + */ + virtual bool isPrimary() const { return false; } + //@} // IScreen diff --git a/src/lib/server/PrimaryClient.h b/src/lib/server/PrimaryClient.h index a0934159c..61f206185 100644 --- a/src/lib/server/PrimaryClient.h +++ b/src/lib/server/PrimaryClient.h @@ -148,7 +148,7 @@ class PrimaryClient : public BaseClientProxy { virtual synergy::IStream* getStream() const { return NULL; } - + bool isPrimary() const { return true; } private: synergy::Screen* m_screen; bool m_clipboardDirty[kClipboardEnd]; diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 1baf61506..c8226720f 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -33,6 +33,7 @@ #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" @@ -2000,8 +2001,18 @@ Server::onMouseMoveSecondary(SInt32 dx, SInt32 dy) m_sendFileThread = NULL; } + 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, m_x, m_y, false); + switchScreen(newScreen, newX, newY, false); } else { // same screen. clamp mouse to edge. From c994b0a4f7d069494b0bf77b6e3c83e20c6d2573 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 19 Oct 2015 11:28:23 -0700 Subject: [PATCH 136/572] Make sure all variables are non zero #5030 --- src/lib/synergy/DpiHelper.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/synergy/DpiHelper.cpp b/src/lib/synergy/DpiHelper.cpp index 7b8effe74..6507979ea 100644 --- a/src/lib/synergy/DpiHelper.cpp +++ b/src/lib/synergy/DpiHelper.cpp @@ -29,7 +29,10 @@ size_t DpiHelper::s_primaryHeightCenter = 0; void DpiHelper::calculateDpi(size_t width, size_t height) { - if (s_resolutionWidth == 0 || s_resolutionHeight == 0) { + if (s_resolutionWidth == 0 || + s_resolutionHeight == 0 || + s_primaryWidthCenter == 0 || + s_primaryHeightCenter == 0) { return; } From 0cc831b2fb78d1f98e258dbcc310d862b0795be6 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 19 Oct 2015 11:28:33 -0700 Subject: [PATCH 137/572] Fix code style --- src/lib/server/Server.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index c8226720f..30e2e2ca9 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -2011,6 +2011,7 @@ Server::onMouseMoveSecondary(SInt32 dx, SInt32 dy) newY = (SInt32)(newY / DpiHelper::getDpi()); } } + // switch screens switchScreen(newScreen, newX, newY, false); } From c23d443a9a4cdd31b02946280b671f2443ad9200 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 19 Oct 2015 11:41:52 -0700 Subject: [PATCH 138/572] Add unit tests for DpiHelper --- src/test/unittests/synergy/DpiHelperTests.cpp | 70 +++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/test/unittests/synergy/DpiHelperTests.cpp diff --git a/src/test/unittests/synergy/DpiHelperTests.cpp b/src/test/unittests/synergy/DpiHelperTests.cpp new file mode 100644 index 000000000..775279984 --- /dev/null +++ b/src/test/unittests/synergy/DpiHelperTests.cpp @@ -0,0 +1,70 @@ +/* + * 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_EQ(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_EQ(true, DpiHelper::s_dpiScaled); + EXPECT_EQ(200, DpiHelper::s_dpi); +} + +TEST(DpiHelperTests, calculateDpi_defaultStaticValues_defaultDpi) +{ + resetStaticVariables(); + + DpiHelper::calculateDpi(1920, 1080); + + EXPECT_EQ(false, DpiHelper::s_dpiScaled); + EXPECT_EQ(DpiHelper::kDefaultDpi, DpiHelper::s_dpi); +} From 9fd210b3a542696806981ef205b78d018c0fdbc1 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Mon, 19 Oct 2015 11:51:12 -0700 Subject: [PATCH 139/572] Update git ignore list --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5aebb4958..fae40b009 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ config.h /bin /lib /build +/CMakeFiles /ext/cryptopp562 /ext/gmock-1.6.0 /ext/gtest-1.6.0 From af305ba3df181e2b123f460239dd934bfcefc52a Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Mon, 19 Oct 2015 12:25:51 -0700 Subject: [PATCH 140/572] Add size_t dependency #5030 --- src/lib/synergy/DpiHelper.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/synergy/DpiHelper.h b/src/lib/synergy/DpiHelper.h index 8f37de966..0488a4652 100644 --- a/src/lib/synergy/DpiHelper.h +++ b/src/lib/synergy/DpiHelper.h @@ -17,6 +17,8 @@ #pragma once +#include "common/common.h" + class DpiHelper { public: enum EDpi { From ebec92fd5b6b846b3fcf477a6d30f406706200f1 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Mon, 19 Oct 2015 12:26:28 -0700 Subject: [PATCH 141/572] Fix unit test failed compiling on Linux #5030 --- src/test/unittests/synergy/DpiHelperTests.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/unittests/synergy/DpiHelperTests.cpp b/src/test/unittests/synergy/DpiHelperTests.cpp index 775279984..9dee828ed 100644 --- a/src/test/unittests/synergy/DpiHelperTests.cpp +++ b/src/test/unittests/synergy/DpiHelperTests.cpp @@ -40,7 +40,7 @@ TEST(DpiHelperTests, calculateDpi_samePhysicalAndVirtualResolutions_defaultDpi) DpiHelper::calculateDpi(1920, 1080); - EXPECT_EQ(false, DpiHelper::s_dpiScaled); + EXPECT_FALSE(DpiHelper::s_dpiScaled); EXPECT_EQ(DpiHelper::kDefaultDpi, DpiHelper::s_dpi); } @@ -55,7 +55,7 @@ TEST(DpiHelperTests, calculateDpi_differentPhysicalAndVirtualResolutions_scaledD DpiHelper::calculateDpi(960, 540); - EXPECT_EQ(true, DpiHelper::s_dpiScaled); + EXPECT_TRUE(DpiHelper::s_dpiScaled); EXPECT_EQ(200, DpiHelper::s_dpi); } @@ -65,6 +65,6 @@ TEST(DpiHelperTests, calculateDpi_defaultStaticValues_defaultDpi) DpiHelper::calculateDpi(1920, 1080); - EXPECT_EQ(false, DpiHelper::s_dpiScaled); + EXPECT_FALSE(DpiHelper::s_dpiScaled); EXPECT_EQ(DpiHelper::kDefaultDpi, DpiHelper::s_dpi); } From 4f1f2dcff03cf1a534f6f0f979dea6580ea9ef83 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 19 Oct 2015 12:34:29 -0700 Subject: [PATCH 142/572] Use condition instead of assertion #5030 --- src/lib/synergy/DpiHelper.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/synergy/DpiHelper.cpp b/src/lib/synergy/DpiHelper.cpp index 6507979ea..67423cc54 100644 --- a/src/lib/synergy/DpiHelper.cpp +++ b/src/lib/synergy/DpiHelper.cpp @@ -39,14 +39,14 @@ void DpiHelper::calculateDpi(size_t width, size_t height) size_t dpiTest1 = s_resolutionWidth * 100 / width; size_t dpiTest2 = s_resolutionHeight * 100 / height; - assert(dpiTest1 == dpiTest2); + if (dpiTest1 == dpiTest2) { + s_dpi = dpiTest1; - s_dpi = dpiTest1; + if (s_dpi != kDefaultDpi) { + s_dpiScaled = true; - 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)); - } + LOG((CLOG_DEBUG "DPI: %d%%", s_dpi)); + LOG((CLOG_DEBUG "physical resolution: %d, %d scaled resolution: %d, %d", s_resolutionWidth, s_resolutionHeight, width, height)); + } + } } From c259af9b4152e0f8c3098993bba3761ca373c211 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 20 Oct 2015 14:54:51 -0700 Subject: [PATCH 143/572] Fix code style --- src/lib/platform/OSXClipboard.cpp | 6 +++--- src/lib/platform/OSXKeyState.cpp | 4 ++-- src/lib/platform/OSXScreen.cpp | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/lib/platform/OSXClipboard.cpp b/src/lib/platform/OSXClipboard.cpp index a3ffbbd12..b384f853c 100644 --- a/src/lib/platform/OSXClipboard.cpp +++ b/src/lib/platform/OSXClipboard.cpp @@ -100,13 +100,13 @@ OSXClipboard::add(EFormat format, const String & data) return; LOG((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format)); - if(format == IClipboard::kText) { + if (format == IClipboard::kText) { LOG((CLOG_DEBUG " format of data to be added to clipboard was kText")); } - else if(format == IClipboard::kBitmap) { + else if (format == IClipboard::kBitmap) { LOG((CLOG_DEBUG " format of data to be added to clipboard was kBitmap")); } - else if(format == IClipboard::kHTML) { + else if (format == IClipboard::kHTML) { LOG((CLOG_DEBUG " format of data to be added to clipboard was kHTML")); } diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 640daf61d..06cc0fda0 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -332,10 +332,10 @@ OSXKeyState::mapKeyFromEvent(KeyIDs& ids, // choose action UInt16 action; - if(eventKind==kCGEventKeyDown) { + if (eventKind==kCGEventKeyDown) { action = kUCKeyActionDown; } - else if(CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat)==1) { + else if (CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat)==1) { action = kUCKeyActionAutoKey; } else { diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index 0dde87e03..bdd0a4831 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -572,8 +572,8 @@ OSXScreen::fakeMouseButton(ButtonID id, bool press) // increase clickState (double click, triple click, etc) // This will allow for higher than triple click but the quartz documenation // does not specify that this should be limited to triple click - if(press) { - if((ARCH->time() - m_lastClickTime) <= clickTime && diff <= maxDiff){ + if (press) { + if ((ARCH->time() - m_lastClickTime) <= clickTime && diff <= maxDiff){ m_clickState++; } else { @@ -583,7 +583,7 @@ OSXScreen::fakeMouseButton(ButtonID id, bool press) m_lastClickTime = ARCH->time(); } - if(m_clickState == 1){ + if (m_clickState == 1){ m_lastSingleClickXCursor = m_xCursor; m_lastSingleClickYCursor = m_yCursor; } @@ -942,7 +942,7 @@ OSXScreen::leave() bool OSXScreen::setClipboard(ClipboardID, const IClipboard* src) { - if(src != NULL) { + if (src != NULL) { LOG((CLOG_DEBUG "setting clipboard")); Clipboard::copy(&m_pasteboard, src); } @@ -2017,7 +2017,7 @@ OSXScreen::handleCGInputEvent(CGEventTapProxy proxy, LOG((CLOG_WARN "unknown quartz event type: 0x%02x", type)); } - if(screen->m_isOnScreen) { + if (screen->m_isOnScreen) { return event; } else { return NULL; @@ -2143,7 +2143,7 @@ OSXScreen::waitForCarbonLoop() const double timeout = ARCH->time() + kCarbonLoopWaitTimeout; while (!m_carbonLoopReady->wait()) { - if(ARCH->time() > timeout) { + if (ARCH->time() > timeout) { LOG((CLOG_DEBUG "carbon loop not ready, waiting again")); timeout = ARCH->time() + kCarbonLoopWaitTimeout; } From df89859400bc0b732a24bdf64255508a9e57172a Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 9 Nov 2015 10:52:07 -0800 Subject: [PATCH 144/572] Fix indentation --- src/lib/arch/IArchString.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/arch/IArchString.h b/src/lib/arch/IArchString.h index 830171287..9d04f4a84 100644 --- a/src/lib/arch/IArchString.h +++ b/src/lib/arch/IArchString.h @@ -50,8 +50,8 @@ class IArchString : public IInterface { /*! This method is equivalent to vsprintf() except it will not write more than \c n bytes to the buffer, returning -1 if the output - was truncated and the number of bytes written not including the - trailing NUL otherwise. + was truncated and the number of bytes written not including the + trailing NUL otherwise. */ virtual int vsnprintf(char* str, int size, const char* fmt, va_list ap); From fa8a0ebbfe3ba7bf95f60d6875f7645c23518583 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 10 Nov 2015 15:01:41 -0800 Subject: [PATCH 145/572] Make center point be aware of DPI scaling #5030 --- src/lib/platform/MSWindowsScreen.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 602e5390b..25666adb1 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -347,7 +347,8 @@ MSWindowsScreen::leave() // warp to center LOG((CLOG_DEBUG1 "warping cursor to center: %+d, %+d", m_xCenter, m_yCenter)); - warpCursor(m_xCenter, m_yCenter); + float dpi = DpiHelper::getDpi(); + warpCursor(m_xCenter / dpi, m_yCenter / dpi); // disable special key sequences on win95 family enableSpecialKeys(false); @@ -568,7 +569,6 @@ MSWindowsScreen::warpCursor(SInt32 x, SInt32 y) } void MSWindowsScreen::saveMousePosition(SInt32 x, SInt32 y) { - m_xCursor = x; m_yCursor = y; @@ -1397,7 +1397,8 @@ 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)); - warpCursorNoFlush(m_xCenter, m_yCenter); + float dpi = DpiHelper::getDpi(); + warpCursorNoFlush(m_xCenter / dpi, m_yCenter / dpi); // examine the motion. if it's about the distance // from the center of the screen to an edge then @@ -1405,10 +1406,11 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) // ignore (see warpCursorNoFlush() for a further // description). static SInt32 bogusZoneSize = 10; - 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 "dpi: %f m_w: %d center: %d x: %d",dpi, m_w, m_xCenter, x)); + 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) { LOG((CLOG_DEBUG "dropped bogus delta motion: %+d,%+d", x, y)); } @@ -1603,9 +1605,11 @@ 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; + // calculate center position according to the original size m_xCenter = (SInt32)DpiHelper::s_primaryWidthCenter; m_yCenter = (SInt32)DpiHelper::s_primaryHeightCenter; } From 85088baceba39dc7bd04bbee46e77d50e2eb49e8 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 19 Nov 2015 11:49:30 -0800 Subject: [PATCH 146/572] Update ChangeLog --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 76285f2d7..a2274c822 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +v1.7.5-stable +============= +Bug #5030 - Display scaling breaks edge detection on Windows +Bug #5064 - Compile fails on Mac OS X 10.11 (unused typedef) + v1.7.4-stable ============= Bug #4721 - High CPU usage for Windows service From 5a03ece50b4eb7cb0b8f7146a4052df878ca530a Mon Sep 17 00:00:00 2001 From: j2gl Date: Fri, 20 Nov 2015 01:35:23 -0600 Subject: [PATCH 147/572] 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 953ad5bc4a33902c95545baf8170e30a40ffc18a Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 23 Nov 2015 10:03:44 -0800 Subject: [PATCH 148/572] Use live URL #4715 --- 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 048e51dd7..e623dd371 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -30,7 +30,7 @@ #include "platform/MSWindowsSession.h" #endif -#define JSON_URL "http://test.synergy-project.org/premium/json/" +#define JSON_URL "http://synergy-project.org/premium/json/" enum { kErrorOk, From 4f028d56f12cbe6566ef554431930120c09d841e Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 23 Nov 2015 10:04:21 -0800 Subject: [PATCH 149/572] Version to 1.8.0 beta --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 618902dc6..6ed1889e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 0) -set(VERSION_STAGE alpha) +set(VERSION_STAGE beta) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From 3bca8deac36dc75cac2fcbbee68b15f808bea7ac Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 23 Nov 2015 11:42:47 -0800 Subject: [PATCH 150/572] Use secure URL #4715 --- 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 e623dd371..cf8960893 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -30,7 +30,7 @@ #include "platform/MSWindowsSession.h" #endif -#define JSON_URL "http://synergy-project.org/premium/json/" +#define JSON_URL "https://synergy-project.org/premium/json/" enum { kErrorOk, From 8a2106a09e54e324831432fa57f6f1f30487f90e Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 24 Nov 2015 11:40:04 -0800 Subject: [PATCH 151/572] Revert "workaround for Fast cursor on any client with Mac server #451" This reverts commit 267f3ac41f7d6b9cd696b16eddb5bc9ae210b45c. --- src/lib/platform/OSXScreen.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index bdd0a4831..f7e5f5dbd 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -846,7 +846,6 @@ OSXScreen::disable() } if (m_eventTapPort) { - CGEventTapEnable(m_eventTapPort, false); CFRelease(m_eventTapPort); m_eventTapPort = nullptr; } From 622045b3965269ee593d451f249997f26dae61f6 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 24 Nov 2015 11:42:17 -0800 Subject: [PATCH 152/572] Revert "Revert "Revert "Fix missing DLLs after install #3774""" This reverts commit 3eb04d1b4c221b49e7d88b35378e709f92dfe74d. --- src/setup/win32/Product.wxs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/setup/win32/Product.wxs b/src/setup/win32/Product.wxs index c47aa0633..6cdac1b2c 100644 --- a/src/setup/win32/Product.wxs +++ b/src/setup/win32/Product.wxs @@ -112,13 +112,13 @@
- - - - - + + + + + - + From 539ac32136725799b01ca5cd98f6656ee073a883 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 24 Nov 2015 11:53:18 -0800 Subject: [PATCH 153/572] Revert "Fixed using wrong local variable #4723" This reverts commit d7063a87c871cd97d7e026bcf7c9cf3b895e9f55. --- src/lib/platform/MSWindowsWatchdog.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index 837db1de0..c57fb2781 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -552,12 +552,11 @@ MSWindowsWatchdog::getActiveDesktop(LPSECURITY_ATTRIBUTES security) ARCH->lockMutex(m_mutex); int waitTime = 0; while (!m_ready) { - if (waitTime >= MAXIMUM_WAIT_TIME) { break; } ARCH->waitCondVar(m_condVar, m_mutex, 1.0); - waitTime++; + i++; } m_ready = false; ARCH->unlockMutex(m_mutex); From 42f201785c81b8fdd833a56713b45338a78fa107 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 24 Nov 2015 11:53:38 -0800 Subject: [PATCH 154/572] Revert "Added timeout for waiting active destop result #4723" This reverts commit aec52c36e6e9c5961f2fee2e4eb68c60317bca48. --- src/lib/platform/MSWindowsWatchdog.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index c57fb2781..fdb2c72a7 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -37,7 +37,6 @@ #include #include -#define MAXIMUM_WAIT_TIME 3 enum { kOutputBufferSize = 4096 }; @@ -550,13 +549,8 @@ MSWindowsWatchdog::getActiveDesktop(LPSECURITY_ATTRIBUTES security) } ARCH->lockMutex(m_mutex); - int waitTime = 0; while (!m_ready) { - break; - } - ARCH->waitCondVar(m_condVar, m_mutex, 1.0); - i++; } m_ready = false; ARCH->unlockMutex(m_mutex); From 53906faf8fa1b273052b3dfbdbd59a128c7930bb Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 30 Nov 2015 11:36:49 -0800 Subject: [PATCH 155/572] Update changelog --- ChangeLog | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ChangeLog b/ChangeLog index a2274c822..8db751de1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +v1.8.0-beta +============= +Enhancement #4696 - Include 'ns' plugin in installers (instead of wizard download) +Enhancement #4715 - Activation dialog which also accepts a serial key +Enhancement #5020 - Recommend using serial key when online activation fails +Enhancement #4893 - Show detailed version info on GUI about screen +Enhancement #4327 - GUI setting to disable drag and drop feature +Enhancement #4793 - Additional logging to output OpenSSL version +Enhancement #4932 - Notify activation system when wizard finishes +Enhancement #4716 - Allow software to be time limited with serial key + v1.7.5-stable ============= Bug #5030 - Display scaling breaks edge detection on Windows From 0207b697e327a0f302d327a058ba7251e5fcf609 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 30 Nov 2015 12:01:00 -0800 Subject: [PATCH 156/572] Remove debug logging #5030 --- src/lib/platform/MSWindowsScreen.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 25666adb1..14c525b7f 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -1406,7 +1406,6 @@ MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my) // ignore (see warpCursorNoFlush() for a further // description). static SInt32 bogusZoneSize = 10; - LOG((CLOG_DEBUG "dpi: %f m_w: %d center: %d x: %d",dpi, m_w, m_xCenter, x)); if (-x + bogusZoneSize > (m_xCenter - m_x) / dpi || x + bogusZoneSize > (m_x + m_w - m_xCenter) / dpi || -y + bogusZoneSize > (m_yCenter - m_y) / dpi || From 9152c275229623ee5d4a0cd8b6fba9e857e0bdcf Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 30 Nov 2015 13:47:23 -0800 Subject: [PATCH 157/572] Update search pattern for src type #5136 --- ext/toolchain/commands1.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index da0af1aa7..22cfb83d0 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -1471,6 +1471,9 @@ def getDistributePlatformInfo(self, type): def getDistFilename(self, type): pattern = self.getVersionForFilename() + if (type == 'src'): + pattern += '-Source' + for filename in os.listdir(self.getBinDir('Release')): if re.search(pattern, filename): return filename From 9a82a13a9a7a0c7b2c3c23a462607c1e8d1fd79e Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 30 Nov 2015 14:17:18 -0800 Subject: [PATCH 158/572] Update changelog --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 8db751de1..1bf944dca 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ v1.8.0-beta ============= +Bug #5136 - Intermitently upload a package file as a source file Enhancement #4696 - Include 'ns' plugin in installers (instead of wizard download) Enhancement #4715 - Activation dialog which also accepts a serial key Enhancement #5020 - Recommend using serial key when online activation fails From dfd8f2599124f2a29fd6cf1ee42264b054a185b0 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 30 Nov 2015 15:29:07 -0800 Subject: [PATCH 159/572] Update changelog --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 1bf944dca..de744d552 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,6 @@ v1.8.0-beta ============= -Bug #5136 - Intermitently upload a package file as a source file +Bug #5136 - Intermittently upload a package file as a source file Enhancement #4696 - Include 'ns' plugin in installers (instead of wizard download) Enhancement #4715 - Activation dialog which also accepts a serial key Enhancement #5020 - Recommend using serial key when online activation fails From 097e210ad6c62eb594b28009eed5498cd9abb920 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 30 Nov 2015 16:21:29 -0800 Subject: [PATCH 160/572] Version to 1.8.1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ed1889e0..6dbad124a 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 0) +set(VERSION_REV 1) set(VERSION_STAGE beta) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") From 6b675300565c9305efde247d97e18d2f9c12dab3 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 30 Nov 2015 16:40:42 -0800 Subject: [PATCH 161/572] Version to 1.8.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dbad124a..6ed1889e0 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 1) +set(VERSION_REV 0) set(VERSION_STAGE beta) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") From a68a01ca0f8f5a5226313063e824497f13bba5bc Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Mon, 30 Nov 2015 16:44:17 -0800 Subject: [PATCH 162/572] Revert "Update search pattern for src type #5136" This reverts commit 9152c275229623ee5d4a0cd8b6fba9e857e0bdcf. --- ext/toolchain/commands1.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 22cfb83d0..da0af1aa7 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -1471,9 +1471,6 @@ def getDistributePlatformInfo(self, type): def getDistFilename(self, type): pattern = self.getVersionForFilename() - if (type == 'src'): - pattern += '-Source' - for filename in os.listdir(self.getBinDir('Release')): if re.search(pattern, filename): return filename From 4ff3cddecf3946241560a95d7d48eabdab38c16f Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Mon, 30 Nov 2015 16:45:53 -0800 Subject: [PATCH 163/572] Update changelog --- ChangeLog | 1 - 1 file changed, 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index de744d552..8db751de1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,5 @@ v1.8.0-beta ============= -Bug #5136 - Intermittently upload a package file as a source file Enhancement #4696 - Include 'ns' plugin in installers (instead of wizard download) Enhancement #4715 - Activation dialog which also accepts a serial key Enhancement #5020 - Recommend using serial key when online activation fails From 7a207b4a54bf0b9c0199cfc04f9d44497a0ee372 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 30 Nov 2015 18:03:52 -0800 Subject: [PATCH 164/572] Version to 1.8.1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ed1889e0..6dbad124a 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 0) +set(VERSION_REV 1) set(VERSION_STAGE beta) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") From 6c11de6a225cc9eb36e94d089002d1a38e74ae11 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Tue, 1 Sep 2015 17:39:25 -0700 Subject: [PATCH 165/572] Fix bug #4735 - don't leave() until fillClipboard()s all complete --- src/lib/client/Client.cpp | 66 ++++++++++++++++++++++++++++++++++------------- src/lib/client/Client.h | 9 +++++-- 2 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 33e2a6cab..62dbb22a9 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -21,7 +21,6 @@ #include "../plugin/ns/SecureSocket.h" #include "client/ServerProxy.h" #include "synergy/Screen.h" -#include "synergy/Clipboard.h" #include "synergy/FileChunk.h" #include "synergy/DropHelper.h" #include "synergy/PacketStreamFilter.h" @@ -75,7 +74,10 @@ Client::Client( m_socket(NULL), m_useSecureNetwork(false), m_args(args), - m_sendClipboardThread(NULL) + m_sendClipboardThread(NULL), + m_mutex(NULL), + m_condData(false), + m_condVar(NULL) { assert(m_socketFactory != NULL); assert(m_screen != NULL); @@ -107,6 +109,8 @@ Client::Client( LOG((CLOG_NOTE "crypto disabled because of ns plugin not available")); } } + m_mutex = new Mutex(); + m_condVar = new CondVar(m_mutex, m_condData); } Client::~Client() @@ -125,6 +129,8 @@ Client::~Client() cleanupConnecting(); cleanupConnection(); delete m_socketFactory; + delete m_condVar; + delete m_mutex; } void @@ -262,8 +268,6 @@ Client::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool) bool Client::leave() { - m_screen->leave(); - m_active = false; if (m_sendClipboardThread != NULL) { @@ -272,11 +276,17 @@ Client::leave() m_sendClipboardThread = NULL; } + m_condData = false; m_sendClipboardThread = new Thread( new TMethodJob( this, &Client::sendClipboardThread, NULL)); + // Bug #4735 - we can't leave() until fillClipboard()s all finish + while (!m_condData) + m_condVar->wait(); + + m_screen->leave(); if (!m_receivedFileData.empty()) { m_receivedFileData.clear(); @@ -382,9 +392,20 @@ Client::getName() const } void -Client::sendClipboard(ClipboardID id) +Client::fillClipboard(ClipboardID id, Clipboard *clipboard) +{ + assert(m_screen != NULL); + assert(m_server != NULL); + + if (clipboard->open(m_timeClipboard[id])) { + clipboard->close(); + } + m_screen->getClipboard(id, clipboard); +} + +void +Client::sendClipboard(ClipboardID id, Clipboard *clipboard) { - // note -- m_mutex must be locked on entry assert(m_screen != NULL); assert(m_server != NULL); @@ -392,26 +413,21 @@ Client::sendClipboard(ClipboardID id) // clipboard time before getting the data from the screen // as the screen may detect an unchanged clipboard and // avoid copying the data. - Clipboard clipboard; - if (clipboard.open(m_timeClipboard[id])) { - clipboard.close(); - } - m_screen->getClipboard(id, &clipboard); // check time if (m_timeClipboard[id] == 0 || - clipboard.getTime() != m_timeClipboard[id]) { + clipboard->getTime() != m_timeClipboard[id]) { // save new time - m_timeClipboard[id] = clipboard.getTime(); + m_timeClipboard[id] = clipboard->getTime(); // marshall the data - String data = clipboard.marshall(); + String data = clipboard->marshall(); // save and send data if different or not yet sent if (!m_sentClipboard[id] || data != m_dataClipboard[id]) { m_sentClipboard[id] = true; m_dataClipboard[id] = data; - m_server->onClipboardChanged(id, &clipboard); + m_server->onClipboardChanged(id, clipboard); } } } @@ -673,8 +689,10 @@ Client::handleClipboardGrabbed(const Event& event, void*) // if we're not the active screen then send the clipboard now, // otherwise we'll wait until we leave. + Clipboard clipboard; if (!m_active) { - sendClipboard(info->m_id); + fillClipboard(info->m_id, &clipboard); + sendClipboard(info->m_id, &clipboard); } } @@ -762,12 +780,24 @@ Client::onFileRecieveCompleted() } void -Client::sendClipboardThread(void*) +Client::sendClipboardThread(void * data) { + Clipboard clipboard[kClipboardEnd]; + // fill clipboards that we own and that have changed + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_ownClipboard[id]) { + fillClipboard(id, &clipboard[id]); + } + } + + // signal that fill is done + m_condData = true; + m_condVar->signal(); + // send clipboards that we own and that have changed for (ClipboardID id = 0; id < kClipboardEnd; ++id) { if (m_ownClipboard[id]) { - sendClipboard(id); + sendClipboard(id, &clipboard[id]); } } diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index 1e5d42a9e..dfb9faa23 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -20,12 +20,13 @@ #include "synergy/IClient.h" -#include "synergy/IClipboard.h" +#include "synergy/Clipboard.h" #include "synergy/DragInformation.h" #include "synergy/INode.h" #include "synergy/ClientArgs.h" #include "net/NetworkAddress.h" #include "base/EventTypes.h" +#include "mt/CondVar.h" class EventQueueTimer; namespace synergy { class Screen; } @@ -162,7 +163,8 @@ class Client : public IClient, public INode { virtual String getName() const; private: - void sendClipboard(ClipboardID); + void fillClipboard(ClipboardID, Clipboard*); + void sendClipboard(ClipboardID, Clipboard*); void sendEvent(Event::Type, void*); void sendConnectionFailedEvent(const char* msg); void sendFileChunk(const void* data); @@ -223,4 +225,7 @@ class Client : public IClient, public INode { bool m_useSecureNetwork; ClientArgs& m_args; Thread* m_sendClipboardThread; + Mutex* m_mutex; + bool m_condData; + CondVar* m_condVar; }; From 0d310a8464084f1bf37bf574e81df033f0ab0960 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Thu, 3 Sep 2015 12:09:35 -0700 Subject: [PATCH 166/572] Properly lock condVar, add timeout condition to prevent infinite loop when waiting for fillClipboard() to finish --- src/lib/client/Client.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 62dbb22a9..ac8191ae7 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -283,8 +283,16 @@ Client::leave() &Client::sendClipboardThread, NULL)); // Bug #4735 - we can't leave() until fillClipboard()s all finish - while (!m_condData) - m_condVar->wait(); + Stopwatch timer(true); + m_mutex->lock(); + while (!m_condData) { + m_condVar->wait(timer, 0.5); + if (timer.getTime()>0.5) { + LOG((CLOG_DEBUG "timed out waiting for clipboard fill")); + break; + } + } + m_mutex->unlock(); m_screen->leave(); @@ -791,8 +799,10 @@ Client::sendClipboardThread(void * data) } // signal that fill is done + m_mutex->lock(); m_condData = true; m_condVar->signal(); + m_mutex->unlock(); // send clipboards that we own and that have changed for (ClipboardID id = 0; id < kClipboardEnd; ++id) { From 1fde0f3e7183c1aa263d71c90ba3ece244885c0b Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Thu, 3 Sep 2015 12:23:42 -0700 Subject: [PATCH 167/572] Cosmetic whitespace, use return value of wait() directly instead of testing timer --- src/lib/client/Client.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index ac8191ae7..41b1df24a 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -286,11 +286,10 @@ Client::leave() Stopwatch timer(true); m_mutex->lock(); while (!m_condData) { - m_condVar->wait(timer, 0.5); - if (timer.getTime()>0.5) { - LOG((CLOG_DEBUG "timed out waiting for clipboard fill")); - break; - } + if (!m_condVar->wait(timer, 0.5)) { + LOG((CLOG_DEBUG "timed out waiting for clipboard fill")); + break; + } } m_mutex->unlock(); From 8f0530c50755cbd860c92039ac22396d6ed56a24 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Fri, 11 Sep 2015 10:42:01 -0700 Subject: [PATCH 168/572] Add retry to CondVarBase wait(), make sure Stopwatch is started on construction (Issue #4735) * ArchMultithreadPosix::waitCondVar() returns every 100ms, so retry until we hit timeout. * Stopwatch constructor should be called with "false" (not "true") to make sure Stopwatch is actually running when instantiated. --- src/lib/client/Client.cpp | 9 +++++++-- src/lib/mt/CondVar.cpp | 14 ++++++++------ src/lib/platform/XWindowsClipboard.cpp | 5 +++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 41b1df24a..330488077 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -283,13 +283,15 @@ Client::leave() &Client::sendClipboardThread, NULL)); // Bug #4735 - we can't leave() until fillClipboard()s all finish - Stopwatch timer(true); + Stopwatch timer(false); m_mutex->lock(); while (!m_condData) { if (!m_condVar->wait(timer, 0.5)) { - LOG((CLOG_DEBUG "timed out waiting for clipboard fill")); + LOG((CLOG_WARN "timed out %fs waiting for clipboard fill", + (double) timer.getTime())); break; } + LOG((CLOG_DEBUG1 "leave %fs elapsed", (double) timer.getTime())); } m_mutex->unlock(); @@ -789,6 +791,7 @@ Client::onFileRecieveCompleted() void Client::sendClipboardThread(void * data) { + Stopwatch timer(false); Clipboard clipboard[kClipboardEnd]; // fill clipboards that we own and that have changed for (ClipboardID id = 0; id < kClipboardEnd; ++id) { @@ -796,6 +799,7 @@ Client::sendClipboardThread(void * data) fillClipboard(id, &clipboard[id]); } } + LOG((CLOG_DEBUG1 "fill took %fs, signaling", (double) timer.getTime())); // signal that fill is done m_mutex->lock(); @@ -811,6 +815,7 @@ Client::sendClipboardThread(void * data) } m_sendClipboardThread = NULL; + LOG((CLOG_DEBUG1 "send took %fs", (double) timer.getTime())); } void diff --git a/src/lib/mt/CondVar.cpp b/src/lib/mt/CondVar.cpp index 2ec18e467..caa122104 100644 --- a/src/lib/mt/CondVar.cpp +++ b/src/lib/mt/CondVar.cpp @@ -63,13 +63,15 @@ CondVarBase::broadcast() bool CondVarBase::wait(Stopwatch& timer, double timeout) const { - // check timeout against timer - if (timeout >= 0.0) { - timeout -= timer.getTime(); - if (timeout < 0.0) - return false; + double remain = timeout-timer.getTime(); + // Some ARCH wait()s return prematurely, retry until really timed out + // In particular, ArchMultithreadPosix::waitCondVar() returns every 100ms + while (remain >= 0.0) { + if (wait(remain)) + return true; + remain = timeout - timer.getTime(); } - return wait(timeout); + return false; } bool diff --git a/src/lib/platform/XWindowsClipboard.cpp b/src/lib/platform/XWindowsClipboard.cpp index 7db899f17..ef5877396 100644 --- a/src/lib/platform/XWindowsClipboard.cpp +++ b/src/lib/platform/XWindowsClipboard.cpp @@ -513,6 +513,7 @@ XWindowsClipboard::icccmFillCache() LOG((CLOG_DEBUG1 "selection doesn't support TARGETS")); data = ""; XWindowsUtil::appendAtomData(data, XA_STRING); + // return; // NTL } XWindowsUtil::convertAtomProperty(data); @@ -1317,7 +1318,7 @@ XWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, // by badly behaved selection owners. XEvent xevent; std::vector events; - Stopwatch timeout(true); + Stopwatch timeout(false); // timer not stopped, not triggered static const double s_timeout = 0.25; // FIXME -- is this too short? bool noWait = false; while (!m_done && !m_failed) { @@ -1361,7 +1362,7 @@ XWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display, XSelectInput(display, m_requestor, attr.your_event_mask); // return success or failure - LOG((CLOG_DEBUG1 "request %s", m_failed ? "failed" : "succeeded")); + LOG((CLOG_DEBUG1 "request %s after %fs", m_failed ? "failed" : "succeeded", timeout.getTime())); return !m_failed; } From b85b9125ead03055552c7500b9bbba279ddb1a39 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Fri, 11 Sep 2015 11:57:38 -0700 Subject: [PATCH 169/572] Remove inadvertent additions --- src/lib/platform/XWindowsClipboard.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/platform/XWindowsClipboard.cpp b/src/lib/platform/XWindowsClipboard.cpp index ef5877396..3df7f4dbf 100644 --- a/src/lib/platform/XWindowsClipboard.cpp +++ b/src/lib/platform/XWindowsClipboard.cpp @@ -513,7 +513,6 @@ XWindowsClipboard::icccmFillCache() LOG((CLOG_DEBUG1 "selection doesn't support TARGETS")); data = ""; XWindowsUtil::appendAtomData(data, XA_STRING); - // return; // NTL } XWindowsUtil::convertAtomProperty(data); From 48069f1a3b93bdc04d508f1d63ef5da3665e9a93 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Fri, 11 Sep 2015 12:24:23 -0700 Subject: [PATCH 170/572] Always call wait() at least once even if timeout is 0 to prevent deadlocks --- src/lib/mt/CondVar.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib/mt/CondVar.cpp b/src/lib/mt/CondVar.cpp index caa122104..751114102 100644 --- a/src/lib/mt/CondVar.cpp +++ b/src/lib/mt/CondVar.cpp @@ -66,11 +66,15 @@ CondVarBase::wait(Stopwatch& timer, double timeout) const double remain = timeout-timer.getTime(); // Some ARCH wait()s return prematurely, retry until really timed out // In particular, ArchMultithreadPosix::waitCondVar() returns every 100ms - while (remain >= 0.0) { + do { + // Always call wait at least once, even if remain is 0, to give + // other thread a chance to grab the mutex to avoid deadlocks on + // busy waiting. + if (remain<0.0) remain=0.0; if (wait(remain)) return true; remain = timeout - timer.getTime(); - } + } while (remain >= 0.0); return false; } From 3e37518b927974db6cd2e3175175ed9717cd0e23 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Thu, 1 Oct 2015 22:30:35 -0700 Subject: [PATCH 171/572] Workaround for Issue #5041 - prevent synergys from shutting down when screen saver activates When it is restarted, and screen saver is active, the clipboard no longer works correctly. The desk swap detection routine just below does not work properly when the desktop goes from screensaver back to normal. If it did, probably this workaround would not be needed. --- src/lib/platform/MSWindowsDesks.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index c88801d8d..98fcbb7dd 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -853,7 +853,10 @@ MSWindowsDesks::checkDesk() // if we are told to shut down on desk switch, and this is not the // first switch, then shut down. - if (m_stopOnDeskSwitch && m_activeDesk != NULL && name != m_activeDeskName) { + // Issue #5041 workaround - prevent synergys from shutting down when screen + // saver activates - It does not come back cleanly. + if (m_stopOnDeskSwitch && m_activeDesk != NULL && name != m_activeDeskName + && !m_screensaver->isActive()) { LOG((CLOG_DEBUG "shutting down because of desk switch to \"%s\"", name.c_str())); m_events->addEvent(Event(Event::kQuit)); return; From 1fc28392d080a23f622c934ab7c06f041b566a29 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Fri, 2 Oct 2015 10:02:07 -0700 Subject: [PATCH 172/572] Add error message to help debug Issue #5041 --- src/lib/platform/MSWindowsClipboard.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/platform/MSWindowsClipboard.cpp b/src/lib/platform/MSWindowsClipboard.cpp index 6c8794b13..bb7c87cbc 100644 --- a/src/lib/platform/MSWindowsClipboard.cpp +++ b/src/lib/platform/MSWindowsClipboard.cpp @@ -182,6 +182,7 @@ MSWindowsClipboard::get(EFormat format) const // if no converter then we don't recognize any formats if (converter == NULL) { + LOG((CLOG_WARN "No converter for format %d", format)); return String(); } From 9036d42469d792b5ace6bda1e70b3fbede97c56e Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Wed, 7 Oct 2015 21:23:26 -0700 Subject: [PATCH 173/572] Issue #5041 - properly update activeDesk and activeDeskName when screen saver activates Ensure that we actually kill the server if we go from screen saver desk to real desk. --- src/lib/platform/MSWindowsDesks.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index 98fcbb7dd..775b1396a 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -851,15 +851,22 @@ MSWindowsDesks::checkDesk() desk = index->second; } - // if we are told to shut down on desk switch, and this is not the + // if we are told to shut down on desk switch, and this is not the // first switch, then shut down. // Issue #5041 workaround - prevent synergys from shutting down when screen - // saver activates - It does not come back cleanly. - if (m_stopOnDeskSwitch && m_activeDesk != NULL && name != m_activeDeskName - && !m_screensaver->isActive()) { - LOG((CLOG_DEBUG "shutting down because of desk switch to \"%s\"", name.c_str())); - m_events->addEvent(Event(Event::kQuit)); - return; + // saver activates - if it is restarted while the screen saver is active, + // the clipboard no longer works. + if (m_stopOnDeskSwitch && m_activeDesk != NULL && name != m_activeDeskName) { + if (!m_screensaver->isActive()) { + LOG((CLOG_DEBUG "shutting down because of desk switch \"%s\"->\"%s\"", + m_activeDeskName.c_str(), name.c_str())); + m_events->addEvent(Event(Event::kQuit)); + return; + } + LOG((CLOG_DEBUG "screen saver active, ignoring desk switch \"%s\"->\"%s\"", + m_activeDeskName.c_str(), name.c_str())); + m_activeDesk = desk; + m_activeDeskName = name; } // if active desktop changed then tell the old and new desk threads From 425fc8b925e1a22f5f6b0ec632c76b393a6c38c3 Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Wed, 7 Oct 2015 22:01:16 -0700 Subject: [PATCH 174/572] Refactor Issue #5041 workaround to make sure we still send the SCREENSAVER message --- src/lib/platform/MSWindowsDesks.cpp | 47 +++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index 775b1396a..6408b560c 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -851,30 +851,24 @@ MSWindowsDesks::checkDesk() desk = index->second; } - // if we are told to shut down on desk switch, and this is not the - // first switch, then shut down. - // Issue #5041 workaround - prevent synergys from shutting down when screen - // saver activates - if it is restarted while the screen saver is active, - // the clipboard no longer works. - if (m_stopOnDeskSwitch && m_activeDesk != NULL && name != m_activeDeskName) { - if (!m_screensaver->isActive()) { - LOG((CLOG_DEBUG "shutting down because of desk switch \"%s\"->\"%s\"", - m_activeDeskName.c_str(), name.c_str())); - m_events->addEvent(Event(Event::kQuit)); - return; - } - LOG((CLOG_DEBUG "screen saver active, ignoring desk switch \"%s\"->\"%s\"", - m_activeDeskName.c_str(), name.c_str())); - m_activeDesk = desk; - m_activeDeskName = name; - } - // if active desktop changed then tell the old and new desk threads // about the change. don't switch desktops when the screensaver is // active becaue we'd most likely switch to the screensaver desktop // which would have the side effect of forcing the screensaver to // stop. if (name != m_activeDeskName && !m_screensaver->isActive()) { + // if we are told to shut down on desk switch, and this is not the + // first switch, then shut down. + // Issue #5041 workaround - prevent synergys from shutting down when + // screen saver activates - if it is restarted while the screen saver + // is active, the clipboard no longer works. + if (m_stopOnDeskSwitch && m_activeDesk != NULL) { + LOG((CLOG_DEBUG "shutting down because of desk switch \"%s\"->\"%s\"", + m_activeDeskName.c_str(), name.c_str())); + m_events->addEvent(Event(Event::kQuit)); + return; + } + // show cursor on previous desk bool wasOnScreen = m_isOnScreen; if (!wasOnScreen) { @@ -885,7 +879,10 @@ MSWindowsDesks::checkDesk() // from an inaccessible desktop so when we switch from an // inaccessible desktop to an accessible one we have to // update the keyboard state. - LOG((CLOG_DEBUG "switched to desk \"%s\"", name.c_str())); + if (m_activeDesk != NULL) { + LOG((CLOG_DEBUG "switched desk \"%s\"->\"%s\"", + m_activeDeskName.c_str(), name.c_str())); + } bool syncKeys = false; bool isAccessible = isDeskAccessible(desk); if (isDeskAccessible(m_activeDesk) != isAccessible) { @@ -914,8 +911,18 @@ MSWindowsDesks::checkDesk() } } else if (name != m_activeDeskName) { - // screen saver might have started + if (m_activeDesk != NULL) { + LOG((CLOG_DEBUG "switched desk \"%s\"->\"%s\"", + m_activeDeskName.c_str(), name.c_str())); + } + + // screen saver is active (see check above) PostThreadMessage(m_threadID, SYNERGY_MSG_SCREEN_SAVER, TRUE, 0); + + // Prevent this from retriggering over and over if screen saver stays + // active: + m_activeDesk = desk; + m_activeDeskName = name; } } From bab2e985a3f9f911a7034c01b4ede67232e3a20d Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Mon, 12 Oct 2015 17:09:52 -0700 Subject: [PATCH 175/572] Revert to old behavior of checkDesk(), add workaround to broken EnumClipboardFormats() in the case where the server is started while the screen saver is active. --- src/lib/platform/MSWindowsClipboard.cpp | 19 +++++++++-- src/lib/platform/MSWindowsDesks.cpp | 56 ++++++++++++++++----------------- 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/src/lib/platform/MSWindowsClipboard.cpp b/src/lib/platform/MSWindowsClipboard.cpp index bb7c87cbc..07831a51e 100644 --- a/src/lib/platform/MSWindowsClipboard.cpp +++ b/src/lib/platform/MSWindowsClipboard.cpp @@ -180,9 +180,24 @@ MSWindowsClipboard::get(EFormat format) const win32Format = EnumClipboardFormats(win32Format); } - // if no converter then we don't recognize any formats + // if no converter then EnumClipboardFormats() is broken: try just + // GetClipboardData() directly (Issue $5041) if (converter == NULL) { - LOG((CLOG_WARN "No converter for format %d", format)); + LOG((CLOG_INFO "Broken EnumClipboardFormats, falling back to using GetClipboardData")); + + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + converter = *index; + if (converter->getFormat() == format) { + LOG((CLOG_DEBUG "using converter 0x%x%s for %d\n", + converter->getWin32Format(), + l_name(converter->getWin32Format()).c_str(), + format)); + HANDLE win32Data = GetClipboardData(converter->getWin32Format()); + if (win32Data != NULL) + return converter->toIClipboard(win32Data); + } + } return String(); } diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index 6408b560c..e0a140ffd 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -851,24 +851,30 @@ MSWindowsDesks::checkDesk() desk = index->second; } + if (name == m_activeDeskName) { + return; + } + + if (m_activeDesk != NULL) { + LOG((CLOG_DEBUG "switched desk \"%s\"->\"%s\"", + m_activeDeskName.c_str(), name.c_str())); + } + + // if we are told to shut down on desk switch, and this is not the + // first switch, then shut down. + if (m_stopOnDeskSwitch && m_activeDesk != NULL) { + LOG((CLOG_DEBUG "shutting down because of desk switch \"%s\"->\"%s\"", + m_activeDeskName.c_str(), name.c_str())); + m_events->addEvent(Event(Event::kQuit)); + return; + } + // if active desktop changed then tell the old and new desk threads // about the change. don't switch desktops when the screensaver is // active becaue we'd most likely switch to the screensaver desktop // which would have the side effect of forcing the screensaver to // stop. - if (name != m_activeDeskName && !m_screensaver->isActive()) { - // if we are told to shut down on desk switch, and this is not the - // first switch, then shut down. - // Issue #5041 workaround - prevent synergys from shutting down when - // screen saver activates - if it is restarted while the screen saver - // is active, the clipboard no longer works. - if (m_stopOnDeskSwitch && m_activeDesk != NULL) { - LOG((CLOG_DEBUG "shutting down because of desk switch \"%s\"->\"%s\"", - m_activeDeskName.c_str(), name.c_str())); - m_events->addEvent(Event(Event::kQuit)); - return; - } - + if (!m_screensaver->isActive()) { // show cursor on previous desk bool wasOnScreen = m_isOnScreen; if (!wasOnScreen) { @@ -879,10 +885,6 @@ MSWindowsDesks::checkDesk() // from an inaccessible desktop so when we switch from an // inaccessible desktop to an accessible one we have to // update the keyboard state. - if (m_activeDesk != NULL) { - LOG((CLOG_DEBUG "switched desk \"%s\"->\"%s\"", - m_activeDeskName.c_str(), name.c_str())); - } bool syncKeys = false; bool isAccessible = isDeskAccessible(desk); if (isDeskAccessible(m_activeDesk) != isAccessible) { @@ -910,19 +912,17 @@ MSWindowsDesks::checkDesk() updateKeys(); } } - else if (name != m_activeDeskName) { - if (m_activeDesk != NULL) { - LOG((CLOG_DEBUG "switched desk \"%s\"->\"%s\"", - m_activeDeskName.c_str(), name.c_str())); - } - + else { // screen saver is active (see check above) PostThreadMessage(m_threadID, SYNERGY_MSG_SCREEN_SAVER, TRUE, 0); - - // Prevent this from retriggering over and over if screen saver stays - // active: - m_activeDesk = desk; - m_activeDeskName = name; + // FIXME? Because m_activeDesk isn't updated if screen saver is active, + // SYNERGY_MSG_SCREEN_SAVER will be sent on every call to checkDesk() + // until isActive() returns false, since name will not match + // m_activeDeskName until the latter is updated. + + // For now, preserve old (broken?) behavior. + //m_activeDesk = desk; + //m_activeDeskName = name; } } From 6f904e55f1a1ac02c27c438c1f96796ed6c19a2b Mon Sep 17 00:00:00 2001 From: Nye Liu Date: Mon, 12 Oct 2015 17:15:29 -0700 Subject: [PATCH 176/572] Minor cosmetic change - missing some debug stuff in this branch. --- src/lib/platform/MSWindowsClipboard.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/platform/MSWindowsClipboard.cpp b/src/lib/platform/MSWindowsClipboard.cpp index 07831a51e..d946e9306 100644 --- a/src/lib/platform/MSWindowsClipboard.cpp +++ b/src/lib/platform/MSWindowsClipboard.cpp @@ -189,9 +189,8 @@ MSWindowsClipboard::get(EFormat format) const index != m_converters.end(); ++index) { converter = *index; if (converter->getFormat() == format) { - LOG((CLOG_DEBUG "using converter 0x%x%s for %d\n", + LOG((CLOG_DEBUG "using converter 0x%x for %d\n", converter->getWin32Format(), - l_name(converter->getWin32Format()).c_str(), format)); HANDLE win32Data = GetClipboardData(converter->getWin32Format()); if (win32Data != NULL) From 2fb36c4f860535594470c16dcbc082fbbff19258 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 24 Nov 2015 11:22:24 -0800 Subject: [PATCH 177/572] Revert "Revert "workaround for Fast cursor on any client with Mac server #451"" This reverts commit 6470240379c896ee13ee98b9a8c951d22f4351c1. --- src/lib/platform/OSXScreen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index f7e5f5dbd..bdd0a4831 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -846,6 +846,7 @@ OSXScreen::disable() } if (m_eventTapPort) { + CGEventTapEnable(m_eventTapPort, false); CFRelease(m_eventTapPort); m_eventTapPort = nullptr; } From eb36db6c2690f21b11dbb0eb3f4e0a832159bedd Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 24 Nov 2015 11:43:14 -0800 Subject: [PATCH 178/572] Revert "Revert "Fix missing DLLs after install #3774"" This reverts commit b575d7ca35d51d1c28ffb84b19fdf96fc941fe41. --- src/setup/win32/Product.wxs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/setup/win32/Product.wxs b/src/setup/win32/Product.wxs index 6cdac1b2c..c47aa0633 100644 --- a/src/setup/win32/Product.wxs +++ b/src/setup/win32/Product.wxs @@ -112,13 +112,13 @@ - - - - - + + + + + - + From 5fae9efe78513306edcfd3a0d9cafa39fd686584 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 24 Nov 2015 11:51:00 -0800 Subject: [PATCH 179/572] Revert "Revert "Added timeout for waiting active destop result #4723"" This reverts commit 0f8a5687719605af59d901f08dfbb5dde74a7dc9. --- src/lib/platform/MSWindowsWatchdog.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index fdb2c72a7..c57fb2781 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -37,6 +37,7 @@ #include #include +#define MAXIMUM_WAIT_TIME 3 enum { kOutputBufferSize = 4096 }; @@ -549,8 +550,13 @@ MSWindowsWatchdog::getActiveDesktop(LPSECURITY_ATTRIBUTES security) } ARCH->lockMutex(m_mutex); + int waitTime = 0; while (!m_ready) { + break; + } + ARCH->waitCondVar(m_condVar, m_mutex, 1.0); + i++; } m_ready = false; ARCH->unlockMutex(m_mutex); From ee20d08080734c06846e2a1d38d41ec0d3f908e7 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 24 Nov 2015 11:51:19 -0800 Subject: [PATCH 180/572] Revert "Revert "Fixed using wrong local variable #4723"" This reverts commit a0beae0f4b5578a16178e6324f2d1f588caeb442. --- src/lib/platform/MSWindowsWatchdog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index c57fb2781..837db1de0 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -552,11 +552,12 @@ MSWindowsWatchdog::getActiveDesktop(LPSECURITY_ATTRIBUTES security) ARCH->lockMutex(m_mutex); int waitTime = 0; while (!m_ready) { + if (waitTime >= MAXIMUM_WAIT_TIME) { break; } ARCH->waitCondVar(m_condVar, m_mutex, 1.0); - i++; + waitTime++; } m_ready = false; ARCH->unlockMutex(m_mutex); From 3067560f7bc7fda30dd002ade969bacb368b1292 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 30 Nov 2015 10:57:12 -0800 Subject: [PATCH 181/572] Fix indentations --- src/lib/server/Server.cpp | 8 ++++---- src/lib/synergy/DpiHelper.cpp | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 30e2e2ca9..3c9114e4f 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -516,10 +516,10 @@ Server::switchScreen(BaseClientProxy* dst, // send the clipboard data to new active screen m_sendClipboardThread = new Thread( - new TMethodJob( - this, - &Server::sendClipboardThread, - NULL)); + new TMethodJob( + this, + &Server::sendClipboardThread, + NULL)); Server::SwitchToScreenInfo* info = Server::SwitchToScreenInfo::alloc(m_active->getName()); diff --git a/src/lib/synergy/DpiHelper.cpp b/src/lib/synergy/DpiHelper.cpp index 67423cc54..2f2ffcb70 100644 --- a/src/lib/synergy/DpiHelper.cpp +++ b/src/lib/synergy/DpiHelper.cpp @@ -46,7 +46,8 @@ void DpiHelper::calculateDpi(size_t width, size_t height) 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)); + LOG((CLOG_DEBUG "physical resolution: %d, %d scaled resolution: %d, %d", + s_resolutionWidth, s_resolutionHeight, width, height)); } } } From 6d0f820db5f64114ef75b8579acbe3de8d04a601 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 30 Nov 2015 12:22:11 -0800 Subject: [PATCH 182/572] Fix indentations --- src/lib/client/Client.h | 2 +- src/lib/platform/MSWindowsScreen.h | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index dfb9faa23..8f2f41305 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -227,5 +227,5 @@ class Client : public IClient, public INode { Thread* m_sendClipboardThread; Mutex* m_mutex; bool m_condData; - CondVar* m_condVar; + CondVar* m_condVar; }; diff --git a/src/lib/platform/MSWindowsScreen.h b/src/lib/platform/MSWindowsScreen.h index 54af770c7..f42e7025c 100644 --- a/src/lib/platform/MSWindowsScreen.h +++ b/src/lib/platform/MSWindowsScreen.h @@ -283,9 +283,10 @@ class MSWindowsScreen : public PlatformScreen { HKL m_keyLayout; // screen saver stuff - MSWindowsScreenSaver* m_screensaver; - bool m_screensaverNotify; - bool m_screensaverActive; + MSWindowsScreenSaver* + m_screensaver; + bool m_screensaverNotify; + bool m_screensaverActive; // clipboard stuff. our window is used mainly as a clipboard // owner and as a link in the clipboard viewer chain. From 221a9a71c42d4a506708c3ce3303abfeb6d30bea Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 30 Nov 2015 12:22:44 -0800 Subject: [PATCH 183/572] Add comment #4827 --- src/lib/synergy/IClipboard.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib/synergy/IClipboard.cpp b/src/lib/synergy/IClipboard.cpp index 4af3802fa..bd6602c18 100644 --- a/src/lib/synergy/IClipboard.cpp +++ b/src/lib/synergy/IClipboard.cpp @@ -66,6 +66,13 @@ IClipboard::unmarshall(IClipboard* clipboard, const String& data, Time time) String IClipboard::marshall(const IClipboard* clipboard) { + // return data format: + // 4 bytes => number of formats included + // 4 bytes => format enum + // 4 bytes => clipboard data size n + // n bytes => clipboard data + // back to the second 4 bytes if there is another format + assert(clipboard != NULL); String data; From 4344b16a201929755880d3fdac96dff06d427e55 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 2 Dec 2015 10:28:11 -0800 Subject: [PATCH 184/572] Remove some hack code #2909 --- src/lib/platform/OSXClipboard.cpp | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/lib/platform/OSXClipboard.cpp b/src/lib/platform/OSXClipboard.cpp index b384f853c..5b7ebb098 100644 --- a/src/lib/platform/OSXClipboard.cpp +++ b/src/lib/platform/OSXClipboard.cpp @@ -95,7 +95,6 @@ OSXClipboard::synchronize() void OSXClipboard::add(EFormat format, const String & data) { - bool emptied = false; if (m_pboard == NULL) return; @@ -121,24 +120,16 @@ OSXClipboard::add(EFormat format, const String & data) CFStringRef flavorType = converter->getOSXFormat(); CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, (UInt8 *)osXData.data(), osXData.size()); - // integ tests showed that if you call add(...) twice, then the - // second call will actually fail to set clipboard data. calling - // empty() seems to solve this problem. but, only clear the clipboard - // for the first converter, otherwise further converters will wipe out - // what we just added. - if (!emptied) { - empty(); - emptied = true; - } - - PasteboardPutItemFlavor( - m_pboard, - (PasteboardItemID) 0, - flavorType, - dataRef, - kPasteboardFlavorNoFlags); - LOG((CLOG_DEBUG "added %d bytes to clipboard format: %d", data.size(), format)); - } + PasteboardPutItemFlavor( + m_pboard, + (PasteboardItemID) 0, + flavorType, + dataRef, + kPasteboardFlavorNoFlags); + + LOG((CLOG_DEBUG "added %d bytes to clipboard format: %d", data.size(), format)); + } + } } From 89eb64a0aa087d28b452b18f87934392cc2974c2 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 2 Dec 2015 10:30:20 -0800 Subject: [PATCH 185/572] Reorder clipboard format #2909 --- src/lib/synergy/IClipboard.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/synergy/IClipboard.h b/src/lib/synergy/IClipboard.h index 252be3bdb..17cfd58eb 100644 --- a/src/lib/synergy/IClipboard.h +++ b/src/lib/synergy/IClipboard.h @@ -57,8 +57,8 @@ class IClipboard : public IInterface { */ enum EFormat { kText, //!< Text format, UTF-8, newline is LF + kHTML, //!< HTML format, HTML fragment, UTF-8, newline is LF kBitmap, //!< Bitmap format, BMP 24/32bpp, BI_RGB - kHTML, //!< HTML format, HTML fragment, UTF-8, newline is LF kNumFormats //!< The number of clipboard formats }; From 42038ce24ed184a0a0f5fd5205b7f805c6c524d2 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 2 Dec 2015 15:02:05 -0800 Subject: [PATCH 186/572] Revert "Minor cosmetic change - missing some debug stuff in this branch." This reverts commit bd660fedbfbff2df1bf5921826dd7717c2839161. --- src/lib/platform/MSWindowsClipboard.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/platform/MSWindowsClipboard.cpp b/src/lib/platform/MSWindowsClipboard.cpp index d946e9306..07831a51e 100644 --- a/src/lib/platform/MSWindowsClipboard.cpp +++ b/src/lib/platform/MSWindowsClipboard.cpp @@ -189,8 +189,9 @@ MSWindowsClipboard::get(EFormat format) const index != m_converters.end(); ++index) { converter = *index; if (converter->getFormat() == format) { - LOG((CLOG_DEBUG "using converter 0x%x for %d\n", + LOG((CLOG_DEBUG "using converter 0x%x%s for %d\n", converter->getWin32Format(), + l_name(converter->getWin32Format()).c_str(), format)); HANDLE win32Data = GetClipboardData(converter->getWin32Format()); if (win32Data != NULL) From 4d19941428870d399e14d3a0faadc6f814f19831 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 2 Dec 2015 15:02:19 -0800 Subject: [PATCH 187/572] Revert "Revert to old behavior of checkDesk(), add workaround to broken EnumClipboardFormats() in the case where the server is started while the screen saver is active." This reverts commit 38209e5079d1695ddd561f8045647e077fccff91. --- src/lib/platform/MSWindowsClipboard.cpp | 19 ++--------- src/lib/platform/MSWindowsDesks.cpp | 56 ++++++++++++++++----------------- 2 files changed, 30 insertions(+), 45 deletions(-) diff --git a/src/lib/platform/MSWindowsClipboard.cpp b/src/lib/platform/MSWindowsClipboard.cpp index 07831a51e..bb7c87cbc 100644 --- a/src/lib/platform/MSWindowsClipboard.cpp +++ b/src/lib/platform/MSWindowsClipboard.cpp @@ -180,24 +180,9 @@ MSWindowsClipboard::get(EFormat format) const win32Format = EnumClipboardFormats(win32Format); } - // if no converter then EnumClipboardFormats() is broken: try just - // GetClipboardData() directly (Issue $5041) + // if no converter then we don't recognize any formats if (converter == NULL) { - LOG((CLOG_INFO "Broken EnumClipboardFormats, falling back to using GetClipboardData")); - - for (ConverterList::const_iterator index = m_converters.begin(); - index != m_converters.end(); ++index) { - converter = *index; - if (converter->getFormat() == format) { - LOG((CLOG_DEBUG "using converter 0x%x%s for %d\n", - converter->getWin32Format(), - l_name(converter->getWin32Format()).c_str(), - format)); - HANDLE win32Data = GetClipboardData(converter->getWin32Format()); - if (win32Data != NULL) - return converter->toIClipboard(win32Data); - } - } + LOG((CLOG_WARN "No converter for format %d", format)); return String(); } diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index e0a140ffd..6408b560c 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -851,30 +851,24 @@ MSWindowsDesks::checkDesk() desk = index->second; } - if (name == m_activeDeskName) { - return; - } - - if (m_activeDesk != NULL) { - LOG((CLOG_DEBUG "switched desk \"%s\"->\"%s\"", - m_activeDeskName.c_str(), name.c_str())); - } - - // if we are told to shut down on desk switch, and this is not the - // first switch, then shut down. - if (m_stopOnDeskSwitch && m_activeDesk != NULL) { - LOG((CLOG_DEBUG "shutting down because of desk switch \"%s\"->\"%s\"", - m_activeDeskName.c_str(), name.c_str())); - m_events->addEvent(Event(Event::kQuit)); - return; - } - // if active desktop changed then tell the old and new desk threads // about the change. don't switch desktops when the screensaver is // active becaue we'd most likely switch to the screensaver desktop // which would have the side effect of forcing the screensaver to // stop. - if (!m_screensaver->isActive()) { + if (name != m_activeDeskName && !m_screensaver->isActive()) { + // if we are told to shut down on desk switch, and this is not the + // first switch, then shut down. + // Issue #5041 workaround - prevent synergys from shutting down when + // screen saver activates - if it is restarted while the screen saver + // is active, the clipboard no longer works. + if (m_stopOnDeskSwitch && m_activeDesk != NULL) { + LOG((CLOG_DEBUG "shutting down because of desk switch \"%s\"->\"%s\"", + m_activeDeskName.c_str(), name.c_str())); + m_events->addEvent(Event(Event::kQuit)); + return; + } + // show cursor on previous desk bool wasOnScreen = m_isOnScreen; if (!wasOnScreen) { @@ -885,6 +879,10 @@ MSWindowsDesks::checkDesk() // from an inaccessible desktop so when we switch from an // inaccessible desktop to an accessible one we have to // update the keyboard state. + if (m_activeDesk != NULL) { + LOG((CLOG_DEBUG "switched desk \"%s\"->\"%s\"", + m_activeDeskName.c_str(), name.c_str())); + } bool syncKeys = false; bool isAccessible = isDeskAccessible(desk); if (isDeskAccessible(m_activeDesk) != isAccessible) { @@ -912,17 +910,19 @@ MSWindowsDesks::checkDesk() updateKeys(); } } - else { + else if (name != m_activeDeskName) { + if (m_activeDesk != NULL) { + LOG((CLOG_DEBUG "switched desk \"%s\"->\"%s\"", + m_activeDeskName.c_str(), name.c_str())); + } + // screen saver is active (see check above) PostThreadMessage(m_threadID, SYNERGY_MSG_SCREEN_SAVER, TRUE, 0); - // FIXME? Because m_activeDesk isn't updated if screen saver is active, - // SYNERGY_MSG_SCREEN_SAVER will be sent on every call to checkDesk() - // until isActive() returns false, since name will not match - // m_activeDeskName until the latter is updated. - - // For now, preserve old (broken?) behavior. - //m_activeDesk = desk; - //m_activeDeskName = name; + + // Prevent this from retriggering over and over if screen saver stays + // active: + m_activeDesk = desk; + m_activeDeskName = name; } } From da5e9527a3f78b1d0e049a75007806ba89499e37 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 2 Dec 2015 15:02:38 -0800 Subject: [PATCH 188/572] Revert "Refactor Issue #5041 workaround to make sure we still send the SCREENSAVER message" This reverts commit 43ba3e889f88e526673ff70caf2b3efe26b13363. --- src/lib/platform/MSWindowsDesks.cpp | 47 ++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index 6408b560c..775b1396a 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -851,24 +851,30 @@ MSWindowsDesks::checkDesk() desk = index->second; } + // if we are told to shut down on desk switch, and this is not the + // first switch, then shut down. + // Issue #5041 workaround - prevent synergys from shutting down when screen + // saver activates - if it is restarted while the screen saver is active, + // the clipboard no longer works. + if (m_stopOnDeskSwitch && m_activeDesk != NULL && name != m_activeDeskName) { + if (!m_screensaver->isActive()) { + LOG((CLOG_DEBUG "shutting down because of desk switch \"%s\"->\"%s\"", + m_activeDeskName.c_str(), name.c_str())); + m_events->addEvent(Event(Event::kQuit)); + return; + } + LOG((CLOG_DEBUG "screen saver active, ignoring desk switch \"%s\"->\"%s\"", + m_activeDeskName.c_str(), name.c_str())); + m_activeDesk = desk; + m_activeDeskName = name; + } + // if active desktop changed then tell the old and new desk threads // about the change. don't switch desktops when the screensaver is // active becaue we'd most likely switch to the screensaver desktop // which would have the side effect of forcing the screensaver to // stop. if (name != m_activeDeskName && !m_screensaver->isActive()) { - // if we are told to shut down on desk switch, and this is not the - // first switch, then shut down. - // Issue #5041 workaround - prevent synergys from shutting down when - // screen saver activates - if it is restarted while the screen saver - // is active, the clipboard no longer works. - if (m_stopOnDeskSwitch && m_activeDesk != NULL) { - LOG((CLOG_DEBUG "shutting down because of desk switch \"%s\"->\"%s\"", - m_activeDeskName.c_str(), name.c_str())); - m_events->addEvent(Event(Event::kQuit)); - return; - } - // show cursor on previous desk bool wasOnScreen = m_isOnScreen; if (!wasOnScreen) { @@ -879,10 +885,7 @@ MSWindowsDesks::checkDesk() // from an inaccessible desktop so when we switch from an // inaccessible desktop to an accessible one we have to // update the keyboard state. - if (m_activeDesk != NULL) { - LOG((CLOG_DEBUG "switched desk \"%s\"->\"%s\"", - m_activeDeskName.c_str(), name.c_str())); - } + LOG((CLOG_DEBUG "switched to desk \"%s\"", name.c_str())); bool syncKeys = false; bool isAccessible = isDeskAccessible(desk); if (isDeskAccessible(m_activeDesk) != isAccessible) { @@ -911,18 +914,8 @@ MSWindowsDesks::checkDesk() } } else if (name != m_activeDeskName) { - if (m_activeDesk != NULL) { - LOG((CLOG_DEBUG "switched desk \"%s\"->\"%s\"", - m_activeDeskName.c_str(), name.c_str())); - } - - // screen saver is active (see check above) + // screen saver might have started PostThreadMessage(m_threadID, SYNERGY_MSG_SCREEN_SAVER, TRUE, 0); - - // Prevent this from retriggering over and over if screen saver stays - // active: - m_activeDesk = desk; - m_activeDeskName = name; } } From 92a3c47ed24ca99b857fc8cefcd2d1c9ae205dce Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 2 Dec 2015 15:02:46 -0800 Subject: [PATCH 189/572] Revert "Issue #5041 - properly update activeDesk and activeDeskName when screen saver activates" This reverts commit 3615add7b80838dc8b93cb89f2c8f87254db6c21. --- src/lib/platform/MSWindowsDesks.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index 775b1396a..98fcbb7dd 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -851,22 +851,15 @@ MSWindowsDesks::checkDesk() desk = index->second; } - // if we are told to shut down on desk switch, and this is not the + // if we are told to shut down on desk switch, and this is not the // first switch, then shut down. // Issue #5041 workaround - prevent synergys from shutting down when screen - // saver activates - if it is restarted while the screen saver is active, - // the clipboard no longer works. - if (m_stopOnDeskSwitch && m_activeDesk != NULL && name != m_activeDeskName) { - if (!m_screensaver->isActive()) { - LOG((CLOG_DEBUG "shutting down because of desk switch \"%s\"->\"%s\"", - m_activeDeskName.c_str(), name.c_str())); - m_events->addEvent(Event(Event::kQuit)); - return; - } - LOG((CLOG_DEBUG "screen saver active, ignoring desk switch \"%s\"->\"%s\"", - m_activeDeskName.c_str(), name.c_str())); - m_activeDesk = desk; - m_activeDeskName = name; + // saver activates - It does not come back cleanly. + if (m_stopOnDeskSwitch && m_activeDesk != NULL && name != m_activeDeskName + && !m_screensaver->isActive()) { + LOG((CLOG_DEBUG "shutting down because of desk switch to \"%s\"", name.c_str())); + m_events->addEvent(Event(Event::kQuit)); + return; } // if active desktop changed then tell the old and new desk threads From 1ac8db56d97dfd60516682ddbd78416f2f384e7e Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 2 Dec 2015 15:03:23 -0800 Subject: [PATCH 190/572] Revert "Workaround for Issue #5041 - prevent synergys from shutting down when screen saver activates" This reverts commit beac70f90c37b626cb7b0ea7bf2d18fdd3729013. --- src/lib/platform/MSWindowsDesks.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index 98fcbb7dd..c88801d8d 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -853,10 +853,7 @@ MSWindowsDesks::checkDesk() // if we are told to shut down on desk switch, and this is not the // first switch, then shut down. - // Issue #5041 workaround - prevent synergys from shutting down when screen - // saver activates - It does not come back cleanly. - if (m_stopOnDeskSwitch && m_activeDesk != NULL && name != m_activeDeskName - && !m_screensaver->isActive()) { + if (m_stopOnDeskSwitch && m_activeDesk != NULL && name != m_activeDeskName) { LOG((CLOG_DEBUG "shutting down because of desk switch to \"%s\"", name.c_str())); m_events->addEvent(Event(Event::kQuit)); return; From 20d9b802910b3d912a576231eba69ef95605765a Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 2 Dec 2015 15:17:55 -0800 Subject: [PATCH 191/572] Remove checking system clipboard formats #5041 --- src/lib/platform/MSWindowsClipboard.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/lib/platform/MSWindowsClipboard.cpp b/src/lib/platform/MSWindowsClipboard.cpp index bb7c87cbc..e0523e496 100644 --- a/src/lib/platform/MSWindowsClipboard.cpp +++ b/src/lib/platform/MSWindowsClipboard.cpp @@ -166,18 +166,14 @@ MSWindowsClipboard::get(EFormat format) const { // find the converter for the first clipboard format we can handle IMSWindowsClipboardConverter* converter = NULL; - UINT win32Format = EnumClipboardFormats(0); - while (converter == NULL && win32Format != 0) { - for (ConverterList::const_iterator index = m_converters.begin(); - index != m_converters.end(); ++index) { - converter = *index; - if (converter->getWin32Format() == win32Format && - converter->getFormat() == format) { - break; - } - converter = NULL; + for (ConverterList::const_iterator index = m_converters.begin(); + index != m_converters.end(); ++index) { + + converter = *index; + if (converter->getFormat() == format) { + break; } - win32Format = EnumClipboardFormats(win32Format); + converter = NULL; } // if no converter then we don't recognize any formats From 70104190e4d5e1fc4b7f68d804422a821c9dae91 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 2 Dec 2015 15:34:31 -0800 Subject: [PATCH 192/572] Add elevate log info #5041 --- src/lib/platform/MSWindowsWatchdog.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index 837db1de0..4646aa2ec 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -293,6 +293,7 @@ MSWindowsWatchdog::startProcess() ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); HANDLE userToken = getUserToken(&sa); + m_elevateProcess = m_autoElevated; m_autoElevated = false; // patch by Jack Zhou and Henry Tung @@ -320,8 +321,10 @@ MSWindowsWatchdog::startProcess() m_processRunning = true; m_processFailures = 0; - LOG((CLOG_DEBUG "started process, session=%i, command=%s", - m_session.getActiveSessionId(), m_command.c_str())); + LOG((CLOG_DEBUG "started process, session=%i, elevated: %s, command=%s", + m_session.getActiveSessionId(), + m_elevateProcess ? "yes" : "no", + m_command.c_str())); } } From 3ce078f0e8a83da08a72d60ff72d432b0b3d9ad9 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 2 Dec 2015 15:58:44 -0800 Subject: [PATCH 193/572] Fix wrong elevate information #5041 --- src/lib/platform/MSWindowsWatchdog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index 4646aa2ec..11cbc7a92 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -293,7 +293,7 @@ MSWindowsWatchdog::startProcess() ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); HANDLE userToken = getUserToken(&sa); - m_elevateProcess = m_autoElevated; + m_elevateProcess = m_autoElevated ? m_autoElevated : m_elevateProcess; m_autoElevated = false; // patch by Jack Zhou and Henry Tung From e5c1821088ac9d7f5484ba2b3642cdbfd5635be6 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Thu, 3 Dec 2015 12:44:52 -0800 Subject: [PATCH 194/572] Outpup log before return #4740 --- src/lib/platform/XWindowsClipboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/XWindowsClipboard.cpp b/src/lib/platform/XWindowsClipboard.cpp index 3df7f4dbf..00bad52d7 100644 --- a/src/lib/platform/XWindowsClipboard.cpp +++ b/src/lib/platform/XWindowsClipboard.cpp @@ -315,8 +315,8 @@ bool XWindowsClipboard::open(Time time) const { if (m_open) { - return false; LOG((CLOG_DEBUG "failed to open clipboard: already opened")); + return false; } LOG((CLOG_DEBUG "open clipboard %d", m_id)); From 5661a41d42996b7116d994704b70e80374d9d0d3 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 3 Dec 2015 13:54:47 -0800 Subject: [PATCH 195/572] Fix code style --- src/lib/platform/MSWindowsClipboard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/MSWindowsClipboard.cpp b/src/lib/platform/MSWindowsClipboard.cpp index e0523e496..b0dd3a3f7 100644 --- a/src/lib/platform/MSWindowsClipboard.cpp +++ b/src/lib/platform/MSWindowsClipboard.cpp @@ -178,7 +178,7 @@ MSWindowsClipboard::get(EFormat format) const // if no converter then we don't recognize any formats if (converter == NULL) { - LOG((CLOG_WARN "No converter for format %d", format)); + LOG((CLOG_WARN "no converter for format %d", format)); return String(); } From b5b2cdfadec0f8d9829e96980242eb7ff8a65847 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 3 Dec 2015 15:19:11 -0800 Subject: [PATCH 196/572] Add keepAlive message before send file/clipboard data #4768 --- src/lib/base/EventTypes.cpp | 1 - src/lib/base/EventTypes.h | 7 +------ src/lib/client/ServerProxy.cpp | 13 ++++++++++--- src/lib/client/ServerProxy.h | 1 + src/lib/server/ClientProxy1_5.cpp | 7 +------ src/lib/server/ClientProxy1_6.cpp | 9 +++++++++ src/lib/synergy/StreamChunker.cpp | 4 ---- 7 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/lib/base/EventTypes.cpp b/src/lib/base/EventTypes.cpp index afa51900b..d30ae209e 100644 --- a/src/lib/base/EventTypes.cpp +++ b/src/lib/base/EventTypes.cpp @@ -198,4 +198,3 @@ REGISTER_EVENT(Clipboard, clipboardSending) REGISTER_EVENT(File, fileChunkSending) REGISTER_EVENT(File, fileRecieveCompleted) -REGISTER_EVENT(File, keepAlive) diff --git a/src/lib/base/EventTypes.h b/src/lib/base/EventTypes.h index b10fb6b8b..d885adcd0 100644 --- a/src/lib/base/EventTypes.h +++ b/src/lib/base/EventTypes.h @@ -712,8 +712,7 @@ class FileEvents : public EventTypes { public: FileEvents() : m_fileChunkSending(Event::kUnknown), - m_fileRecieveCompleted(Event::kUnknown), - m_keepAlive(Event::kUnknown) { } + m_fileRecieveCompleted(Event::kUnknown) { } //! @name accessors //@{ @@ -724,13 +723,9 @@ class FileEvents : public EventTypes { //! Completed receiving a file Event::Type fileRecieveCompleted(); - //! Send a keep alive - Event::Type keepAlive(); - //@} private: Event::Type m_fileChunkSending; Event::Type m_fileRecieveCompleted; - Event::Type m_keepAlive; }; diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index 9c2b9c75a..615931d19 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -252,9 +252,7 @@ ServerProxy::parseMessage(const UInt8* code) } else if (memcmp(code, kMsgCKeepAlive, 4) == 0) { - // echo keep alives and reset alarm - ProtocolUtil::writef(m_stream, kMsgCKeepAlive); - resetKeepAliveAlarm(); + keepAlive(); } else if (memcmp(code, kMsgCNoop, 4) == 0) { @@ -892,12 +890,21 @@ ServerProxy::dragInfoReceived() void ServerProxy::handleClipboardSendingEvent(const Event& event, void*) { + keepAlive(); ClipboardChunk::send(m_stream, event.getData()); } +void ServerProxy::keepAlive() +{ + // echo keep alives and reset alarm + ProtocolUtil::writef(m_stream, kMsgCKeepAlive); + resetKeepAliveAlarm(); +} + void ServerProxy::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { + keepAlive(); FileChunk::send(m_stream, mark, data, dataSize); } diff --git a/src/lib/client/ServerProxy.h b/src/lib/client/ServerProxy.h index 144fd000c..5133af501 100644 --- a/src/lib/client/ServerProxy.h +++ b/src/lib/client/ServerProxy.h @@ -107,6 +107,7 @@ class ServerProxy { void fileChunkReceived(); void dragInfoReceived(); void handleClipboardSendingEvent(const Event&, void*); + void keepAlive(); private: typedef EResult (ServerProxy::*MessageParser)(const UInt8*); diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp index 2cdceb400..e07c81873 100644 --- a/src/lib/server/ClientProxy1_5.cpp +++ b/src/lib/server/ClientProxy1_5.cpp @@ -35,16 +35,10 @@ ClientProxy1_5::ClientProxy1_5(const String& name, synergy::IStream* stream, Ser ClientProxy1_4(name, stream, server, events), m_events(events) { - - m_events->adoptHandler(m_events->forFile().keepAlive(), - this, - new TMethodEventJob(this, - &ClientProxy1_3::handleKeepAlive, NULL)); } ClientProxy1_5::~ClientProxy1_5() { - m_events->removeHandler(m_events->forFile().keepAlive(), this); } void @@ -58,6 +52,7 @@ ClientProxy1_5::sendDragInfo(UInt32 fileCount, const char* info, size_t size) void ClientProxy1_5::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { + keepAlive(); FileChunk::send(getStream(), mark, data, dataSize); } diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index bdd0f23fb..cdfef01d9 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -50,6 +50,12 @@ ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard) if (m_clipboard[id].m_dirty) { // this clipboard is now clean m_clipboard[id].m_dirty = false; + + // add keep alive message before we do clipboard copy + // in case there is a big data inside and time that would + // comsume will cause connecton being dropped + keepAlive(); + Clipboard::copy(&m_clipboard[id].m_clipboard, clipboard); String data = m_clipboard[id].m_clipboard.marshall(); @@ -66,6 +72,9 @@ ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard) void ClientProxy1_6::handleClipboardSendingEvent(const Event& event, void*) { + // add keep alive message before we send each clipboard data + keepAlive(); + ClipboardChunk::send(getStream(), event.getData()); } diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index 380a37a38..bcf4a712b 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -84,8 +84,6 @@ StreamChunker::sendFile( } if (sendStopwatch.getTime() > SEND_THRESHOLD) { - events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); - // make sure we don't read too much from the mock data. if (sentLength + chunkSize > size) { chunkSize = size - sentLength; @@ -151,8 +149,6 @@ StreamChunker::sendClipboard( } if (sendStopwatch.getTime() > SEND_THRESHOLD) { - events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); - // make sure we don't read too much from the mock data. if (sentLength + chunkSize > size) { chunkSize = size - sentLength; From 7cbd3fdcb45838f90d4f7bb4be52f4ab4a0c4f86 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 8 Dec 2015 12:27:55 -0800 Subject: [PATCH 197/572] Revert "Add keepAlive message before send file/clipboard data #4768" This reverts commit 5287c62540b277474603ea8a38c53b50cb8f4d52. --- src/lib/base/EventTypes.cpp | 1 + src/lib/base/EventTypes.h | 7 ++++++- src/lib/client/ServerProxy.cpp | 13 +++---------- src/lib/client/ServerProxy.h | 1 - src/lib/server/ClientProxy1_5.cpp | 7 ++++++- src/lib/server/ClientProxy1_6.cpp | 9 --------- src/lib/synergy/StreamChunker.cpp | 4 ++++ 7 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/lib/base/EventTypes.cpp b/src/lib/base/EventTypes.cpp index d30ae209e..afa51900b 100644 --- a/src/lib/base/EventTypes.cpp +++ b/src/lib/base/EventTypes.cpp @@ -198,3 +198,4 @@ REGISTER_EVENT(Clipboard, clipboardSending) REGISTER_EVENT(File, fileChunkSending) REGISTER_EVENT(File, fileRecieveCompleted) +REGISTER_EVENT(File, keepAlive) diff --git a/src/lib/base/EventTypes.h b/src/lib/base/EventTypes.h index d885adcd0..b10fb6b8b 100644 --- a/src/lib/base/EventTypes.h +++ b/src/lib/base/EventTypes.h @@ -712,7 +712,8 @@ class FileEvents : public EventTypes { public: FileEvents() : m_fileChunkSending(Event::kUnknown), - m_fileRecieveCompleted(Event::kUnknown) { } + m_fileRecieveCompleted(Event::kUnknown), + m_keepAlive(Event::kUnknown) { } //! @name accessors //@{ @@ -723,9 +724,13 @@ class FileEvents : public EventTypes { //! Completed receiving a file Event::Type fileRecieveCompleted(); + //! Send a keep alive + Event::Type keepAlive(); + //@} private: Event::Type m_fileChunkSending; Event::Type m_fileRecieveCompleted; + Event::Type m_keepAlive; }; diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index 615931d19..9c2b9c75a 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -252,7 +252,9 @@ ServerProxy::parseMessage(const UInt8* code) } else if (memcmp(code, kMsgCKeepAlive, 4) == 0) { - keepAlive(); + // echo keep alives and reset alarm + ProtocolUtil::writef(m_stream, kMsgCKeepAlive); + resetKeepAliveAlarm(); } else if (memcmp(code, kMsgCNoop, 4) == 0) { @@ -890,21 +892,12 @@ ServerProxy::dragInfoReceived() void ServerProxy::handleClipboardSendingEvent(const Event& event, void*) { - keepAlive(); ClipboardChunk::send(m_stream, event.getData()); } -void ServerProxy::keepAlive() -{ - // echo keep alives and reset alarm - ProtocolUtil::writef(m_stream, kMsgCKeepAlive); - resetKeepAliveAlarm(); -} - void ServerProxy::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { - keepAlive(); FileChunk::send(m_stream, mark, data, dataSize); } diff --git a/src/lib/client/ServerProxy.h b/src/lib/client/ServerProxy.h index 5133af501..144fd000c 100644 --- a/src/lib/client/ServerProxy.h +++ b/src/lib/client/ServerProxy.h @@ -107,7 +107,6 @@ class ServerProxy { void fileChunkReceived(); void dragInfoReceived(); void handleClipboardSendingEvent(const Event&, void*); - void keepAlive(); private: typedef EResult (ServerProxy::*MessageParser)(const UInt8*); diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp index e07c81873..2cdceb400 100644 --- a/src/lib/server/ClientProxy1_5.cpp +++ b/src/lib/server/ClientProxy1_5.cpp @@ -35,10 +35,16 @@ ClientProxy1_5::ClientProxy1_5(const String& name, synergy::IStream* stream, Ser ClientProxy1_4(name, stream, server, events), m_events(events) { + + m_events->adoptHandler(m_events->forFile().keepAlive(), + this, + new TMethodEventJob(this, + &ClientProxy1_3::handleKeepAlive, NULL)); } ClientProxy1_5::~ClientProxy1_5() { + m_events->removeHandler(m_events->forFile().keepAlive(), this); } void @@ -52,7 +58,6 @@ ClientProxy1_5::sendDragInfo(UInt32 fileCount, const char* info, size_t size) void ClientProxy1_5::fileChunkSending(UInt8 mark, char* data, size_t dataSize) { - keepAlive(); FileChunk::send(getStream(), mark, data, dataSize); } diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index cdfef01d9..bdd0f23fb 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -50,12 +50,6 @@ ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard) if (m_clipboard[id].m_dirty) { // this clipboard is now clean m_clipboard[id].m_dirty = false; - - // add keep alive message before we do clipboard copy - // in case there is a big data inside and time that would - // comsume will cause connecton being dropped - keepAlive(); - Clipboard::copy(&m_clipboard[id].m_clipboard, clipboard); String data = m_clipboard[id].m_clipboard.marshall(); @@ -72,9 +66,6 @@ ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard) void ClientProxy1_6::handleClipboardSendingEvent(const Event& event, void*) { - // add keep alive message before we send each clipboard data - keepAlive(); - ClipboardChunk::send(getStream(), event.getData()); } diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index bcf4a712b..380a37a38 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -84,6 +84,8 @@ StreamChunker::sendFile( } if (sendStopwatch.getTime() > SEND_THRESHOLD) { + events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); + // make sure we don't read too much from the mock data. if (sentLength + chunkSize > size) { chunkSize = size - sentLength; @@ -149,6 +151,8 @@ StreamChunker::sendClipboard( } if (sendStopwatch.getTime() > SEND_THRESHOLD) { + events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); + // make sure we don't read too much from the mock data. if (sentLength + chunkSize > size) { chunkSize = size - sentLength; From 5e35fe2c1bff01e0fd5bd8acaba442ba80c97cd7 Mon Sep 17 00:00:00 2001 From: Erik Swan Date: Thu, 10 Dec 2015 01:33:00 -0500 Subject: [PATCH 198/572] Fix mouse speed increase with sleep on Mac server As reported in #451, with the Mac server, sleeping and resuming (on the server) causes the mouse speed to double on the client upon resume. Fix by removing the CFRunLoopSource from the run loop on screen disable before releasing it. CFRunLoopAddSource in enable() retains the CFRunLoopSource, so even though the source is released in disable(), the run loop still has a copy. When the server comes out of sleep and the screen is enable()'d, another event tap and run loop source are created and added to the run loop, so the callback is now being called twice for every mouse movement, and so on for every additional time the server sleeps. This is a better approach than the fix in 267f3ac41f7, because although that fixes the issue by disabling the event tap before the event tap and run loop source are released, a memory leak still occurs since they are retained by the run loop. Additional references on the behavior of CFRunLoopAddSource: https://developer.apple.com/library/mac/documentation/CoreFoundation/Reference/CFRunLoopRef/index.html#//apple_ref/c/func/CFRunLoopAddSource http://www.cocoabuilder.com/archive/cocoa/242438-trouble-with-event-taps.html --- src/lib/platform/OSXScreen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index bdd0a4831..58a323370 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -841,6 +841,7 @@ OSXScreen::disable() // FIXME -- stop watching jump zones, stop capturing input if (m_eventTapRLSR) { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_eventTapRLSR, kCFRunLoopDefaultMode); CFRelease(m_eventTapRLSR); m_eventTapRLSR = nullptr; } From 5f6ea6054f24dc2a3fc1d63b1c7210b33f7a404e Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Tue, 15 Mar 2016 11:58:05 -0700 Subject: [PATCH 199/572] Update changelog --- ChangeLog | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ChangeLog b/ChangeLog index 8db751de1..e0617a1ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,16 @@ Enhancement #4793 - Additional logging to output OpenSSL version Enhancement #4932 - Notify activation system when wizard finishes Enhancement #4716 - Allow software to be time limited with serial key +v1.7.6-stable +============= +Bug #451 - Fast cursor on any client with Mac server +Bug #5041 - Copying from the Chrome web browser doesn't work +Bug #4735 - Clipboard doesn't work from client to server +Bug #2909 - Clipboard copies only plaintext between Mac and Windows +Bug #4353 - Large clipboard causes crash +Bug #3774 - Missing MinGW dependencies after install on Windows +Bug #4723 - Waiting for active desktop result freezes Windows service + v1.7.5-stable ============= Bug #5030 - Display scaling breaks edge detection on Windows From 2ed3d268171efe76fb6b140b4ddb919c68843aed Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Tue, 15 Mar 2016 13:24:08 -0700 Subject: [PATCH 200/572] Fix indentation --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index e0617a1ef..5829ca1db 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,7 +13,7 @@ v1.7.6-stable ============= Bug #451 - Fast cursor on any client with Mac server Bug #5041 - Copying from the Chrome web browser doesn't work -Bug #4735 - Clipboard doesn't work from client to server +Bug #4735 - Clipboard doesn't work from client to server Bug #2909 - Clipboard copies only plaintext between Mac and Windows Bug #4353 - Large clipboard causes crash Bug #3774 - Missing MinGW dependencies after install on Windows From 0034ca4b7666f5d570eab4a9c275e373008b7c64 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 12 Jul 2016 05:54:47 -0700 Subject: [PATCH 201/572] #5461 Caught all exceptions for activation --- src/gui/src/ActivationNotifier.cpp | 7 ++++++- src/gui/src/SetupWizard.cpp | 3 +-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/gui/src/ActivationNotifier.cpp b/src/gui/src/ActivationNotifier.cpp index 7efe4e82d..fb2fd465e 100644 --- a/src/gui/src/ActivationNotifier.cpp +++ b/src/gui/src/ActivationNotifier.cpp @@ -32,5 +32,10 @@ void ActivationNotifier::setIdentity(QString identity) void ActivationNotifier::notify() { CoreInterface coreInterface; - coreInterface.notifyActivation(m_Identity); + try { + coreInterface.notifyActivation(m_Identity); + } + catch (...) { + // catch all exceptions and fails silently + } } diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index a3a081fc2..e6ada683c 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -243,8 +243,7 @@ void SetupWizard::reject() } // treat cancel as skip - CoreInterface coreInterface; - coreInterface.notifyActivation("skip:unknown"); + notifyActivation("skip:unknown"); QWizard::reject(); } From 6dd7f340c2939e99a11becaa79358b66a7946860 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 14 Jul 2016 14:58:49 +0000 Subject: [PATCH 202/572] Versioned to 1.8.1-stable --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dbad124a..493a1b65f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 1) -set(VERSION_STAGE beta) +set(VERSION_STAGE stable) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From d196ec5e9a1bd60abb413ba6f7b9ded94a4141af Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 14 Jul 2016 15:01:21 +0000 Subject: [PATCH 203/572] Updated Changelog --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index 5829ca1db..a6899ccd8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +v1.8.1-stable +============= +Bug #5461 - GUI crash during activation on Mac + v1.8.0-beta ============= Enhancement #4696 - Include 'ns' plugin in installers (instead of wizard download) From 822267d300dfae044e8322ac09970479c8ca8f1e Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 13 Apr 2016 18:10:25 +0100 Subject: [PATCH 204/572] Fix indentation --- 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 da0af1aa7..dc26cc76d 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -1957,7 +1957,7 @@ def dist(self): type = None if len(self.args) > 0: type = self.args[0] - + self.ic.dist(type, self.vcRedistDir, self.qtDir) def distftp(self): From 428901859ed32ab92699ff300c64a31d12115859 Mon Sep 17 00:00:00 2001 From: Will Tinsdeall Date: Fri, 2 Oct 2015 10:39:08 +0100 Subject: [PATCH 205/572] Use account in auth url #4913 --- 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 cf8960893..c04faa072 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -30,7 +30,7 @@ #include "platform/MSWindowsSession.h" #endif -#define JSON_URL "https://synergy-project.org/premium/json/" +#define JSON_URL "https://synergy-project.org/account/json/" enum { kErrorOk, From aa178a356f574fbc419bf6a0980ad73de14c0dc7 Mon Sep 17 00:00:00 2001 From: Amanda McGlothlin Date: Wed, 24 Jun 2015 18:23:48 -0700 Subject: [PATCH 206/572] Updated mac app icon to match the website icon #5299 --- src/gui/res/mac/Synergy.icns | Bin 314227 -> 142499 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/gui/res/mac/Synergy.icns b/src/gui/res/mac/Synergy.icns index 7ce779517c00733a690c10a9cef380a926e24756..6ea0f601946791c9448f5fb712b7e7320d58ab91 100644 GIT binary patch literal 142499 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�{COgwHXJoJO7`0W^zboelfbF!9x_u1fYNTgq62E!mP^=Apl#g z)lHnp@S(D}!lOE!Ul}SPx$sviU|m~O2!m3~o63)#{*JNs$cHLsM-{OM*&opMh192xj^!gV>ZwrDX*(jU)KJDm?-IqdoMlSL{ z*&j&J8RQ+Akp9g6Juo*1v+~zkK6)%^WLnzq09?Jw9kTN4={ito3_tG6B6zcCX{$kU2xF11U2%*uCiSm^eUEMFZv}1oksNH|_qFp9HKf+_rvMvn8SO z^Z!M&A?RAVQ$Ny4>(EL8cCZ0xPW3~^_WjXy)8k*c6NV;<Weu)6qcG z&I$3sXI>N)Jm#;`J708s88lu)X&>ZRSeTw2IJa=_vM_i6YX%@tiot38>7l`@o(bSf z%{n^&rb<*5NT8_Ao;`pxXPsi3q}%AT4usPS@6<=twblA-NDd*1$eU7KRWmP4YwWh&pZWaUi&`v4sy%8^r2oU>6%xLQnWlM5i&65Jq-9@V8BR7#I@k8bW_yC%EOzpgy-drE_-@fat zHCO-AvwCI7*7h>QU-`AhbXCH}U-@l>3w0=4k#oBChA~vM%7EzC2{1)QR)x7$HKp zt}bYz9G`C{&y71PMr+MYAlXH1gH)8U*5&cjLCT&57WiDSE2hx`snq&eYw5tC;iI($TDFY!>Ax$Rtlp%f-by5_k>-uRt`OMpYO@GIV-aTCg2OLv7*|fyi zaR~o`^?$vmQy_m{+VKkJSrfk+!(V>o%YhJ*Zw@Wz?TZ{T(*Z13o$HJv2w0Dipc~lM zFexC@Dd=+!3g=|jNU6W<^JSx~{XXN?8%8H6CN%3%0<*H)MS7BM1*v0-x8^#M-qUcL z{0ngwQXb;0WqEC69qr}*gF;TVkp)^&s$30=FS6QyZNA8=bZ;%N1l?FwRe?0iGgj@^cRQr@gu4T@S}wlRregj1KInAJ@s7O$4dl2iC!ZH@HuHJgWJM2)qw4S> zhBq>wDR0Cec2F_D;rEjdWAJT|JxAr7va-~`m!_mjBuTcfcnvz=fn%R5z2BM>7Lv~g z@t+u7Odg4}-aL6Dc_4Bdt!zZnjXkNFuiRk5w>=2K(iHpjJo1mBT|7wXlYklMwteojUf^2dOFdxy|Z`^MMF&ZDn@=cjjUT)H5@|4+ozv&hl zG<{2ik{?!i^hTSqOrLST(3RCg^E(h4{k6XnU4FuPlA^t& zAOXkwT1Ywh^W!S#3ZDUMyQb>Y)4<7`bL3wiU?B18iZw_a)%-hE-9pK4CUgk^2N)*?eF$V9#Ji6CCbS#9{-G&CToonzN zsu^VvV5R9TnWMlB;KfNZWicA{@~oIcvrrY?d61@&Nj~io&wO$pA^bdr?)L8 z?vwrDPqSn6-EJuXy4RYyF~&NT39NM6i>!a@oDrX~|88L;-+%C7A7QL)0cV@2oQyc9 zVs<%r&nuN;J_6ftG=FO8 zHhtD|xjQ!=9EmTlSg?Nf?&f`a;p0KkMeXu_#U#>^;t=1AQ86C~`5Pzz5tMb0zWreP z630{pn^Y8~FIVP^;FO8nWSks&AgJ0Jy;2>2S0AK!_ztn|W5ON=kwP-=e?(xuVTGCH z_a}OfDg=d*JsRSZqoD^rYGH;gKCA5j+t|b?Iq%Nzh{AN4N!!)leYXVj$%zDvHtOh^5 zyYVm8&paOaX3xj>4Y|BoXu8IB3{$ClYUFo=3>S!=r#Mjw3z8uv8wS7rrxu7AI4r3V zIZS5l-J*Cdb{$Ofht-EqH#c#1U8UwAtj_bPDTLjK@&;)WnKx5U`k-(p_J>i&^e^^H zM>`m*k6?jSr~h5*SGV$0FE7;u_wo+_4PK^uN$djJX+R)MYqvG-<=M#WOUS#zWw}4< zT(5`qoi&#{Rc?M}crP?e|K#nif6;Bt>xlGb-CyW93T!4&y_sA%>)W#`={)lxr=~Wa zrRJo2cD8c2SSmBTx)Jj}Y$#Y{7&eloc83OS#~1w%0C5M$yvd9uguKa63o-uTsUH2U zjfMQaIwz(*XJh#x8+pyMFr$OR@^r*D{|e;p9-d~zbo`+Qpy{RbO!6qUytHqfg2;a z2ryy7q73r^e}+dd1_Eg*nl!#}x&1yVdwJ#V@@e2pD65U3mR6Bw7z%i^qh5aC^SNSj zOAKtUU}9C^H$=pKy3Y6QjpH1zhyjlP+2C>);L`&7en;q=0DJGR>1f;E3qn&b3=RSL zHvRk-krAAKXKb3%&M8*LzbCYvMKU&J-Cp!2Q@9S|GVTa!Q<~-PuI5$Zf%XCc%N3j&KhBGUZle!^JkM}{d%y^7=MBj2FZbqciR zq6bpG^N_mf1b(tVyCr_3NnDYhjpsbxA-aG{!$TF$H8L)I%cBxMTEXxnc1p zf9LP}j!<7uVWCc{ngBVa({mMfML<%viq(~XUS%SZ6F+Fn_Rhj%xojlm6?_J{m)dJY%(p_!_ zEeIA|{6#cR`g+A;Z_&mY=CN*yN3Xc6Unp+h${)U2Y5(?jk;BL*^ZifDU%6AyAC%Lr zS<>c|C|4{5S8;r6;mn*TaVE0Nt_^JitW;ezFEx*Fi;x1pqE#>j;?6iTLXjVw*h2>C zje)Mnfp?wKaMJXm89v{@_t%EM7-3#_LTz6!U@DD|`!44hq`N4!ugE?kn-GA1kmirG zRtRKzF1u$g)Y}5ji5D?fP^I{p&idS&Q?P7!{`s(A=DuqF{bd$b=vOjaRd;Yol*oO_ zm;z6T)ow9J=66Gw_dPul>ZnvyVYYpOpv3Grdw!s8FrA9~Yl+Y+Di|jRjrM*9NXild zPZIYALZ`_HVZyBSc2<2Nw=W*Mso%ToLH_j>=Ma*|+?$CS_ed7m*Rl1q^8z1I|GVh# z@gNQYn-Mf8q|6t0X4s&@67of}H`)q6&WrB450O+OsE9bh_@iGCHS zKUJ%1^^VC2GOhHiy-MeDkx0#La)e;!c$m#2EAIk;;Yn^HF6Ye%8tisVesk2Mw8`zb zvE2NI0C1VT-S|P{0bfaKUdS&hJlbg%>+J1I`m6Z{RGTx_grWR?@I~szh61 z`Gwovjr$7Xlpqys^M!o_M0))1fKl$j$mhhramVMRt;2)z1#1q0JJYO33?jz^bP8fj z&VyLvtgrW{daH5TEDb3m z<`MF%ZGbR@Q>qia@8j`q--ifCv2VZZug0&@hwDVHSox2rr{3+p7SQe>1&GR&oL|-+2TD_e_2Y3`H{8H!fpFG zye$#-l=sd{EuQ!;vK|4V4uA}D%cO7Hjm>3K2T>omQ!Nc7AvdYW%hU3Y@|%vs>3}?O zy4f{}bL}Yf38XZD)@TQMO@z<&YWzXD@b$UZ0`omH?T-!$le<|#-Q~*lDlOH;tEq|o zz7vlh8v9e4BY{YRMHlFy40s}31GkJqs&eFyKd2r~RLg5!-)xS7#GuE{NKc90T>hq> zKi#NX7Cl~3vx~kQ>@O)b1RoxkK?kEdW4`NMw^V!Jpvg8bYj9pd^0`5pD@yOUZrz+l zD$L&&8Ns@sA&uh&iyWR3UCUM*Z8?fNiLy!`#58Vf$5@~NYF=Yx!k@)qAk|9}G* z6?Lq55>&JB)q@;`M(eDPpMHIG%ZpQK4YiP#gZJMkR^%Qrfr7!PO!@9ESg)TnsosdEMGWpH(n|T5pQfQ`!BU zI(8__`ChhHx^cWfFk`T7)sC2+pGz}jpl5g~++S=c|o^@|aF z>mh^fHAC}kg2jQHXHMwAQb3F<^GlGnQ>qV}uhz0EuZhnAqGCJ}?q5z4CQqlAWFH7W zN43iS34MO6yHvimzSu2|4=q;HFLqP%eIvb^J*g{+6F z5FDR3|L8F#k*0!hLgN%6a~8Oswt4LyD?bpLqD^mU@;`mZ&QeZ5oH;;jMNaL2Ks3ch zsI>JD@pUlf`$=5Hbh0j~+JUHpTMrR4@uT|RhvUC|*(ACBoc)nRqra(8X2aMTIyv9p zuTdL4Jb^3UizQLqDg^hpWAFm)S#Dss%~;&XqNUbo?lDgefk<3uQrTPKI@hn8&Wh>{ zTjzqHLYLnyPi480>$yVI#Bgm2wa1^$X0pVGe+@PzA=RuU$tenW!kIhr=>CeO^#BkJIDe@DXIphnT3a3=DPeEPvaA2LF|TB;^Mjdo)WX8n$opgcZ|N ziywxm&rg%A0F53=$^cP0kor9*(r)S8%r1x6c3)xGScm1J_Bbv>D$+kw>~EtHrjG|j zPhhjp^QS3d*RAb*buW%thgv~+*4#(#8YxMN?XX4!p- zS-C0TJ$brDTP)usb;Kg0z%vy5^6KrB@LCqt$j|yc_mRI~rtWj?DdJ}BvwV1)i&_F7 z193dNegFZPq(%tD{E7~rD-Gpn`WEYeDHVwPH|TCICWX{@E41!ea*w3+hX?Jw)LU|$ zc=(Mh!mJcT%|D>dd;NW-W>@qx*WD@}#f``x^kbJ|YVR+(e4Zbax{#eW zlmWtuZdvH&f5?vQsH|Gsy=AwJ5!6FTs}u+9u_Cl$~)`Hm0lRwmVm|?6&W@ zcs2IG!R7Ge>98XF>*OR=)9Ddn?QS9Dd4uY-6e6X(NxN7Ov6e zb)S@((`y&R+Ld=nyzY!!1nUeLHF;ISm6ic{9C2x9MA~J+U^=_wA(bKqZb1I&cXB`) zL{vml^LHxdIMHBiP;##6=UWW$@@Yzu+2kUP1e>ZIfpIsHTFYJFyB(d$P(jXTjUMmO%?SJG-E6cxp48vyU5k$+McK&J8P|<1~Gj8ms(VKZ1>d_S{2Kbz((aG2kC zNM45EJ$X!-+%C7RN=c*EVVSTCH28}J{3 zG4mcotw~(M(Oo61>_b0u(lOixQ4p`z{qm9iw;?8l5;uJ+5sYVCHI31+<-k>aA~%*P zn$@<|b0c{x(h|xk{W<4PHa@yCkI zj&3CU53qSa47ppXEu#J!^!ty9v9Rrb_hw(Qby0`EscFql)~~c*Bs?-Z(`@t~BKmf6 zcTu*IZYpzrbrma{#g1|olJ8|7=^2kcB*0knB^vVs;Z&#Rb4I(NZ6Q?m-wp1}hLhxI z@83Dn(p1dc1a&gyTs5jMhX1bvERPOh$#GYZl?6B6+Cb;WxwgmR_=D@PpBTg@;DsU@#Wp&w#V8hR7T{Cil8$*-N-PSeia`++L0 z*9IZwKk0!PbRY;L>ouRO?Q^nRzI&5+{6I-(fb%Aad_|pVJSmm8OWweu9joKY38KIv zWKqO1#;IxL6UfqywM{|EuK6=4B|uf|Mw3skCNSKK--*IV4>5DN_4{Zth@snl)}(Z- zdAeBa<3}ylnD`G|iSk#DXC4$}7|y=_j|Od??7wB$0{b^Sc1-Qsbj;Q*h%PkXyIBo| z|Hw9;U3&iq1euN1edsFN6ev7y)XX48w2w=1fdA-epNs0;)$ieeXy!qg7*gksN?oI{ zp=r~r*MXGV`XLVCx9rx1vmC!_=c|{PTjrW7dL?6&CI8UA9cIdyg+?47;tt$SRbTD7 zZms__`9Y!DublyeP|9l-Rvi5!o<* zZSCWdw{;k6HOAw(A3U4jF|(UCGIS6X?i;8H>s1= z3Q8s!HeJu6$p8mN+9VwjQiz<6kB$upkbjUP)0H)J?We}QZ1KU}nFKe|4PYudDb@Sq zW>5TeXSJ3PLZLH=O|E1m5v+J`Iks0R0-wn;eHzW2GX>bej*-bhQ&|CzKRBUF_L+CU zT~E}L0D2m;A}i`&1abukBy8CL5A?Le4+zTCW>6rSLE&95bMTNKNFU2N6)s@?IuZ7J z%``>j`DN8fubHMiqk`VW~fup&rOe;{>d6PhAtuSYB$3TYL32T)g=$ zo&eXG{nDU#Xx=FyBO?mx(L-oS@%PcFDgR~rldq6VphVLH!SeZE%dhHE{Jmo_^#cC$ZZ$+D{riLR7E-2*cxn5>&7arc z)nDh${gGu(iLPoFtlD*nMOP1gjJW&X93|Ck@ddlTY1l|f7Ki6@-DzfLepwz_sViv7 z^PLjy86TE2OhZ4f9c2y(<#XG#MSPVI_9RZM!TP+z zwm;$D&I7cB40PS`R#mKMccJSBU7w2*Mt(OI8vG9L1J6dkM48$M8MeohE*q~jEA zYbEy!Q!dZg7sml5X8*4}EeY4_S*3*?jyumJL`)P_%NJ%&YrYp%KO2AO`fkt45d)FE z$-H`)F5H?vFcYkFyb9TmyNX`H)+zdvxW!o|h$~X+6*yHgE^kxN?rXG2d5(JdoZA+& zM1mM$JeG})9guiR+rahaE$4BVG&M?|wWDCt+FH;ul2Nse$ux&;Ni5s774zJDglXTm@A-)Y^kHdjtViB;2 zJ0z#(Q_x@~$4?mo@++gI^v0`~OD2VZ7r9|W;~LWZCbXhU7RGUH))@IGF~y17%Ll7bL2QA+-X)}M4LegBv4hZ+qq(-Ip{<&k+$QZTW$ZLW;w z_iDh10KlXlndAH0DEod@#=CK&B)+^nOvM061?EDY605E!(L*iRJc$h`>Qis(#fTke zjQ1G!3ubg1k+OwZ9-bE|RzlUAv`qczGHHg-y3h)4sF=EupqOJSl#*8Aa_H)fSXKUX zSA5%dJvx_t5}yOrNTveJwmPQxuP7$E$+5Wc&0RpE)MP2opJIM1eK(tQBAGC#CR>i; z&L-^d{~yl@rpxhz{5qNmefC8^jivW>IHegcTAOM#P%PlpIAJkcNm}mGjK^8jrGMl2 zAy^dZ_i5(aA3W)q!dPgROHRZ`@3L9%ZQ-X7r8W`NUz!y zlMnH8m4#mWzxhU$^V_?+)7*cHcOM_H8GWjO+t%{To9X`+Q`hALC*-b@{9jLY>%fzV zk0R*w^722Fz1Hmh-{~^PriFYX&SwmMQF!!y7Hk!6_BEXS^6CcK0gI+(KwZ=+-{})n z5YcSmmg4YLzPX6Wi8am0i{(~FF+5@KL-z*ijkF|79>l zF&O)X9$88Li$ihGD~0vt)tHSO6V82^zVs*b&6L2?E6qH;r>{@sW&;u8l{uBcHnKKy z?gYUW&l`5+G{@&}01+0Lq<=4VnnIa=NG z3ZGX^1Vb}3=1BsHup`_H%Z%qUBr7BKcI)$!Ph&E9%6s4ENM>Tg?-Q7Mi%6;=J-ULv zp5NjT`k&XpjzX4s85JHawBTIi3RqtIlFdKr5M=Mld>cpKGQW17)6Z*QvDT%`YOdBd zwLQ6k7IPlAWke)afZW+ntxdY$IG$3q`=;rZ2w(H?V8IoCi#C*is}!4(!P${RyTT}> zTL$|A5fA?%0c{}m@K4a!LcOlFw6`VKD_-F_k>KRomqKW0 z(Osn)7la6w;!r+MFpC1hw{M!!EYDhN_wQ>wlfE zbo8@pP^shgTbx}P{Jty6az>`axj;V4}eZ1UB#4l$VH$0Dqi(4FuV4QWP041^CqUTg4VI~s8Q-7_6!WA#bJO9m7>r%8-(<#n3k>aO4A z(y`I)KYfaVJOx|sc0V!{bWF*gKSiFO{;z|NkA8$10K0cJW+%oA-xN%6Q}|O*?}_a{7!w0%58&hf@CGLE*N|>@D+e3bLC1V+wypuyBaKo~!KS?l zQDBY$E$T27YQOmtPy#Cg4PfCU&!mLawH{wbh=14gXXJ%P`gx>hDagEkg?FC%1C*Nw z3Xq`hn#1T!)gl18ax3OsGZd0o;*e97cj1&0&+%ISvLh-~0K9uM-R)i?_Vv6PtdoM( zW$livc;u>?N%7l|6%hBtzK+haK&s@omE8PT;Pi{0=7T*0F5;9zXsUUUtJTWrW=2v| zB##zBo<#jQ3mI<5N3!QjocQV_yoSLJ5tVmw$f&Tb^+(Osnl3;vvLZJ?DxHGoN7x=X zL7?gWlz9!4_(!m?V0~sp>wlYPLkdP}9!VeA?p;-7+Nx)6fY~!A86l|;M2+z|x;_;W zuLWMXZOV9`h2TDqA!I6SyXgt9iz-0$fY3TF@>m*uDM~BVD?}?aC}FSA+(Kcbjnc5H z?|;fPSGq3Z747oTbiAO=EM1&U%+R`yzh;f)T_uEl97apPnYY(^3ZPsPJAgbMh)1W+ z+s|R=bGuU%--s&byLr7f8yzsSmpI@EjSR5lC;oW3l2*;mD2O8+1)2G({^dVz8H*^$ zSefRrGp*zv@K5}2i6=jWI!C`V+Fq6(yZ(pu0&62H}ty5@_mp^-Fk;G_%$=5G5^yh3t6|^O+NJF}U z;CG53-?eD8J_Z`@E}{d8Z{W1i+~KL^mcYhH>?Jm(k53OiE3<_->L9XrJ7`@5RZ~)> zw$*}q$-r=CK$_p&)K+x$<3*&=_$$FS}^ZTDqq3m50!g90?d z041n7g4(!N zdcE?CO^W`eJc&v3l#pN$F6!vvWAnXI^=>DoN z@qWoCHd4Y6Zh>Fqgqy$ZmCS9v*(E8)If@db&3n3{%{5%C>?F`3n>x0;RCR(V5Sna) zIDphZ$|JDrW?an7vH7g2?pEH0ek#E;BkX%6iv$gK3;ueUq~lFTtz#-R00|HX8g=IHPbvoUQK*b46y-8d5VLP+p5; zc0DILUQHG?P)~kZr0bzNmvKbmuy#cCo~TN zY*-9Fz#knIrgTS)0f@+Idl9T?%m*9SyRH1v?X9~E7@`Ntd(nNTrR|d9$bqquEG%<9 zIgTxBM{^@#W2k?sLjc3HU2_Irvo7z3r~Qxx^a*kM7e(top~elsSIR_U!nvQfK|NtCstLz9zI?)yFvcgm!@cBdE_Q-)HZg5;M_orKVKK98!ot! zXofV!R+6Z2HJTta66mps<`k3pA+Pv{EB#n{rWHPLB!|=D5HP5p3)+1%-;u*n6EhX^KJHv}C`N-9yhoaMUI@}aVI%swq6PbE3qKq1OJ zody-)%A+8+oR()dPq+=rrnd#qaR9;0tEe%sNc~}&xqaUeHo6PKsn2IJ5u*R)L5%Ba&)_@UES>QD)I#(%BnR~BnUYw?X77FpGqWCxH$w~j=H;lUQ$ z2jfUr8ob;aRc$IM&!n{jm6Mz}FWiWE%lti0r4G8ClNyXQQW3$cBUGjb(EL&z!@v^e zt^>6N$&f3vSUV1iak$r5i9Fq?b(=zvpBq{h$Nuv`E(1VYQ-L}@*jDus#;APcBgHG=h zj4>zJ?e;{fp26 zItjjI`II;+vy17_JQiX2>_@#cTT+{O=yq)PvUu;@s46AzXeJBE z0sb5BI~?F&#$H!&--BDv4pyRkXD86Ap~iWY^509@$Uy$8lvrfl=3Cd14njS(Q}uB| zzjOa(TbI9w+8|L^r4bcfsS9*5$N0x82K46Eyyhh4z~QwEl!Nmu%;EFcgGT#YfVoSdP5&6X^{{ zeTP;1(T;r_MhlLv)N`84 z#I2d*{uT}6SH*VL8xrxx|$5soi97r ziIFz2^*z4G1b_mN=>gx(c&09*>NCi0 z>EDG$Ng2)8h=#B9Un&l$2>_Jn9205P!jyjFYI*s z)bH&xEb9hHl&1%90CX3vV{7wy0JUL1fh0N_OS49keo(_(Z<8R41&{1rVf8y;B6M@o z*ZFCBhIjmv;`!{_PL*^b%YXGCIxzkvx}NXFM2`QD;em2rA;BTnJp2NiK*NLXY#n4x zL1Od-wKXtwuj^o38A^U&9F#s1AqM)!GX=Rv5|p2) zjYT$Au$s7becDp4M3EnhDt57!IYa>|$iVWUMGVLOV~j;~>g}v0qvfj}pG=1kJWf~t zRj#XgW)2kBs1K9LKB~)cSb%+^8lJ8@^SlNbf8BsxWEWTu9?hGEcm5#P=0b^|?qgpI zUn6{Is9XlMe`88(AiiM`H+-7?wcx8x_+Lkdt3+ zwgyUpzADwMf)$`A2LfWm_g%RS%oP~)5~!xVw{n7^ck~75uio=s6nHffe<{D|@aQ_f z9{FQ#q@IIKbuWR>kBKY`3YfUz8W%fsI@NMI*v(TsuTLTQ8p|vMcsF2c*r>XC_3!6! z^Rt;o1B@A%9Hu^40PX&G{{ZPk+7_d7e>_AIA9W{q5 zau`|iXbDG28Zb$$^VLZHFMoO=|2dqKSJ=XRqN!^Y^fi)9TO1N})NTOSI)-u_^X)Db zBnnr0Sv{_~IDc&w=mx4wmu%5l-_wF9U_;D~&b|hcdIW8n((v&$Fn0*{T8+LG*VlYMzUB3FV z;%O3K5JKA)U-%&xQht`mu67+>i|}8>TOBd38P{m&h@%B3Ul4OB{-=vXXqgq@9tuDG z7hkOw%tnh6e`BwB-o)TKtnTtGEsc-C(Y;!fEjaEr%BpJ>@Tyf507k;qR^!A_VL~h? zXk84Wu|bs{axaHf0_Ejd#Z_M;{V_X$i4B}y$EvIIxWzK{g!oq_7?TP`el`bhi;B_B zU_e?HPEvFRVxVS$=(yne1M$hLx9 z>X6K9&DGdwcUS@@TIB(KX+>#=ZSl9Tayko?1WR2u1Nmg1chSCH8EELv!u`hA7tg9V zzN69}UHN0NaRYMGEcnH$$mZ`uw9?)kZ)4$VIvvE|Pr|FQt?cAr1pZM7S4vUfjP_0kfQJhg^t&1&Bm|FUE~#E#(s9K7vR^_N z1eHC9ZkIXnxydR>EO7}PTof3P06l81=dKWGB$n5QukQm6my-D!DTJNzoQsW`&`1&8 zb1xg8JQLuL(;`-kg{SL9Ph$*`LrxsLjD(mauBYMn?0_pV?^@Z}sfuWh!Pgn&X$g)DBMP=QNW z`$bpsb$p@NXf^>r>(Gi0D^UBSh{?K*;RuJtF1;3ch{{rISV`#5|RN z#)s7ze$jWEL1$znQf1lrjU-74zUjp&dV_62DsOh!>;QW)9FS`I_@z~#H@MWx;t9II z{#Gde`g8?46PHY@3EpjZNV)X zRn5>!sO{?}>^O4D>@3Lvbb=7I8 zs$7LB(o6lnwTtHiaJj=5-EKLGVf%f2Ecqnda{HT9NtpJesfLsA=9M>i?)d`)pa(;~ z*w^5-mOM7pjvz&L2@bN-t9o7J{OdFnN~+7rVa`kQdIL(}w;6i8@o^a7B+rQic^$x@ zcn^RawXfppJq(Pd>-}rFc<1|eQtS<6z7ADr`ME748S`kMi%Q|}c%lP(#hgyh0VsOO zr~O}hJ60RnoL*-%ikOf3R7H4$7w#}Uqb8?yJ{^hn)iX58qKblKAIhQi(Rp$D=FiV6 zq4z%{%lFTW|=C%g0Nt$?4fY9*!<1w96@Yi@eGk_v);{0G+!}TUYzoSeC_{mB& zu0`Iw-%fpQXq)Jt{i6i?Sd}>(&OL@^NT4&5OeJ^myFq;OK|BOb9#1H2%;edxjA*Gy z{XYJSFl8p0*k8fs?NUikrgXUW$~P2{$i{Ev44&LbTcq-F9uabhuUY%M#|K44EP)C2 z1HpzMtqS(5vs~kVE*JsLKJ^zClVRU77BzqP!1vqj-&THezmeECU(m4apdSIfA$?#% zZo%mD1aE=m%g2=R8~4O#H4K(L5JxUX@(;psfb>6FIKrJu-YGNAo7bdlrT-Q))&dnB zjjdZu7iy<@QMqLZ@Pwzh0f)bOrM$Tu+}+hw%iaNQHrfjI3P$U<=vDH~C{ zDQ@0}Ja+j%5*~B<-rnVCFp1$?PUJ)iC$~}kpX(03MgKb zo+|9)1xVZfTmK8FE$@6W9_zfFqs;t}VM$yrY2Gs)7_L#fP+pCtb3Zmms(ijE+rD#m zX;dk8w;YvTsWG^pB+Jx&!l8rFWM2<4H%Ffs8t^(-zcz!8EVvwg+B2>!JlyRef#t&k zl9(&s2!U#i7b1MJfW?sVDlTq ze3}+gM*k<;RH_tOaox_`IXvtR#T^q}M-RRNNA-^Kvom0HZ@eprNl!FF2w28Rbl=Km zQWTr`DmF*aC!6>(tH|^+rUn7}2PILarv0Mblbt<7YFJj#oO}fIj6!Ifa01KmX$&8CTHG z<-5&0-MXbQt8eBOs4S01{yqWqvqA-s3I`2b(W7aMy&l?$;pwn_;CZ^f{VHNMf)Ne( z6T0iZS9cQ)FG8J%$RYw*@ub+204n*_yD#pZpjuVwBDBH3L0G{tV0 z_Wa>;h^J3L0{@(4V46E0`|5R>{^DvHk!O$b#?{i%aRYh?QT2)+Sk-})9fTT(Mh$zmE3ct4Gaa~?D@!Vlb4mu8GXOs&M%7*?i`G8LprM=A8V0W_^;p%20-zf+j>D| z=8gVmMBRNqF2;4F%B;kMF>l3Rx#CcR61UNIp}jbY!i*~-d#C+LT%362RK3j;kDZv) zaAY;a(|LINM*-{pdSYg=FBf&ucU} zmd76jm7*Tvu38*dG*~F$Fn+x^6vt1a8bTupQ-TKs2ZJ4tEtnS)R&V1z`bZoHKjoAxW zPR+P0DJm6>3Y@a+ARV=o6Zcjtb@-U`o0}zlK>l6p@+Ut;!$z(7;Mb+m0oF=p#F%Rh z{WPJr&75=|3TRLc0*ns3DzWwKp)R?eAd0b8-IrVln*$S0nuic8-&M!ZHNNzN{`i7J z#%GK&p?5Y#bvT=N2JkYaM@=1U0FMrXjfgHp8MfLu#J1u#^AN@~Hj$xORpf-a3q~jY z-6=9(Wsz0fs|Acky0MDgKo%kz4CsJf$Y>pDlZu*kQ$r9ge`udIN+B>(FeZC~R!JAv z?g%UM+w$>zCx6@!NMs!I7X~0keke%*jOdjCYY(dmKP82WlAxOZYpD(L@22kBM5)hi)47(gZP<+rB$HS-2RzE8+2ds(@?Z8D5lI;6;J<;Iv?|Fx6 zQkqPHMpfO^NHM@SAs2eAip1zq7RW+&uRj5jZR-79b5?w}<*EW0>h0Ej>V-@3y+fhi zkiHyls0rp#V9s1q;_UKyI`pdJqAf&6&5rXQSa4i>TwR+U3DWzPHw0xOl)OQ!!b8BK z_X8WRgom$OYEBrOHJc(9U4C|I-r}wKgy_&3_#CTZ3iEwtoZ=StZs!DY)QC{H;cs*h z&D(ra-RP)7D*x=MFWU-cmm}N4F-1wDkc1p>=CgUD6%0V}-r;~D7 zpDx?w6LW=Ql}kR%NIn0ZFSP<#ioe=`nzu0Q&oIBr-?3Pk>RK;Y4O)_ z)5U9ap}<q_n#g}hx#6cXgQbVci{(x0W!!2+5ZPkgoXoHa2KBWYagxR(p%tz zVQiU?D&=m6Xxbpzb+;`r-sq8;#o(_} zHVj5|ERS{=FO3I!eT(&n6|95W>_Dix*}n`CB?iLUq)!G-yzy=n)s)9hS@>ltdh5^*Zfxs_ zB=#wSrCnV69S)I$ncO1`Wcg-mZ-jk`0wzTYr=KwT2-@+kIAA}WQ)QM^S@ zq&bPyjrEn&VIF%**z9;{dAs8tEYp3bc42#MV^)p`h2ql*HaZy4>J-FW;Z^XyrI^=a~0k^R6g7uVs+at znNNAtKrudR87Vm=+oX}?ys_%E7_l86(~d{&g-gFm?>ytHO)?DR^{0VE*R3o`!?F)Y zhL;1>VzaG|73>a$LsXRY)K(-*MRK_^Hl=xv1#bweO8Z{+=MS-}a|@9WA6N1diy z4f9#eXLBk-eWqe*9#JV6g3yDajMMfUBV@IyruNXoK$!+e@F46{3`PDS+G_N~gg{cR zXkm6J<3z&GJpJ zOiF|5L74{quQ*W=>Za zsF}?V_$uz_21M1gOlzjdsSLC|*8Uu_SiVxezR+#(&*qMn42B-Q@dC3dbpBTx*ah&k z8d?*(Lb@JU>hBtycEaRwoIph{vH682k!Uu9?m8pR_hi@-dLNP@a-Bau@`L-+>bQ+y zs=P-d_d<+kl&ouPjhls$4a2=fN8Ufjo4B4x8~B$N(P9kKKT$eWu?92P5Np&}V^SvU{kYzt1i@={_NB*2YKco+0BUV7HNxsNYu};aSGcJo=V~S+(91_ zA2CLgpr3VJH0Vs`45*UR8cFD`gVb-}`xHWHd(nf;{vu1heXO0AI#BX%#c(fTS)HjQ zL_E0|-Bx4gD}x_yMn=ny_kOGKy9DXAph{{H8^YqC&Yt!(U$|Q;J?}YI9prr5_7{o7 z*ED+sddRtf;{JMw^|{!hb5te+UEw3syzH09=);eux4@>(!UxmTcg>lfN$B3y<|iDN z5Aly;Ahnx~0(ZhrYfJAXN{XEjkpH6V05~`lSLuvaTsti?ZPQIg#eo61D!g6DS9e*W7e~8gG0)CQ zalAnOKn(dm>Jhfum;vg?O->da;Od;E*rrs&Rs-~+k!{$O%$;njmEH)3zKDIn` zFCW;5OTzW>nO;g9m!MI5#TJNu5kLS9Vwqi^+xB9-+Oj{{02l1&|HrxeL`;a(O^zsR zHUp?j)W(oqRRCJu)EVe|aoQqu_lWV$f0=i_7SlT%@4 zef5Of_K!8dd8gzIpJ76;KGfT@PXhy4QbI%HsOuBWv*1hBOtVML+>!%=;AkKBf-$er z3y`j3md)%zBGz30J7 z@9^)jVrOdk0IBc=w)V7f6FfR^%%S-~$_zpMujG45xlE}GrDz$|j_iMgwdk*tq@X!%oc-gqG@(IwH7>OXtGJ{^Koxbhp z=+hcWeD!PoDu$FNo=Hj3hiiqbENR+6+d|UE4^rrX!jOLub`j0oi&`3dLhPgH3T}Qa zgkdxL)OS%mM+Eeg{!?=N6~ici-{MTIXIK17g%H~}z~8S17pbrQah5%2Z9YU--8Zii z#}7tD{(?6Ji6UYu4oZ9q!{u7rA#cWgT*cs6{P4yXm~Bbg0U(ekdF+xxMYA(JwvWM~ zexb+`KG@5kBA(GLFAt{R4VV&$jQVTS%2Q-jGqXc z`xQOUj9zC1Fo5d|sD(^1S@Q-Ys7Ry{xc1Q59&&Pl4O4^I>djo%Gh^q245E$1Nf|N; zws_A*j#w&4gcC47-85-;RAfk^fV`YfUbOvnWt)%)GR)fq*vfu8S$X zaP!*vd`#?Ni|F)zLMDMZ2*aFzi*FKm)=u@$5aE7UiUGtYbQ6lVc~8l`HtZcu{n_Jj zyXt$I=lTZp`t70Lylk?xzddU3D41XiQxRfIv2AUcW;iX-R`~q!arlE%kR~SyO&)7B zqjE~#che|D*_NiBk3uuKvD^Br{WaxQ|C!-gH|qk#5>JA&Q%pG_EvEeJEZv8^?igw< zQ5*AwBiD9XzT2vHznK`6-F$A%RIpCLmfHI6wSi4>H4GTkgnqt%3dYG|% z%+Aio`9~bvS<$b&++Zqoace0{Ey3GaFp0c7?k7DQ(Q5Ao?cr5u6Dj;5QNKRA#op*=r$ug(JYT|pxiuyc!&H~8 z3y$76x-&9JU$V~;>8XJK&HwiT7|o@76gkpYRZ9Wy-hIAKLpanFWgU>=Rpeppk%m73 zZ#Y-N_0H(i(SY&9H7kL31e!qU5?GLsch$y^D+? zAQCZevR8DSIjOa4Osu3~aA8=P1y~OthAJVASlb-J$@*!`xvPIv2M4nR7#Wv{KOH?+ zbf)@iS*cl+l-^YS#zk5u3Ag*hQdOHhvmD$e1LG^|nPx#Vl!!aJ@gV1y`6a7B;=T-D%THy}wsd(9O0rQ^J;zZ3x3_XiH z1E-xvuV4eKMv9vW62iWo1JLLqi~K$1?v7}0tMJA=@~CTxK6aU^-q-nHdA@YqPX9x` zREpn|mTr?z5w)A_F)#Cz+qB^NZ~%!mPxlbPc~MfL-c*H%p%?rjYPt%UTWz1_r8{-Y z-}bx6(X>x=&n6{-DDLJ*AN9xK`M)s{P!H8t_QWK^pI_^+Myh<#VmCw13Ab{fwW6=! z5@vs%a@}r2g?u5D!VV;eU0Lg|ek0ald*#qc?gziX(?nxX)W+Kg_=DSJ2$?0@{ znW^rdoubp*#OJfoI2-2Icjl`HBf%ce1a{FaTk}c>f!0OL17DKpM7Ue3uhc1Y#K{6i zBOt|`*aiM5nqZGAz&W1mYu;7z&y&A85BtwcA_>+=?l+MZ`EjxEm>X9!wrU*Qt@vzy zPziid(_LB~KmF)nc2#1>xFvr_9s0cU>q_lx2L&ni2Xo-Q#7MJDr`mjc5rbj_xjcb0 z>`QDB^Xqyz6SYAg48Oh$x>as|CN>bixu5v;1uv%bd5&>P&adnM8E*+=xKI~n#?$D7 ziWd8=`>nw{3a>S6i94QNmOSc(%jjC_j1&Lon3y-l&EvEmQ9C|+trtc*WLnB$1p_0W zH!t7RzeNF*p^%cQC_N0mk)nQo6PMYnwsOkxqOLI#%qZM*#l?qgu=rx(q-&ZApsNqx zq5lAR`~5=z@u0Y?@eI#^$-9HhB%FQ4;4}?=8in}$3k!#LAdYWi)z1kE5P>fwk6(VV zN7LoDf=gHN9aF6M+*{qy!qg?oyWQ43|I9I# zkf;`26m4-8Lm%$G9W)&tU-{~2S#p!d{)R;HR;>7PgDAsbsbbRTG`K8(v~5ahF1$xK zz-~7!Cpkpyoqm5r(WyGQT_on-xBTpSv{$2iiB20f6{mc_YI!kX7FzX zlv*}XYclM{oW<%<0MhIvv0y-1^z1+tyQkjq3i8LiVY&CeHtC(fLdY#g_`mw0(UjMc zVM|lI7Kt_fMORwRhm|;o07?{8w8Wp zI=9LTy|loA$Lh4v0CedwB{-jS94gHzLU7h*4&o-^lZv396?4BEcN~9z)AP`3j(N5f zQo1X$ts~2L|HlwH*gP}>+v!a$sEd}nx^?Q{a`hF*E3muN7IRf6JLM;>TCL>pok64m z)7kIS@{aQqJu&6l*%%ZIQcWilpS+*{7Qu^_mY%U(Q<5@&=8k-leoJ$cP#f`o0Ny|$ zzmE}sKtI)g(udRg}3^^PLz0J{W-k2t<O0%1Z2RH)TD9{0s&NL746rOUj7o!!R~7FEs#!U!(;;`)1Am#24t768s>0Wt@Mw&Y>No2%MP2tXDimA~xapJ}RYPj~t)Q`Rq= zbxZSWFU)Mw;{~dl>P5cc=m=%K=o_E&9Rd87aOtZ`+15#|I};h z?>^$3+7crE&<1F#$`n*?l#)@F0Km&!>(VbL7dB11bKJo`@gn zrIImdjeHP)QaLg4-AT{4P%D%EV2YmEe&zPN(bkD z_r!|#Zur+;mmP3&6*l+^B;7#%pokI3;=XXxHX{P00z|yNqQkr2=?$=HPs!f@@7Mn< zuXh&)TKPC!D*)Y~uoDs#Slz!Fhhj5V9culc`u7~9`&H$8kgwrh!*+YdF@F@|DyiR_7&+em;xl20)%TwW}-xo@p%LwfU)%vpe2~x zSX^LLji!lo#)&Cmz;E~_k zjp{+(ngAlSNb(~hVfFfwS)f|aJ=o?yHKFOo+8tuAA+wx`NC6vV@>)$4VKpMp;c_k` z79eBlBJ@$>Ub$gUhNH>y#Fe5M`$M~xnO|~BTiK}1$Aoi5+ zePHQ6H_Z9(+JjF15Q%;MpbEm@OY9#Rk5E)3g88Z%`QFGtApGS^athg*VaJgt`f2AL z#0|$}Rf$ypd7t_{?@a*FTqX&Sl)*?7Y(g_=9edHJ(~iEN?)KJYR!O25rT`mLh@%1` zxJK-m#*i2~V*Ehvv16OxYpv9#YGazNo$}}AW$#XhGANEEBh>vwnz9kw-~4PiXucr( z28=*yNh&`esQ=NGwe>r6Tx~lA)zyc!>Pp$SIc5VL-kP6x&2&)Jm zxqv`Mn?u4LK4No7TP`q)y>pCI)q5wN_w|W4{%fc0_x>R`0FVVehUp*_$GOn+Jm>=k zi@0<}7u$QvrC#2_QA)#RCqa3MOXMPf_#XX7ZN{xV&b1evm+w@ zl823c;Pv0xIQzc8HO_fp3Gz3@^4ab`*cdYdkU@pk0AUJ9`Vw*jq~=X(mS^A9cwOb_ z*xXc)8^e~t92GFw3>u-=h`@`c6CuFirscx8shr_>u|-E+vcsc)e{J{OzJ37GyyGr8 z3(`UKg^w&mATuzE9-~k_sq`D))AqjM>7%we;phLtmT&)7&G>0jU>5~Q5`p^|c`=?$ z__J^*hp|btsSjlez0q~tsTlWoAS)!u(XW^}z6{W80vIsk`b+@{Rksm1a@?i$ha7!= z-Gr8<>Fuyr05XFHq&cPu#N=QAb|d;LjVVY25qunhW=d|)N^gF}DE)u#|Bq$=dTVZn zXT-6Eq(~P|bYw*LHa8kigdYO|!Y}*S(X0Gh2bFDq_BG?vt;;S}i^{f_oHYGLNrw}S z7XM~*W8}ZA7m@UD!ST;DE9xF^oOR!+E1rH(QjM4k=|AM+`Nu2xU;>dDfB{n}T2tT| z?%S51_}T2>b(Ncv{}1Vc9b^sE2Ry=Ga}NQ*;9;Hcb!;5ny~=&RcC2yJ-M?8n z_072*od_wE5LJ+Sy&ge5Qj9pvH7W z5r4-^_2{vR`hPdfd0>3Q?0er;;n)S@kGL)p-I)3lf5kBS_ zM_*KLA9H@)l$9&er^bs_3t9k^wE%+vm&2EYD5=V=cCYu4QmXv({@*P9^y8OSBaOI% zT}7E0*b+l88!;(xY&pK$e!+I<|9MQTeZ|>QVeuJ~>+XcmF4FJQFBi-kvGIK56#^J` z5%QPpmg3c8k_~evU5J?fiK>|}Ju8Wge#q+wSwZ*^2t*7(XxwB_)X15Nk?AkQ4zlGXw($G?zD0C^&+)tGXb;YdryT|MUI+ur@= zS>O16aTz6)LkQ_W1W4XsQZx{iFZ(WNwxHXB*XmzPjU|w$K~ai#_*VJ8H@$G$jwfII zv}tuOmCb@bVe9qS5`hDbgZchlYO8fR(osf7wH&617+SexF1;g_fc#Iuf^om z$u4(B#c2Ju+b;R+%@1DbcJ#Os5cAmY2$he~h0Jmlea+8vMo6a<61HS82$6xXhJ-z2 z*O!ZT+N*e%Oa69Ls-@vHwW#JPJHhzze+(w&v^F^P&{o$)UHXH~2RQKA<06_#fwQoVn z3#(**!F(XWCp=;%LNQ!LuCG>Ve|}|2y)^bnM+?TOzH(>~IM|R!@Cd+6oFj%Izh)cv zb|3-~@%KRd)mT;i#|?8Qjc<7Jp4XA!N%gE=mx=#C$inZ;01S@HI#ch{ed$_$JeE{|fNJ9&NXmPQa^P;p4dUBr~msdj(CQkKe4B-iJqhatYF023H5$D$4*ZzgQU7SM$ zzY4m1yoUtj!rh1n7Cw=4lDtyaafU%TM%Wii7#R920{;hS}M;CeN} zX+qS3MLVjCzVpEdd>I0Y;soimrWBW5yJGIdYg^||HkDZ168kz(_$L$pL6KtyU{F{F zzewvG=8+=Dz--!zx!pM5I_3Or&abvBZ%XTSOp{dE=ZQ8rczJ#`!we897m_|UnUb)- z#7C;+J!7k}r=52`dFi(Q>FI6nkUdpFsv0a=2tOT&njowhdK)nXLf^32n1XeQd()h3;sY*o6sFK6>P;X7wm-*Q;+D?XK$HUri*wEqU(u1JB!|pDmxV@(FdW zsF&*CgAw?01cbH?TQ#R)E|Je<*?u)_eABo(P_^r`=TIYB4`}I-K9>F_m_oH#V?JE}kz4fMgQY1f}$G##ZETF2$u# zH!e$vD)~Us<(EPD-zY6tUb$}EN1wMXOWU~S`A*=0w*-L%?+JgV0Y|7U2rHsn{E?6 zxKWq=0S1AMFAYRM&`d$`=ywRB9b^~+5~>5qFytQWO!%hnN^fJE>c#2R>W6mKCm;Ru z`$8`}+ZeOJ75SJ?GO5jm&)d*X~Oc_alJvV~d@CiwkP<}KuG*G-36i7 z^wR1Nnx1+1^oCgz7J~T8xQ6U4O#FueC^G;($V#6uW=o9S*d7dxjJpzIz_D6vvV;po{{PR4V0%=WR0b&>sDW@ltR@j;rBk#b6 zw(Y-XmdJ1X`NR)D?(T4HTywxkIPg&smEjMv6VTqa@L}y20i_ib!)wZk(>3goOWp^x zEs{_R2zi`{w5BR07hh^@Q~d2Z@Bp>YThv3ouS3yVl&?qQM&aKsu(Oua<6*OL5{K+# zyHzkCqF2>)_Du9)Dy6)s>9!sz4v;Ji+Q8_B%Q%E7!JdNfgt$p!ArBQO_iFV9+ z;4jC2{F!Y9R&uo<Dys1y2!*nLW0l3i2%)iu%e1^ z`iqrSOmCbw<I`eb2#KMcIQv+OCx~4au*4N}v+fj{^M; zp2z*b;E$Uf0h)s{Cbff2pme>o;`OGvlTTSO>)z!c{yr1`jmMuEfQ`p)ttY_*P^Li1 zEK&nxep64pyuSLdvuYpdY<0c|%c_lqmcj$WiM-a6{Dl!1b_4{w8BI3KP3WkQY{>C7 z)l-)%<;qFd9rxZFU9C2l0O^DRr&i_1Q4e*V$3mOnA!H4uL#^!UdlMwILj>f#~;5QrFnwH%5Dl;N*K zKmf!)pv4r#UxrhlH2sFgmZ$G-x}va5|Hw`UhN{UF$4!zGPt+mc$wf8^0Wy@5I)G*z zZGI|a2SW{c2E4%Z>eH+2m#&@p_0vkK4Gp;n;2j9FKZw7q>eBoFbN)wGN83@@gr^NH zU`c8z>gOx>Jv9Nz5d3u<}1ppw1#A_uR;?*3>Pn|!+zh!OCRsuJF+ z@K=$0R@x6;`^0WnmsIO2nE@1yBBvb6%^$q~*Yn=By4uGf)X)}ARYQ6e7h(j48v!hq zNE_|Dwrm)QRCo7^-#50t^(3NBOwUPSZjx?`ZT`c7Ix_&nfgn5v<3l_z<==jr;NlJghbZZPnpJxVOnC5_RnkFx7ByB}_bo#lM<@n1_cTO?o^zV@&)j}jT zq%6FQnSfzJoU7RY1R{+9QV0fmET(!rtK7d``^4AJ022_vj4HHKf$Ja>Kur4V<5_>v z%z_tDKd%oP05TWdR0Oa%3da?YdCykVg0kxy7EJrg7xV6qEAd1M{wd^~3gS;5tb7ay z0zx-U6|D*R9gb@Aw~ml`l`vni4}8*39DQzG@`#^R|E9OcyH3@?0my+$8^W+bAb;n` zR_?hlN2#jpVowXLph7$N{~i9`0tZ{Cdmd@2khK?tf9ET1-`yxFepA6l7n%G3Te;c% z`kY39Y^0QBT9V-L@90`d#cj>cK6KHFnfJuOjHXm`*p!IKc70)vJGZjr|FOY#AMKH`r=KaQ@KluvG% zD~P|T#xum95>qkpANI+>D!yTdG_#H()H>A-CZIq~J$Y-h`ON*TR~J>NceoD4UdxWK zyRtc8Goa5^4grCFOn|!W&fCh@SJWHu10)RqCZ*>qSa`kehL8V_Yyws|qeZPeV}}4P z7uf&=Nb(nIe}qgsj-eNqPivY#<=o{@-D7C+Ld#1b(GP7TMPVy^3_AjB0vL8k*HkEC ztr4&iFH*q_c;>N})LDn0Rr7H7O7|4aP;Hbn#HK950=wop{=^7uAq0duK!KMaHd8y| zvhOT>TD*77j%xb8H_rVpXLaXqz#WhT27!Sm>lPZDJa`R=KN*~4$@U!EfW~Pai2o@K zGw$vp1~fzbk+6be{fB!punAze5gnvH5o-+#Du_Sj2(V_}+@wD}vGv^IDs2`t0R}Q% zU>CPg=o;j4zQhP@ZUjQtAEdHE;;R3x8WYp7Nl>T;0281likzOMAKfNPaAM7PxuQX= z1=!r9k`MJqlD{lDo{;>@UTB^-Wju&~Cu04X_-~;(z$Spr40OL70WvfJ&dh%{TJ!F2 zJHNO>d&zTr1NtuqTe)&n%Vr)2uKvpr@Svqilxlna{^4DJg3W~}1DoJVy=1ra!S%mv zluZ2zBoiPHY*fM-0>2zdzB42Qi1-VKKWt$wZ2lFmHqU!}{E8>+P1iUVPzsSvT6nCPC~2&<{EO zxIqZ?5r2gGf%qFG<^OJ;KlPmDGbStn@t0uprz|Qg`EL-Yn^u?^fK9`1gPx9%08LW*n1CA_-X(JYn1FvHS&#+;8T$lm z0G?cQSP&3~f6tMX?ndP-I0M2SCF)hYbW&dR{-W0r7+?zrK~Zv8-pQ5ZIsyZTzgAqa z5XAq?6*KO7dkyiY>{A}+_uUF}ff<0U06M+WcO@SiUiC2n({Ehy&isij=fW{CqX{6N z0FuhKQUm$5`Gvq5qOQh^{Lcdu_0E!sRKopy<`Wbv3c61^ff@3n1?8 z1|zVB_`~53l7DgeLa6=EYvN>QY4p2V5n#=&S$*La&nx^i3*DvYwR?vWkPv z0QodGHm0wF_!D^v9N%+wy{P=n=6O?3Uoqp(_u%jcn}4MCXX3vx@DA5A%m54*k{emi zx=cXh`|}=XIlZJpdtERAY!28+Wcjg;L;%}WNm8=MJ`Ul48OaQ;fL4#E7Zkh_s-$Rj z-^kkevD`@KW6-Svmg+r373QYN9|%FLlF4^D1n!@ zq)%@C&n`6X5Gy@dp&klcCG+USf$fCiZrU*>{m}rCuO2`usJaJQF=Yj`YPc5p{9(L0K zX#$WZ00pry^T7ygE(G8J7$h5meMy`nH$XbPRMN$l_rq^}o3ryw3<1gCzk>KHK0HXF zF>++Ru=obp{LgQkH9-gQcR>8Zq!JwGpLKJD8Gx)ClC_cr(FQ`^v>R7=&pgmPuCPMA z6Ul-!{N6lh+Co(=vjSN-f+M;~p;QmY|5&lMn_x6Ea=xr-uBAp zGXs#N14GTC-wo9L!~`g2p=yB%h(B|0^RJ9j>+f=rZXoX{n9UH-*-dHQZsaSO@u56l2G z0ScG_KXJfM%adbwFFI+D1IzxJPWs#9z72Sc%y816@_^>?eA&dVLv0J^e?v-kwOeI> zKIvmu)m8X+iOpKd_WsB0Hm|!YkoA}*Y`eU8Y$Dtv;&HJHB&2$hGn~LlV>{o9HyRf_ z_Nx_7-0_m8NT#b8mgL$X{tC7{V`u+t9@U8Mvj!lWN2y=iND}}JED?WIHwRi@LKg}r_ zp@7wJ=#o}J3ZxQz1~>Lph@v~3BPLJ)tJJBAVW&0ZBV0Gl1+;eCN%0%XZUnpUNEwPzztP@R5L z1TFecO{BV5i z&rS)0JTL+Bv>R5qw&R|E#MxELG~KvzWusjhOW=p=K`X!qBd~D@$cXzdUh7dZ9tKhh z*O&oV3z(exB2@kovi4%E9pAPLt)T4n)>mi$XT$QB=Xx)`mQdme3iG2dKyprEv44aP zKQjO$bl`~D4!j`1?~j;(0Gc>^)-BC(U9taMSYEhvpQ9_T>FsoO&m}tg8s|^DtYOCOOTiMv zm6)0IQ%SK$gostnoxbN?PnZE1;X@ARzyN*$GHK$BxRRRw&s7OYS~aP+(_3=rIn{sd zXmEaLBos#pB&6#Su3O9mjPNnvg3ZSwADH5O{n$qM|E1{~U82dvM@}F>njIQ%*!y)G zs(nK?4M!^|y|HoL)L%C|dAFw~3N_D8rKGe)_I{}R+0%cdk39PTjPwyi`|&#@CLpj= zzG9eiGC|IPH#WTb%zZ7Vl-BA~Z7a~B0r24*2%Q11+c_}gTe1rSg#=pl$pk_PCPkWr#ly{YW2lYX86=cP{iP zFZ+;j4urWbP;3j%frW4myrHqpbNy2fIk)CByTiXC+3g#+o`z#rjMxDD&)R^zoGt6^ zbR_RX9vrwvkf2BgrM0L(&!hE1nw15@aYI^h48^qmD#o*=tTFdi~Ut=x)d~VAJ zv*T(kk@BowFb4{S7qPQnRu1LHHHsO4jl(S~pCZ2i*!e@}z#h*-`e5nFo10^4&;Q+i zC)BN|tJi+r-jv?fOsHw3d4*p9e)#Mckdih2J|b# zA4dQJLh7e>LgjCH$!^mqRDH>;nArU6BY#{z8EdNjjvc5+_LK~vBd|SS@nmGX7?b~4168g-4ITw zG7~UTMt6&~Gn5QN`W~tKBdq}JO(6q7{N@&G5x;0S5eQZOnvUfnpzV6iSY`c3ZJ#G^ zbv~JV(~6n*OIo}@_tQ=aDn+a$3fudK6a6fziy43{8jRs=O2#ihwvxVZ4qW_Ho4NQY zX@TPei;g_2=1<90()qnfTT2wF7EA)d(Tf>@;T(b7s0$RLH`9|)*q6atN^&8tMNC3& zbTmJD5lQ{z=$8iJk0FlFFv{wlY@Gk_f3JA6>s4vR6cZ}{w43U600}$$<@mf{1|WwA zIKu=;1sHsj`om44d4Xty)fMcdU@U*5Jny;Gt{Bd_il?fk|}Kn_oc4euIK zPRc#~9}8TEtOCpc^ao{Re2%1kYONJY{i9mbQu2<5rzZcoarWIdh4{&_IH~+$N`$3H zxEF_f-^l19w{k-fUxWWdpz4$QnuvA3KVJPT}|eD-O_D)*7*NE{M_0< ztXys%iJy4@ZGZ_b75OhG6)OZo-gpW|u$o$XqWVmr3 z=KA_e{Vvk^Dsb#H&C0qL8=s#14=Y$by?yq>8n*CNy|j-Izf@@N57^#6%;S)i)iDE* zmBTW;jS@@%oC9I8mD9a`5KAc5(kW7Wsnqzg=Srs>d4A=8s)~Mb>#}q_QKF_iCx~P5 zXKldn4#SA64N0c_%29grnuZ6G-Psx_vRX_@--%BYe8HLm{3&}72!}>^_*433DFsKr zxLG~g#%?|n8sln<3Z9R0|&!y$fII-r}YS5KO>8ab=$O-UX zr1V~R%rEMG+tqBJl7#PfL6Mqr(o!7R!6>|tR(oawvUFUBtyv)%kYM|lQr&LboC&R# zs;jE!c%Bw7l(rjp#aO3psU#aFQc=?$)jTDJ4f{>6sK3+?O^s|hz1{J6RXx0Wlt~Rw zO}t^nta}#XHE{HcTd?)hrcRO6&)nRm<3HkRUG_l z81K{fJih9U(POlqH!rhxOq9sB9H=U512!G^5mW<$z9rfV=2Mu9B$vp9IDFaZShkZS zBV}A@0f=t!dFvw}q<&cWklqb({i$BlciniRe!G_%X5V)o^7l+=n0-H_{x}$VIaK+R zkq-_XVt28v-+pevRzQFmfUN*}HoS76NdRrKu-L0l-qt8B`PBK__G9&zkH573l9naj zxIlNLc%ha;&VWc8K`OU(h_JWq>sh#4*N*dY#jkE#u z0GkB}g`7QHCl$OR<^~MK7;PknDbgg***?=_W&pPC(DzsOqa||Ev$_+SWT-~n*Jd=# zxZ_Uj*E4GcZ2idQ7e^Z3Nb09?W2t}Zj$PKgof&|v8H%mf5Hbni@IlaJ3sC}EOi4oh ztIxQlF=$Uo|0q@cmyW-@?w4&#q@M;z8Guc}l30Bb&=Q32cj+lO9|9TIAgnf$ld#%&O6gQkwqo7c=j9pmELvU!Y3{Jp2*K>k{!NPF#$mu-1&6 z{&GvVFapc~Y+=Ah((8y35H^088c7>q6i6xASM@pfw$kD!wZfDw-8*w4cX~hrRfF5 zhdZk~5gh;z5nRPT9@ot?rbPG&V)E>#A0e{%^jDw)i2H3r{;CoKI5V7Hol$d#I zbK}Zn&tFQ^qUTTh&FHgwy1nr$8`JT4u}0c}nCJFI3y^;R5ep_5n{TK#y=FnuWq);x zGyh*xS6T{og&_4mX#nbu8ef6f@?B))1Oj2wh`Zxs*bx{=_|qW#GHfvMSVe88k|>?L z{Lz0t;@GL@+MauiUJi9XCG>%lpJRuFpBS@@3UAn3#}(!r0?Yv9+>mab4hd~QCL3U? zM|giH;Up-(a$gU!2==_2eEYKWUSu*h z3surh!au;Lw4=-j2sE?k4+--bh4JcD?z?mDYj~Lw0D<^J`oso69c`-H-t=?u2UJQh zb2zgKVAF??ocn+y6l@{kw*}$%+=Nk4s~aViPcD78-5pClvVDXapj<&l-S@hL9fw@fW@U zKtfLSc(kxlI$`;ZCp!@>AT3JvIExAj%=xGMcJ%Sd)$Um~iHGk#~g-T2HsEi*8NfeY{Z60~A z?<4&5vr$6APdWSLz;$9qd6i_8);|}sNY!uQD--^aH||;8 zV%7j;_3(_G#!;#fFa+c!5EyZ!2Mi<)UgQvOq}2C*R3#OaDnIz?AGSSVRl9S}sz#?A zd1ahlDH4m>GQz3-umRm0py zTqm(rjMmm^>uqu zKmjnpkodJU2tTP>nDBo!#(Z}p5nu*jBZ1?`){lUYRftlA!V0z< zR>27P2GRy2qlx7F;G_yUR;(R)#vivid{u+<(~dT`T7xkP8i8Ig0&#>7hC2*EpMbik z7D0^G|7I?+fe2uiSa|*;vM*MwAN|XH7Ct4OCquuP2&xbGv044yqu!EKZ6^qz7$F+b z%;;|5i}Iv6%~g`p6{;pZxhlf1mC4cF${jX;u$$ z$%w9qqdXCQGRWW{RvM>hr54xt4y|Io1y+h`(J zzvU)-0;Eiile?c@RNih@j+*@Wl)uk$dpqW}zcR=6U8GqI3I7^ZKNJ3S(d8?{jsPY875LaV1R~<^_I7%mYE#p`f4E@fGE^mq7`FZO>kb>Q zRqVb0yOL^<^e?u5-ndug>{Eo5V2rSs${A^&CTY@7TLvU~Jzg^gsTHL+45M`V(#LLk z$?jSC{L0s#1%qW{j|JTT@h8F`0VKFwmiQw$_`Eq0UtCk6V5 zKKd(c`=I<#^=oNJ`<@hd$kQ)LT46~lRa6% zot*nb96!Ovgg;LvPd0Uv8GvjWjXZ0MU<6233MEd;7$~{6WO%M{7PR(0vJ?r22n?W?S?2 zJFfiX!grqOrX7AL#V=K$ZugU4jw;#x+i%N;SqgYb(-NY*-s*F*Kt%MZa~az{+#$#* zQkIaAC#@j|zhTylMwV=Ov0h#>b=e~~zm#6t_WG)K7OaLgfSxlYFs!N`PiNxz=^7D! z(jd@hJ{W;5iU2bJTNG}7*U~SD*bD zUjN;?E_;p>$XdKmDmm)xV21;K{D_n6`Uz6e4y+?zzK2vfTqXMveQ2E&HAS)eaOjH@ zzoZow`SIGZGq5G^%$_BS-qMPT7q&yqA0Ur1y!o+7j|O9w*w=6^ej-H52$0UarMtKiTCerXe?dt~VZi`@t(0!mmP3Cp}s0BcD#GV^6zs$I_j@ zxj>E=R0W=w4~~CC^fPLB5Pf`scn-ElB`TlxZVofT|Yi2Kta=hmdIDNGTgJ#qH_xwA$@rOO`Bg&$;KifB&m@v8saj zd#V|i;E-l)d)hD6n(vLW-s z9rsfyHCA0ul76FJT0OJzsr#PmeD|dfjM9qtS1o+O$$0O>89>q??Nj`IR5!hV)%*fx z{*$c;FawaSBg5@l3xR%GfxZP6S^*ha0c?vFY6XnAEbV#k(K-i@F0k zn%%=O$#-aLu%Q_MGoZ;S+Uy35Kn!GnoCx7Lh$@K=tetR(-apc*ACw~Gum;A-RFc@? zFEM&Y^Q~JhU2;Klv-ei>`za7E*QK51d^;W6=F|(*)%%}#ud}lC>>O16)4b`E^2K2H z5q+7$Nz$;-`+;p2&{WWsm?X#IU9tLYXEx56xB$d{k)aDN0{{RI%}GQ-R8~^8xO3ri zJ#hA;xj~#lOl-#sOF!_VYjhCNr!!0YgaRK~jQ}$MSv@@5xPb@^rWMdl>@Ex!0Ic7% z=Kwidd1Nx!>)=wev(?!N=dT@m;i&I)w4@J)kNdvujkW|;IQeqh2tl-!K+#cw9(aKg zMik&4k&89C2E>XkuHerIIEcZaL)M7tf%RndD#skPLwx3Kzx?#7cjk3|gnI^}`fWvn zeap5q3X^KDrkY z{%A#wO7oc!$YKPd>B^Y^vv`oWX@es$m{!05Gk`+}GeEX$;c^`>5dH#cSG%+QUPqRU zJ>uNDeR^BngB4l*0a6g_o(5I7ClgbULPP;_x5I%8Uwt^r!2vN8=t7bbnhZjAAlsM% zYN2Q)nK_8?qYqZ9+cV+tCym+3{PV4sF1hucXFHo<^G7;nWL}Wr!0x8ZvLnv4b~x)9olB5K128Bod~qZpFql?=NEdA0)D2<=NSY;PATWy+X{YZNO4dqgo7w}VF^8R3xkF7) z^>@64y75Wb?djcUDhyd;k+(NudKt{-$AS&5r0Vjm!=E9d+;@i zllp^d&^0&PiGM_WGw6%%uOay4xCbd8h`5mOU2LiA!Z{C)d8!7+2H|?8l2KgI?xvH^ z8O5c~w>lGXOa; zjNFL`fx(yox~7NBfY#4U;7SQe+V-2eRH_seq&;W!o`;ohQQWAPVk)=0{SPk1QaSS-o1uYgn0$XT-nYAhc;%>=V-~9axbf$oJm{wFMT=iasyYlEh+cCc~)jfZ|Y@rZ(*C=H`-sFy#&vFf#Am0o~0j1PPZWX#6Xcv-vjsO&^A=? z2Z;K}L_b1;&pCwvGXOa?oZOlI2n@yy5P>7j04Bcbr)9)zLB0p!r03$LsF$J;|G6k@xL*oqb6aVOp zgZv3deb7%^K$rV#4Cs4w3`Tk*0*Lma7^=cwQI5W5h(0kIR_|&LUTc9Gb7NA~&gwlk z{^H|%Z7X=?!&g_64%@_rbe8KINa?Q`UMgvCciI(t)pw7&%kFABOVLc?a0Yun9^LFm zUmx+N@N?MNOkXpqd^j?Ymws4;cGi=i;y_{@$Ce@GBhe00?2&|DP%Ob)znuuaSyl5! z+ewW5*BmmgYSjKe zuH3Z)$%sr-*#(Tk*SlA_8j=wSrhqg7pjGs{vXI)?L$3XhF#w@Kl_6sQ69OgN6{Nh6 z-q0Jr{6IY!(l3algZ?y}w{JiM2>iFU%Kd zWK0IhOviC8wcY7g?OeI|MX__?QX7TT?sHTwEtL zK1ucB3nY74A-@Eu@>Af=;J83#0DuqYeZ3F_zp#9{pGv;1m({%9_WGPfE%P2}0JqSr zXi9UcajBnP1=_#nAbUQ>$A=T33Rxe#Uw@)c69vN)j!nPju6dq6F#ZFD50FmlGU2s{IF4gU5O4zQwEK#UdAPlkWsPk%S z_C21RbF>;<-fscXi^L+ zZWzEp_!W2sI7z?uj0?R2qh@^B*Z2FK)6=<&s+*MHhNNIRqW6a1hW>Zm=R$i4H532n zL`CRKNBCK$~wJ3PD!*?T3zU9r;5Z{bW~z*sp3-6b~LuospTeff4Kj`{o$V9&hvbp z^StkQ-gBPwyr0ke9(cx~b%l%v`1u?575eg2oRP;r#=1vmjs2OKkiYh_A{suowA*Hc zOy?z^LQJrfw_55#tGr)n(0$E{euOwtz{IhlMqc>WE}MXEy;?R8h6`L1Ww}Dc_jPk$ z!D)vXNF-m;hmoUDv8`Y=c-U=r6GmLAK4OnOaR`Hz#qyPD@=GbL#P<8B`7rc|LRAd9 zWz6TI;fpY*)~I2L{4{=iT3_FZ6o=uB7beZkP=EF7DiHfJ3bw!^4wf*y%kmzBEgbQ< zN3gRuLghxzf6Zf-JnU6kFL-)p{q+k*Y)tOsg?0I`kHyti;Tj$ekocn!{(KE+GSU1xLKt;Bq4E9`eEiy@8!uZ%fu^snn~-m_9fjCM3|7FJkng{lJXD6*Nk_GtS@JK$Zy3A7GsW-I%)VqG}Ii3Z#FfKDXQ?QIr%$(qpmpW`RVaR zpGmnlRwq}#ENYh}SBEup8e=;hVt1@OIs`E=x%^dO^g-UOYf{T9pWC(#FEwvp?^vG8 z0GG36Oim8ztYd9qYkki1X}=4jLp>waS8~_b6$a8jL2s9Z`U=c1B&zYvtW5FcXF?;f z1?+DKOSjXLPzxKCp4ESS`Jj3>CYG|y)9JBD{VDZsK+#og;oV9ODM4c@c<`xN>`poc z(y?$wms16!ESPg_2sWbJzS<#InmVXjx{z}U4p*ZKoT!ET7okMShFn9|$ep5atUz#+ z{=_ASslZI?QEN1$S5CzEhulZp%7n!ck6mfiEVri&Tb5h!f5~&`SoTIKN3K48iWAz+ ztUAcTsN6O#Lhke64E!G5Y@E7x92+sLEq`owZ<^f)fShy2bOR#LrD3)&vA2t}*T6~zp<)=9Jv7gk|sB% z?E7a!#dhB8V8=UX%!PRQ;Hvn64bDLSTiBZnr2fs(5!qMK%HvwvL_32Fe-#?H+ZY68 zQJ_bkFapNX{IufmiJ`B%l8Mqa>WAFV{ih@|hiw7=h^O@{Xv5Lep3)AeZNE_d3W%pC zkgz}lx6trD8i~0Mk6d_XKPbZl5RjR&qiX)^4S_?{UoW#9rvNM+N88EG7+ZCibBi&xDF ztXTOlu1qdd71t_F*66Y4cWP$LznMDrdX*GnJ>7bGI_T0GCzGr%#kxF{{K%oWFj_)p zxQ^#pzGLbub!v1R>8VZCkR<`ywGYWa)A7V~%-qtLi>TeBE$lL|iyhz5H4CRZgVgs2 zA&t@^G2Vm(>|K;tVFhRVb#9>n0X5sC#v{n%Y4$Pi{@VRv-S&9 zt?^8O*BTIPcMA!QV+&q7g(`xXH2?Dh@q4np9h)$mP710Y<04xG7a&hYI8s%8G9|RO zEGqCw8718N(5byYRGrY^LJj~I$vgmBFJ3(h>P|z(kQ?*RqW>fZE*5EoaU*{j)OmBf z-Jt8+{mT~_1MPk=63K8+=CC6bfd{T4m_O{4OKka7ke&5}XG4YFcjsOfg7hCbiAb(s zT~|tt!ju+dGg>y+{{R?q4w(#3y<_fH&vhetCHy>VFF3K?@d3(f-(TLZu`J0Gbqy@e zb+hFHBp^^p;S0Z^{sZ6P?>I^pTln6k;$x)bx@s2V83T5Xq=V>;c89+dl?j0cn+Bxy zIgrJp;If={pF4o!O3D%0q;N>inYIMT63}qNOzdWmo0xnCFl4sXkv49HEe1dE*DK+M5~F2x8@33o9RnU<#@KHu)N|57B~Vt^R8 zs3sayVDCSyjqtpG1?ArT1bJpjBmDgo`R$(;KB*sQZoYnh{an&~ z{FgDWd;a|+{>%0A>*rp-zvw1#@c)qigg>n0|9E&a_Ycg>U4EQCkKeIl_0Dh{;h`sC zJ}G9$j{OI46_qScLQTxR1HAe9hXfppHIE?kF){n$4?F}Gi-#cfF)_2ASL7j!#e&rR z`ybT^eH{29#*PGK=93U0=Z72moML?01EX&@j= z(UgzZh9U?pD7r2gS>o97uR|zOmqFIR3mUrZ9Eu3@GPT%C&>lRBNGVBG$;i^`y}NP{ zL|&hrt|1`5oATNkT8l@X6UxY$)#8FAukBuiC?0v1E7G&+wWWSX!V08BWJx7g(d?|p zrBNg)$imWUerPL|hgMGwhz0&z@(UU&fdBRE1iw-QX~-^>0+%MjsT2g!@_9}0l0-?RQjiw2x(!|^ zl-xQW#B5cXfqzp*p%D0QF{=A9{)@5%3gBPc->kK@E7`@lVwoQJ&(RKdI@=6vu2Crf z{*%Ox(dmI!y`&-qS&+w-N$M*KF!t|0crawF$HxEW{aF+pmUHsTfPM5nieB!JRh1`% zQ-J+DDB4j?%As)zeZzqL^Y_rvY+yf$5#vQhkn1Sw!q|^H7LOq9-A<>6{T!+f68qlA zWO(tBDzrXG#=EZ_1NQBFl^#JHdtZJx9b;cCw&;NU5~^fYsTeKhUZS3mR)*_9aklJqnopIU%CS>C@0)>EIp%x~1Ojz0G|<;ALpDE7EM z^&~v6g3nhk%%SL&k0IfiOcd=t@X8-Q6+nq+5#+TS=+NJ>g1@Ul(IW_Q3`K)IbjY$F z(b{JaJo;emqxaw0P}>3|5qQVF^n`D7qK^ z#zVsi@Biz72}PS1!~2qY;Ai$0{2E?`l|vS%--H+Rp=5>U6@IMan@w=u>1zm?(PRup|$6`(~fch0m~eJ?3y0q)UWtN|Rx z)9k(b_jlcYnmeov?>-KPyN`8gcK4n=dk+40{#+~$7wf5YZuj1VgrB!Sj23%lV{0m6 zJ$>fw-V>d$=c&>5?43`>?BQ|XQsPAoedvVS$9vj^9_g%w$IFB~$% zcm7v4jQ4}xYuH#k1wG^^-|3C3*Xk0`rJ zA`>_8v`S)3a$bJ44+YIY{yb1#Sdf{TnO~-E-1o^RpKm^RV1Fi>hP-sClo%PFkW(X; z>J({#2iJeN=Dm-%=AbFaUr`3mKOnxC&!ZMoC?&a>F}prqyK=>j>J((D9Dl|yI!{2S zC_!^BMMV`ENB{lKiZ{K6laa+g6`%DEN#-&{DahjaTJnK)E0=wmnv5)JOg!xyURteA zM-~aw)~{N=?9>|sc2jM%;ZaDE4*8CZmv$kJ{|V;%i!;7wWJ^~)yfURima5(qB15u!q5Bgo;^psv#P|Y$X_O^C031~vXoXW*A?LW z4<9)dl1CS$BY)yDYRxVBMgdnYi#d7HCzwd3H)J7y;Lt@Dhr{l&lS1RuD5Z354)XhE zS_wm@u)fo!Q8Xs6u6Lwc8yXT^fl@E@u@>GePF04SX(NX0&(rtC$pWMt)A3 zfSZQ=1r;z#i#b|XS6hqKpi|c7#YKeU`KfR;QcyDTI0l!t0#8|5R$3_+72(dFAs9g9 ztTa+)rP5qOm5hSOO>?Q7hTQPeXYwy%BF9TlE#m8{_#kov)vW47|DzFX5IGZ_m{~5b z6@$oeYqLX6oyfWj(v>96NzSemc|?vLcIH@|5)(N#iIm9}b777h)lp|o1hA4o9Nble z2@;&m#GM*plvRPW31N~Y7i^)}6TDXtoO*lL~-rwo&>rx5j23wn{x)zf+rf&G+ z#S0h5+dDctOe~tvBX88YX7>QJAgZLWJ@QsdrBG=Udi7KCHi}BQn3B78`_An<=gqSE z8dVJyeDGjqb{?IU z(F)R2va=JbFmbzd3!MS;4V}F<;#A2*jI79zkO)>P^4I;h(AkG`=xisqyqr>8P%f&= zjrKkf)d=EtV|IS-;rvxTh3^qJHnoP89(w#(DyHEs+(GANA70~Cdc=)EiSs( z)NuH{ci#<0p`1m{7d9F&X=~X2*Oy-U&I)NJDZ5^(^GMstzrXy_M`e(v5!5y4beObV zA+G$}%m45Nb-Qp(T3au*>3~I)y7?b}`}0RRn7HZ19I?TQiCbgDo3Fq6(zmtHMYD(_ z)Hvmsw4nvt-}vXNuYQ~9k+vp5oj~KTW7@4T;j=gX_1a%QIFcd+!>XrQD&u-O$=_FgJ&?;m~Ye%05lQy(6YQw4(%T|59Gohu^)~q$P^>+(7pxsbr z{K1b_uUzrYF@2le*)ufaVzEHlL^%-$zWiX#>GtulvEeoz9h0`3tPEVJSDMppkyV#5 zxp}~(vNErzw6vTdF7qh2Ck~VUyM1}_^Dq7pvvU5|=bcz>*YMQ~QP%x{0G;<<)j5Gt02p25;yyM}#~Jk=dYT;@sS{{}vOk7c7_Skb2* z4wl&W9HazbpKd?4T7NF#kELGw8@z{OpT>UwbIzj=JF(O|E8!&q`!xE(f93DSQa!e@ z5BBNy&(`bXa_6wr>#xDvde7*c{WblgP7h-Iy?O;JYX7y^to{*8tjDJ4H?`Qu`Ov2; zSir~9(EXmY?Vitfj{j2NHzZHe;3K9N8$54!6aM(P#A~!i6Z&M#K7TA*Jy9RO@VnY4oZsciVT zr|0SHIUzRkzj|H_u|MJ(MSl`(`n5DXD!Am!$VyRDE!FF-pC$WOHF#byiTS^Io;XY~ z8uY1u9ONba)KGVq+uqvR($cIq8r3?Jdl)=V=aJXu2m9T&X1!XaQtR{vRim)Bf?ic! zCpB7oFQG%o^69~Dn_g>BD`aAkRHo1xEyl(=dVV?~E0ZWHs8>)oRrW zg<_FhXKwB69T*rM8yy|)ZFia#b>(CtE;=?Xk11_(T}R!>AJDNjqe8$FNt-(cC(d7( zzKFm4%GGN(Zr->uJvlMl-{ZE*Dd{o(fzeqtO50Tj^1gn!->eqa)d}PlcmD`fJTfvk zI5avjIep>$aBrvE>2SH*UA87+ew_ble_SC)VX+~9nC`W!xU6cv+UoA^>2SBX@$GMP zc6RmlkBklXbvj`zv}%=FZ?!Zj8j2Hq4jv0DtZ%cUCQ#7BW-*7!;Hu4br_1TEw>sVJ zc=sD^?OnZtqXS)bqedZ@D^x0lL@ZS(WdbU}d+$M?gqo%?BaB5?i;6=nV>RlG%_bB4 zw^*!phtt{WaCLO`j}3OW=oAu>NFot85b9sxa{1iq;<)2Gb_eJ2^aDv+WIa0A%%>Gs z@MKCA@Tbuknhb_!lf`Vdw6wZA2PQ^(>;|Qzp@Cmt%dW0wRxzuqDl4kWGsE|7Jsd+- zc59FiFSSbOdHD>XR4fvSWwJ&E)TGhtwMvCjsWG-VT{eS|S(cxhmtRm^R!*f-%E~Au zMfl<+`Ef_S*>E(8XT79AHlaNhQF%@Qjl<{G@){aMVu=(g6biU(CZnRVnk!W*C9K>y z{}V@!o;d9j5>H4c=NIOabFwotva@gpzWicmT#aE&j{LQ|iJhNbP+n10U0uWC)Nwd0 zW(BpRFc+WmdomfsNF-MXX+*z$8@?cXwsigGZF`TNJ%=ME6Y%(@grtx|UwyL0FIUzp zMcy5?ikaEzc~p8Cl}@j$tSB!jAZMhdre|bi=H%uVmQ-@(2CJ?vE9B6YuRj0mKmYj@ z|M5$oe6jKSAH0IX!y_WYLIMwe^XbMDB))xAgnX#C%P2%rZfRL*3HFnpnT*FL;Yn$k zIpo~@g2Ga2b%WB}VG|cc9shpgSL?s}`iuX3^3lKXAN=W~&o}P);q+NQ|3KeE8$SAC ze{y{*+5nc4OIVnQOUW(77k!hPk%Etpjlm`0N$J_-{Ngf78MT7RZ8Ws@I2%i2j&Ixe z)mL9{`09(#{`2uiAHM(Idu!Hx^u>1vPWbqG9r)t?Pqs(094H^z)!8g6O^QoONzWwb zW+cT%hDYKO@uZCGynptGF>)??iJ3f2&hhA1tnOO zHEd2@y-?8%7LZHJ%s6*)-;OPtHf{LktMyp5pM3P;2kX|pv-<7#zu3HU_qI>ge6Tr$ zZl140_Mt9uR$MG0nM6p43=RwmiHwOSK+kgvVWt?>Y%ae+BG;ImJ%dBN_C_W-F6hkR zJ=?c#+4%L>!0#8Iee&=3-hXe+syA1z{bbXwEg!CWe@iIShH{VtZFXr{RCp|iLQI(d z*}%{UTmm5_Ju9z>TFI=b3S+!>?V7h&zq#Vg_dfr0 z?b{!2jjVN|)!=OIQqkkX!XqQY0(^bX`Ugir-KoHI8MTtrAlJ9Fxzw~2d_qi!ueXo4 zm)99D-+<7Fa9jb0RYC|p{Qah{zg+(XjP?8Py$gf==JJ*6K6rQanvZv;h=-WS5p>vC zLpm24el8>c821khi%BG;X2D3)E7<~t#nD7hj`KTmIyfP_q`ZQ`WYDPvX|euC4|zo- z7cq+By!LI|u>K2=C(b-0`^m$S85E3K_8Nec1`CFWI^;C%LN`39!+(~sYO z_uVzCR<2mKV&%#eE7q>}Dltw}BF86eg6x=JzW`rwjsye+hlWQdB&X*Tl~-|PI*YL? z$u}U4DO1_nyZR=sT$vb}p1v`BadNQBWs%jEB?g=&7FOg&pWM0Ot1m&u-+On>>bIbU zWh<61TlwC%;Vg%qj;unv)aAH9|FgcQy?mgF(C{c&LK(TGRrRWt=9-M4kW7}^(KB}Y zCYpu(A;sZlcX@#*zH-Gi{ zhZw7v7`_QLE?n`>7sn|^v>aK6+C+shem-8_UT3_|1_Xz}T1-wSmsIgoW>#Dfp+?~v z1}DZV=y0z?UX>6)pcX|O`fmLvAAw-3edjHZJHm>EE8pE5Ro8)1k$(+p7|9_ZPw@XN zFbE?{OwBH4G-x$+Togs#+BI2@yfHD>Z4nlQg%{*S9^3N8NAG_Cb**0c=9?>4zPW7K zs*m<%Yj0AJ1!GojR+Jy!>t!D=A3tC$GBznSv#^4%Gn7Y@sttWNP&)Dkdg(&9w(MM3 zdVc)zt)D?3-+R}?)XF#C_}7YeHU!dI&=TZdDB9LYjSKPxwtT$L`UkZ}sU*WBk0myzyRt^bQCL35$$LOvz(N)iivGqU(As^7rX(Nk&9=mf!Zz zKYo85#>Vm$n1sEt^26_AlSZ>|6)mM?pA)qC60mHmatYok_vZlup?sKD3z zj1P$7xv0d9G9Ev(K-h)~kk@XuaT4*Ffm=U&f9*SO!{mC%SFBv~RT$fb79fA?(J)iP zyicCO=)HV_J|ZSLx2B;uv)(q<)mulo2{zGf7}8|`i= zOb9xC;?x;0ADGgB;B!&&85ChndPVc32(tThB{}4X?f-f2oi%TRkS@oVU%q19_c3*C z`N-1iuKM&SzthmV7vASNSfJ;k@#GpoZi#kWg8XfwOIDCeI`Y*A@2q_XI*!r8|F_qB zm0H2s^6FS~Wm2dQMtsJ{7gl$0I4-4(S4EcgqEh7fpPXEBdhmB2z4Hz>?1bfuK{LGd z@7-zYfn4NOv|U*k8|Z!dl!q2nesE-529ujx(=ySBJU7%-nu`13GjNTALLn|+1eEJG zNAtRYaI#gJ9s$#J3I^QA7Z?tSOvq)|6v##|0pZ0CerB@oH|yS6{nlG=;+H?OVmW@< zg4JJom)lV?^6Fr79Wmsr*O@b?F=Yf>K}aN_lv&EP{fNquXUFxl%!pkdf)c|>m%=oI zoPBz@uxUC67JY}NA}-MP5v}jpvwp!L(Ijelp?pXQV63CQAo0j&>sGH^36uz?RhBLP zU~jsjKL=TS$ssF^@`IfPf982Fp!5$3#idiJ4AWI5@*K<#BlXPs_uhUJ6 zZHsMa2cOF;-Ih9X_*qODfpq(x_47L$m{3TskawXfUQWo?e}gmu9gGlYvGu+z z6VScf(^Q=i26RB^1q0;g>m7)vGMJWeHS(OJT~3MF^YJ4x{PHEsKonMeapg-+*fQid7#S%2i&-LKdRkigJ8_ zH_7W+Z=muHNugCKMsI1rH)3a{9RK1S&>X3twqZ1+U0E<1T{22yfRDEi(fb)6U;NpH z;b|16>HKvqvZSja>&(|{-&*k|=+Pyh?U%27Zx>$d&O%x0bjDzjoofpX`n2xxi2Ldq<0emWqpv2oH~pfM58?g}8KDmDmbI zFJEb4;E!$k>{B?)ee&_gpZo`ov)>#ca2-ImWWcIm<);yea4aGa@Cyl~{F-`+Lyx?G zI%{(R_ix*}<=f5QZr;4*JN&i47MBo;7ni_6nI%!_`whrn zU1COBxWBIt3@pq!==Gp;B&xuS5sh2*GInJ-4J?b~vW1j#mPFHxk+jRIvtz@8gWz9q zNGNE%C|ou}WEuyOQHxH-qL)!9r6r`|1tnDtYV(KzJl9f2MpQ^3KH&Mlpx}^@(1^&` zEQZKF2;J${$)#*+abZConM}x8L?-7_d1}iAj5856-v7+WQzuWHJOyhO)?H{aP2?QG zh%_=0vk-;=tOi2LqSVxs{F+A7Fc7s03PO(^+<)M}{{06J!WbMmem1d8&^io6eHw|N zA~z!~1y0P#i3^f4=t5(^9(fsUsn7D+yM5dD+rR&Q`;MKv_U=1)5?9Q(4`d?G_klOO zf}D{`!Y3xg#o=)a;}Sqm={h}RwaF*8Z`tgbp)KEizhmd#4T#tj=bZQi_f+xNSV#TN@&r=huVvs}pZ z(8c3oqX>};qY}t)-0#vMi~Eg~b9*;_z2O_ehG#c!{&wqkyN)GML{1Q+7dmVTK}}&+ z8i|k?7aJ8BiH}+kpIP4Mn1t4PRQdk9HhulgH_+I|P2X<$ZtHF@5?zXk?ei0M3BQ(- zo0XcJ6pxE0MlOg=DdL&>K+?N}X=k3&MxO7G@Py2gJdDeP zxLADjqNu2ZG^)H~LW8_KtS<@J{Vj~h#!Z{P{T3tJdn~4uWgG#zE*Y<}mRd;8N`=A1 z;bVXfM=We`VRXH6@~Q8zVT7Imm)myiI^dm5sk2OHBd>OAg)+dTz_26{6M@l~=&0zV zY_@6a3aSL9Y_G*1+6cn}O@6y|E3~-#NKh7q-+l>5r&=UJQB8SqZdN)rGBAA+5x5kp zy8kAS{_cW`8oU$#^^4zZ+=TTJDEFO;E~0A&Fx888$^9FUSL;v+6yj=XXfurXN%h|>Y*xqE6h-GzjY7sdzg#i5Gq9Y?>2!$fs z1lkA{Tlq=HwnH~Jg495(!0w?_ape@24cpglI)t?{A(LKKknNe6IG~M+OQosWuK?|! zBKy?NZ((>hfaGl3zGL^k14sPG)#Y_Ihevw_vvSj#9c$;$>aIUybl+lc7ITz1n) zmkfEuR28~^EA({329WXXJ9q9m@WaV)Y6Vp>oQM2vNX>0jH&j|;}k zWo=`q1SYS6bY%PHOfOZU@ry4Yg23Np@ydW-2}|CNdhAkU?#*4b}mZ-MoYo z+p%ef^|9sq9lQ7caQN`iQ-K+5W|evZErK)rsJ4>Xte5iZI8~L!1-aR2*rJY!#U-T` z^NjG}*v`bl)a(d5Jkay4W+_r1q!5@wuI~kBbtuNy^r@*9eySODHvs&69sAE@9 zigGhxK;s~iBsPwe$FUDwy@FOD&!cW-MfCB#+dvkeKlp9G-@fnQ;bX^7`9v4g(`nkF zGUQ)l270;9tPl$uYHKPfMdWnP#NcAVd688qbC3V%s6_rb=@4WE9NMvU6RabUW(e@v z4dZhBq;Gg8tAh;svo2x%YL#}lEqd%xC!}ntjcm&pQ*_p z>e11$@%U6St)aPp>Q))D^g@T8nR)Ky9&mNwH~(d$XU_K>IDGW@iPQe^Wqd|{y<@x_ zd8uDrmanitZ4gYsVU`!?XJh+I93kdeTtaF-U1IFNeB-tw4|x-v9CE3cnSO`&?AW>m z&JbI^-HP$I|G*DNkDl-egguI#Q)Rh8L*AKpOEYLK7Oet0%&V;`FD=N)NCRyd7YjR9 zJTX0=sj~DA4_&%>9qmF>kw13b80~SIHSFB*DY-v&nxh#5VW@5PS;k`Rx zEt46d0B2!@X7rKf2^Vl;-;SNI^&LI@!|~JJ z;Q0y)k4vwRSH-1@I?uC_1L&BUla?ZGvzv^DW`kC%kP7)8ZC40xZb~JSRa?&&NfeqU zo5Nwzia3?j(xSYql%#OqqX%|@m>>KBhWeEES-*hLI8sq|=(1>{0oR*dpf%}}ns&9}gbj`N5u1-g@Qpl+&Ey#pgPyBEIfAFP4M~ii_+!GOC1tcKR=rqh zusC~r-OW;VSyob1h|e)No<5#7UocM228KoAGif4TT12+2eTt9#ZPZ$ylSnbPx0=kR zW&jR!D!CNaJ?65f6KHE0HC(aEU@>cWl(cC7Q-=@za0qnk36E{+7Z3neO9YOPUoBx1 zql*;nR~nEdgRN3>Otz%WZ6h>)3B3ofNFkSs_}sc$R&^DFQN`woWg>PFKJ?7dg9m;% zcKqn^Q)kZl!x3J6kHtB+=~jq`W$almc|iS<~E1TLNGsXYJxg-8ifq- z5FroGUBwg2kEd<-!_7b%5oi2jL)BNEecskI`7jF;vgMktk727fX_4BjQD zs=gpPD457K*4-yI#R()zFKw!4gbNOo&gy zC#Pl?(bxj9*4)-JJUs*_Aaty+!{x9THFB{?rdFy&0i0CBLwtm)RzAgf{P-6;iBmD$iz@*TU*aScV~Z3M^FFs{d?#< zhT;BrWxT)LZUH4?v2{3YCT*ikp*A}DN6+8%V7QkcyRF070!zb#l#KSfniUNk28~|R zAe0%}!LadQxHl$;y6rl>(O`CV4_~-){l-ss@7=q7^XldEy*7=ohE`g}=BXV6=TX3L z*KjWkb=nOYz0uY+GBthi^0jN%Z~XYvjhi>GU%h|?>;gR8y@u>?_<3lcKbx~FxO=KRtYy$y= z3lgeEv9`5u?EK{U^Ai*2$MIwT9G{%Ncxifad}Oe{x2LDKx36blaG=k`FHT7-Q=2*l zLHa&LuMRr3a;c`Zr*Cj*aG)Pb3=Rwo4v&mYOihlT9~&MX=x%Rsa{z|X*3sSJ)Yj$^ z(@Pp!2d9nTU7PB1XhaPfM@MHDzGGcyS5I$GcQ4d|k)NNOm>eA(>~XuCc7S&6Ef$l> zX18j&x%iY)fpO?2=JJ_p(~Ih*%?_7~;CL0px4Rc39~>SXpS&MRLjTK4ZM7q*_8GKo{M_VMEdZoG5hBto*ix!qoXHVZiKi1ox-hrW!p~0>; z8>aGXfKyqUVOvr(HY(L>IWsLHI;Yk#j$#n*<*sHiho@?4ZqhdafNTeb+MJjo0BJB7 znk+WAt3@m2RWqtsu!Lm_4R9ith=dId!UhgGE+(0&>$)vR7LT?`Sq#2XufuEpqi+IS zyUD26%0X#M8s$o*O4roV?rzmfxeRJ?QDIR5gecLg0iJ8%@wgn0psqAAJci6~osc4n zZ;ZA!(rIj|k|1BF*6K8J*rx02`3>Sm6~NpOGU)2;>FR*!PhL%BIe^63nW?0d)T}~U zHHVF_Sz29P7#nadjpe+8iXfzBz^P!+S-b|3L<;&|%;T^Cz~l*KN}bUJ`#fllp1$6m z-ZnU!H`KG>WKmw44<3PpxWweFA{wIt0{60G!E?oTjsS%F9=FTbz^tmP=hoKpc=c>- zgRADimZZ{|TCgAk2n6iK#A&e0VOC2T>e(!O^|KX}!rZjv_^8M@VirDsX>n0@bVz7& zjiG;(k1QVS*40tzEN(5HwUI#sPa2I`%NI3j^vxE#)9vm63G40~8W|lK>2XdJTN3&KV1G(U^u;YJDYGQ13L`Z;dKzKr|zwfyOx~2yp+=J+Z zrLH)yu(S-!c>vi!#q5vs?)z;S5 z)i*fU!pTcdO$_t%@%086$2TZEAvL`~U~PvDN+T84)>MN*nUhM0iwq0$Ipqb14{yJ) zB#vzwLvSy)$tz%l^TBF@=zyZKit2ia0!A7lQ{7#IBW>&~QrPLU5d?B6K=RdaATG%w zL`J5(rEj;5FL6-KQmB^Z#LuO7z>2_+!fQ~y=FyKVo)TtsYM|Ky-}E(PKXH$##jX-;j~|5wxVYO18{qc zb;Vg}DWqhGLdnW40+?JNRcV`T9X&$AITBlG>jwaLIeKx}rC`K`6|0-HVm7_3C^IEK zGBm*V3>Eu)*e0o17L(T(;2m2*K@YVx<#njLKSiK^6(tR7jxT>0@BNp79F9Gg|?{ zeHOho-L9@G0LLmZ8K3cDHZWCF#}`4swYd_PE9<}z+)FL>X$dv@MmB|MMniOCt6S>%GUDh{ZBRZD9%p+w#_3<&OMt1y?qQ3)!F zvWW395upLzFv`bHo$?OGGt3?Yw?kh8w@8u+L{fSdOj>yjmoJbQT6w93@;*Rt|8&76 z%uKFVbINm56XT;`p1e<;z#m_D$~U@L*5^TRM=U~0b_Q^foRX0Zq6Au9AkGxq<099Ym~ZxriaSojP_wM!Kg0h&L(#bB`mCo3wd_j}Y7m zXKe;u!7s`n#KRKz^YuD~k)J#plc(zUAh4ITEk^28Q7F8R|@yi)Skpd+AEY)-|j(hyPE_h>B%0M#$uxKi$J%@6(wv8!F{1$ zTTZVnPlb3Me`xjONy3Q*rvl?E%oi{Sw@uH=PbCsPt04_2L4>(#VTp7IAlz3kwh7AF zm6`G35H#X_`Xq)N!B9u%O1m&u_n4!Rk()wHBehZ(LO(GDWT~caVMs{9)X-%W3Lf(!+366$JW+@>&@T@QB zDXdX&+yy1pSVn>JQn!{lM8&K3-ujc zfZ>j|N*MqtCIhAjGM%2DSy)V|tK--v)zDtMyo#C=;}2Lgj4S@c;*%$jdq?Cowqh9W zM2n~bq&(#Xm=|C)Gb_J@#^Z^4t^v+6Zm6Lb#05ZOC!u%8VN_50gk?2YN3sBQ95e~( zIdHZk;gg?BNlDMhBo|ij__}dGa-T&lb(MvQ0M?&A2@7utM(7=qR@dyobNfufifmBg zMAEY!MPA6@HP}6Pu1j21fDeYih?7KM@)XdJn1)V(W-oL$31~SP8R^8-r5KOd#nrq9 z7Y61IXlhDQ!u-5_&frfiJ&j@5e&^B{Mri2e0f$~#iN!N!VTT9st&wxf>UdHIcE0a5 z)zY$}g98Hm2);}G{Qdm`qtY2_H=tun9ROu=%1erii;4+FONxt2ieax+V^oW;Sh&n0 zd{jg@;oP%f=OQA{MJDA|tJ+30;lhvEpcAvJsu=i6gh8lW$f)8ek=hRU?Bc#AlhdYEC>6=_#R`Q|YX<8P@Y#hnxk}2Umf=gEFQZT?<>gd58x9UF z7^K^0w6wd-nET5>)GgBM!HH&X0h;F>YOS18NvFZTii%1+V_|i@T&HURO!v87gSp*l zZ8kL+hzX*Fa7)o_>%k-fbvITPl3-%-NzW08DH&PWc@#d} znF3IE$+*ShYK7oyg$yr#R^+kCB-&s+ZHjzjcQLc!h{37jaI!z z1(u{tD#nWz3WN%yqkBw;EF3b`7p39j@d?j^cO15i+`KZDtWnbj%Y^GvH#u9IfKDL? z&!`YAM>uD+^neFy5nwsYyyS#94;?g@lUGQsl}n6p*Y4#urLN5hHiSwEd_E$RsZE^| z7(hGal29`NP)JNjNW|hu!8c47sHDaopmVDAVCCa zVOe=4Pb<^61L-Ret0H!{Tbd0zz%!&`37`Q2k=*F)n+9ChrG!lh9~YO1$0x&(LX>cM zwNxe6^g?iRb`_@DiJjZg;I^BYXa>0S0GV0af698*o1S6z(^S_Gsu0)t5K2-kH< zRGrk2JsSx>khVvDVW$ocR`ABF^h>MP&EnI_AU(9bu=j5z3r`btJ#P- zyum9W7K$WFbN7Y27_QqYqU8{9F>w&|40}JEm6&X;PbwG}*Ffa_lClvj{Rak01r*i19C^GfMeZ0NI%S7QKN_r>XER%1su_>pWDlTM|U zKU&^$eQW;|7Kn!q7_0MB5`Y>H2bLTNqpjxD)rk#;YQPPF9Ch)kEnvX3wc0F3tyYPx zx+O5;3Y~KpF1rA%`%}M(lMg5l9Ln6ox@4mT8?Mm!Sr?rnWGHkc3MhWn^Y%Ht?*02!TG4!D<8jq_dyD1mAd41;JlaUFO<65&{3`WY9y|aB!|<*Ym|P zrNvQKEcSr9O;zm9@&2CPZkN+$2E7EzS_UEkKaI`p(?88p0Ms2Eun6h-q=Wd0W$4AeW-%QMHv*uLX{j*f`5-iP0x{g-Fj!l7#SQM8<;a`;8XB#2d~^W3 zft`R?fDaM08U}et;F5+?XYM_J`R2U`6fk8chXy-aR1667!Nc8w^bD|ka|=91J4EDb zbQY&9ui6Tr?kn@|#=N?b^TUJSRqX;74Ca>v6(E;4;w4MOQn|LZqkHPg&13*{SHVTH zAIE!a%7!Y4W`M~cra?FWX!jy2gUypDlyIHM?q4D9N{()Ys?=7%O zH7cUw&#*m-8#M-VTi@7)i zqf>+OZ~$Y`+v<058RURD+amXWgZ;YvvbjZz3^5UmnaT7yib)$4$Hm%AaAqI(49 z_G&Wn&68vOVg(8* zWyOVH+kw7lz&w^lBhWb7MOmdf49tBNoxj|{PA{=cO^goXN4CP~^mMu%puvH8g9Uud zEjC+=4UjaO1Ddr$@JtPu`$E9^>UnG?4bGT!MpZ55_|-LGaHYM|&d(@N4`5&}cg$Ry zk*yye8yOuQ8yOtx?*Z0f;+hQxW0R!?oO|6}-MyGU9g4u71DiAK8!~Yn6QT|)0PE%Q zrEs~c*=#a4H(5Ge?4&G3H+)kN^4AF~Co@wyHa$KvG6wg^`ulpifor>kVEV}1;_T?| z=@|egKL8D*gWc{{0RJ?SdR7G$D#e(GHPd7^S%6)$tYm7hx)p^U@oye$!zsNc7qs%F+Mif=`d@-w^;*c{mN=~T|KznmD;8ji`8be+g!co zl0>ox%iS?*<&lX@_l3!^^BCv=9mTlqYInQfNDLl)OKW@2(C|QCuiLBv9GzKJRm0`+ z8-yYmcx{`^EjFvI#cXfyR_DY%0&~9~v`NWvMTUt>Q^3n4z$xP+!+@Ohg2TN7!zyid zXFCjr%>ZA$P*+#W!* z#Cho7D7bNf&0f$U?vBpR-d+$2D_~Nfo8T}29)HZu2|$F?)oO9LdissD*rXc60KmCR z0nV+$$5Qk|7bXc4i^l=)fq?|q@W2{y3-=8Sf-LkyXEC${_DVH)(1BsNpyvTaJNnxt zsnOXD=AH=|=!AZ&Fh3@V?KnR*J`R1wz$U1UF#M;X;o;HI@rkj)0l1V6Kn9$!On3(Z zPV*NqXr#TXr$bW`g)5ag0nGgiItgyAln8>%HF9BUVjSw3n7lBBKffBr4j6>@(UHD3 zC%94D-A*S!r0rcD?$%bDyUVU-1fR>UZR!Fv_s`ddZ3fYL5q1`KT!woO2(x-d+ut)sKAugj*$@(U%Dh&)j4ivZ=na*agqc!cE+cEUF@fOj`3GBh|O3JixhQfg9sQX;s6f+OOR$<%s< z!S*XGSHNLcmO@xB!~w>8E|uj|7J5uI|3ku}4_$ z9|6l9>hJ3A?&|97c!cFn{fPV)Mc})*ou3o=&clN=6yR}&*62SSX+1)vKX$r35qW5O!W**$V{p0m3SFT;VdFSrrK$}ro zOE1YQpj6jzrKXPIYXIZo(M5Of+`4w_-rWbpIl}BqGY{_ExO9HNsS~j&`Pn&CMuVYk z1VeD2-T%{#Yd5anxPSll-G>D9?+<4m+_`mU7JmPD?drwJiIMIWxcf#*B{Lw_3NGQ| z(HG|)T*FY|2Q%~N{KJRn{Db?1drR)#hWbZga~Z);JU2erDxhX0k@7hz#~56}Mep6b zdg!r?Ug!{)cW@;l1t3s`MCKcD=fz6TsWQ zqBHYz_n{Ya==}V{S&SZ^ckjXUm0C@X6 ziq8FX>&}l49s=#dS^WLq&CbI2X5b$_KlgBMcIN(%*MQvFV#7QB+-hrq;0&|9rCCvx z5*d@j(YP-q>5vz1-vreArw3Tu5ANT)KQnWOaOlDp?&fvaN=)+lDmrX;a4LdBGJvUICi3}$`pV=Wzi6t$by z?Vs*HfSP9SK#dqX59jXRxpnK#y}S1w%;D!(L&_Y?Yg?177Err_+{_db5sO00r!oKy zqg5~%aA_$BPty+rbo&Z=_44%}=YZtyPxoPJ;m@sGx9`q8y#D|{yAEoHIiH`qerc+= z)ga-r;1*+XKAhL^3DHrpNon~drBqr)IXTMvY&2cp4bbhY`|sX=@YAik_wUcl!Enz4 zXFuJ(`(So{e&#-5W*u~R1~P75neK=B>ez5^8jem7shtMbFySTwE;+ZTlviK!%!}nl5K&<9wZ(g151Kb;)S5}wfGm+4Alnzw zt5;`k-GV`!oxKZ^GB-0voL_(m?fn_TEHXowTRSsz@5gJG#(Es!Fj2^)5CK|K&7f0? zA@Gd==t_W(kAGNPN}~UXV?o){|HIx_KvlJM{~nMA^?*os2m%tKk|HW77>FnmA|0aA z-G`8tMi43K?(XjH?i58SX?W}4z2A4ygIB%x|K50G{I_Fx_TKC@=Zd-Jir<`bbpp5+ zeSdR$7Gw=HDBAYw$_D7VAe_q%_z$uT+k$Oh1a-T$zP9{%ac*jI1Q@%3umTmuxjCr` zG0_pgq}%(Ivz@tt&U1AY<)?ZUrW%rRFT8U*09t#z6twfs*Io%41Gb5|0gAu14k}?6 zw#Nhd+1kqI*?!>R9Teyd785S7g5nFxih;v=PC76)iwX|`6JIA=00e7mswgSFFfmY7 z)Gz{5(J0X6w^!jk1m0DEWWu(|Ha9`dg6KB8J3E^jo1hbq=7;*daxl}?)_$rEQ)7Ip zZEOv5i7EqAvAnc|n0Fz;fG03TH`CX7s-mEzsr6JzN!=*Cav}nB=1rg`fgtUIuDb!^ z=>Wn&z-;%f?f@y<`Z8M_?q~U2%h=uv*ce7d0sCwpXG=XbWnFu(xT?nT{L}`8+y=QPFNm*S9U<6jN4FIgg`LYH&70?vhK)ZlCM+IWPy}4@#sBf(eC;FN_HFXO~ zPRs!NppC7-;-tR3C^J4h&_P#G*Cn#Lt|&Vhfck-6ubgZwO!c3sE6Xb=DLs*ue`cT5 z4xDX|f8JPL+1TFP+uc~(fLAjbykCI=z_#&rH{X}~=SKHF_ejGU4!m--sinJiubA6xm8sM${F3<<~9>B~EwV$d2i0O&ku?#Q*xA_wJ?%i*>;MKko9oM6aaP985e5IjY6sdYGD9t2n7%Em%!mmN z@Nsi-um+~6Dgbi;6lE2j*`>A}V6}4#DBqw>)*7*7B-RnvDhaIAKJeR%~a zV?YFm?7=S(t@WMF)rCSw$FRb7I95vxylp!Rg3K)=iqb;CYW}6Qg(1il0FC5fPf!)K zT#`Evu-ej%we?jH4+=imfwqR%*)~x9dt2ja?tUr23KQ;a+f@-^XBnE8{MH*7@q>1K zstku%6cprSf%>ZeSnaX>!Lc>i781Ng@qv88se#D>XyN_&Waq$wE`ZfS=NnR7Ey8jV z-gvvfjVPWe!xS);lojP=9s}tHSna9Vk6@(O*aXdYP(h$CZU9AszV$xaIk>17c-ulp zThl#k-epAixdF7-NLvk{S(rc&WM!2M0?GhZi}87JZ4(Su@R17&O0~YS1|NBrvYfs; z+mgefTD$0!Ko2*2pvVE@51I+I`V$3h_q1*R)#B`bod2*4H1*d3bO#5FzdO78o9({7 zUwv(7n=)KprN+N@eQ9lK0LLp2q~@`#vT0D|I2@@3vobK5I8ZO}o)5YwkeRK?_>jy4 zU)zc1SO>q9NROA+W`-}Gswyi1F_8r($~w;J?O=-!Wq9tx!a8i{)PZ{12I33^aC_%t z{@bjU17F+0+6Y)!yuX8$xe>5J0oj*>CzsPSk8J$p1A&grFMo#Xv2CD$4&(!_m%lW< ziLU$VYFp^<6Bz=J4FR_R0EwSGmX(oJ*6}VJ@`m6p4u4u(g^!~kHJFNk+yf1>GZPbB z1YB)#C)!hAy@~X;GY6doj!!>%0(*=mtDs?**bb0dl(A9JcCbCngT(N0AFiHOYJ5|g z08)EmrXt)U_{~cTBVD*n#1oht#$(`lpkf+PcYxH6Pi}03VG}OQKykvy&7IAisknD# z-N0qGqrl%c_?4xB4nPzY?c~tmM((=)MH2w1U7C2meo#kS@Sz(FXkdP_vEK16wWBKs zY<4!qdk6c$Ktf>i0{S9gi}v`*bH`*jPBT3e?zRxu;e!%`0%A_Wcat3({_V`2tT1ud40kj)X!0)I#@b5`y2p z0>TAQcz8-V(BEWLj6x~_P>VA^Ilb@&u2nYHR<}WIf&RO<{h>CoX5s*-EslKaX#uo{ z3ecM{IZS|N%gJkd0rMqy;Bq`YzX~J?R4h<%@QPjsih3+Js~I4**mDi3K_2G1KpqvI z*ugm}X}RPz1Edyxa&lo440PKscQLkCz>wOXUffX#*5d5|Y5@a}l7a$E4(ah@P&H4K zz&KDf0n!YO%x&y!tggd0Y{9s@4(1BmUAgsxMZj0HGS(kv_!JljJaLdiQ&3Q|iYx>0 zEJjoJ@WLjjdobifz(N88-0dtimv#e`1^C$}2HF`v1*6zYz(iifEVuyRrRZIqBa5K5 zcOCaIfcn{*tEuS+PPT;YnL+lZ@Q*>20~HU~d&~ulA0^r+^tpZ{MXlOh$_c`!bL~H7q{<6Kk1p9nqd1>kM=cT3P^^MOxwJiWj z!(ONZ#`UHk5`~VouB{FR2*hD*|0WBLq~T2Vj?I7gI5$5x3;x;7qE1dvwpVq)!PUl; zh(H&B+S%H}Fl?P(I(og!Z3ciW@X6^O9s>JH!$ZUNLuiAYz5UJA0F4E)VX_iq0zEyv zVLrCr7(Qnwv{cNJuVnRIw$R4lN3 zjK+vbh>K0hE^LIuvBTy0*)btOZ-WBg*ax5m2FIl3m4GA|UB&rD>A;#Y%{Cn?EiE1F z;^!8Z)OWza*y5CTt`^27z&p?Yz|Uv~R<1$GMF7FY>MYJH&PoObm>HR{EX>SIu*IEU zQrZOISd8wHC^KnUDM?A#1MK_v#qZs_Da-+-d7a-LXeg5%oz~!ftngdg+_grOVTQKdt!7_GJ1a5*Kj-*I^9v44*YTcNCd*xz%o&QZCe{0j~yrpdMdyS z@<$0Hhf!cqQV>&|7xjV55K(_?QA%1|bWAkh1W@>tG+>JgHe%a>5D^Vmhd#T;L`_9b zLQG6ZL_$JFN=(Ld4OVyn$F>xuq{PR>#()eSlniY3mX(&n?Fddz)CKE^uuxNy5)l%d zK21mnBSIyi;pJPfL%^;d2Lx%8Q_OGS>SIV#Br96n!4symjp^ZLch1wngFuo{ zP!pfRC!r*zWV-8^2lugE>Z?o#+~UAiE+}MbS{ATlE^R2!f@85KI}740qccT@F0${7`)+Sj|O)g(ujj&CJOwDz0p-ug}f;ip91iI2uWF(9*!W z1{pC44be$lLNZED#eix!7CYLOpOzR4su&ncr-1q`Dk-UMZY)kMhhwpmc>#9n{EReo zG?e7zB&5_tP$&TjDg8AIV49W*fsQwqrUA!9AnkDpDZu8S5LSv-)6$fc(0t%y+Z5(# z@_-Y>hyX1_N=8lsf_IWo(lU!!CUwKH*v^u)6c9ozItqwxat0jMC<8lg<%!wtKH!Lk zL|0q&E6iu<>8QwTNwGfs-rk1F+Mgr8pK0` zrQ>AgmQ;c5{f5qr@FIZ4lK16@yBpnP1@X7(s6l@R@(D_Rijaa)*f_SL1CGTGHx{KP zCV_>0WNa$jU>&q~Z9OnmZizi`v89;r&U4qj&wdsjzJ#8E98^0A9`0!hYVtFJnjuyD z@ep!Q`OyG}NCKwmG3g+jTR}l-bpzNt?#_+L`wGTRRrs6eJmlwOVWg&^qaX!MNN}2n z3^XOfbsg{I&d+cc+pXE^+@zSexWoi_D*|nmS6W>UcGH_W8l&PGfr~9(TYC)5((pb% zJ2N#UC=3-PInb`yWT4kE2&%o#ZmI%cEam?4NJn;RdO}=GY(hLdL>vG{t6MtS>S~)> zQ{TZ|Y)_39d74;11wl3#Xy`#G7a)$L#4r*pA|f(+E(wEx+yP)xO!aZ5w<0|{7LbVn zcK2Z46F4%I)Hb)aG&FY<2c>iXU=(}5E#A_^{NY9Bvy2Qddb9&dB*eBvSY-4ZLaOeO zX|e2Z#j<8fd<(ocyA)s+xLuIKTGR z)PUp`;9+}axWq@xP+gS!93wnX5DhgA5I|xwQbM@sQ?OmR|I9qLrFRP06(66PoEdDb zOaYoSE-@_ymW-bYvIhW1;H%IIwlv$i3;d&MM&KT{ec8s^Hp(IwLHI8~lbV(mF4W{; z>>wr~r2=8nM9l+}bJO$cn#bWDwqVV}qf zfbO*QR|dSv?+b-6&oxC_JJ_oVvVk_CrlO{$rw1LKlAMC-Kv|Gd(Q^ns(sc>XC~F(~ zYzudO4)$UI zbeEP3LMwu3l35`8pnt%_FLrcy4|Ya-CcqKd$NQsYp6cet(l;)#pQWV(@n~qNsHn+- zwxOV9WM(0pwQ~YOA{4w0T8_jbY_rcYVw16r)Ry$%XglcftH$#h>)Ct z`^FRJu&9KT)Fhylk}^`0GYg9=Ybr}XbnL9WuO1!%7i#b5>Kq=(bP70due~+enQm*O zXQp<8hn<-T#CD>gp#^nAN=8Y0mV=v5KmbgeE-;@t!@_&>iCsWQ6c};i;*+z=%1f(( z(LhOVb|xSV@?8g7)wUC}2cV;Qu0Hv_2Y~FrKx2lBtd5zi;Ca|NQI@mx^dS5a5N^PO zk%e6V`2Wg062Eg>Tu#FxATG1Gs;Z*Ays`#371r03l$={?oO#HpW8D(n0ItWe!#*5QPe^PT_i;oRs=3^EnQ_J5N-#tpd`k zJNicf^4^p3X8b zGBPp3Wt)bUnu?yC|JrRiW4|omC)(apS5X9DQxLin$QRu4u^Ygb!2F>PIF1aLdzG&_4`}tsSj`3gKw%NjMtoulLOIrTMdamw7I*!OqdKv9oiqGcf=u zqo!p9?Q}~oBBuy=tAQqOY5`Co@G0vbv>Q4x(BIQPFg{ryZV7u=)(fy$=yZQ+w1x6> zD>q}=`y%|DtgJA0Vh|~g4IXjt?74Fsg3>lgB{cwj0r+lL53G-HU}zX1Ily{k01z9V z?8|pG_eiaTgR$)Uvz-N=8j68UI(7FMQ=@?s+l_287crdJP*PbGP8iDU^;i< zBL6)d-@@ju;nA`2k)eZ}i~wrDc&Dd#a;~Q|O5fP+D;P^X+*zI(Y#@1G-_}Y`QCwJn z`#dWvkUbXG3tR$VNGk6ffQ^yDi3|-51JUXOVD`}D%urpVp1xyr&H);Gd4I6CA=yV; z>Y*Xno7V#|76Lxc zSx5c3mfABI@Pz?ZpzgkJlLmkh2k=iE0M-yqWiifX#`-$uK9N~f z|HWhT6M|kEs6J7Y2hkw3Y=GBNL}_g~FgPhl3Gs5Zx6pZ}Yv~c0Uh;=;?O8*>v&YjZ;#9U~Au_)S7?$yeW6oc;dZ)|TqLr0^hbCm787lqra- zVr37)BLs#5-`cX8|M=Fv@9Ss)8}`7V9e5k3XQzOeqF@f!(AhWeA3XNcC%A8I=fQ|H z1mXkFAjV_=JXQ|!zt{iP!2j03FV+AG76Bz4%f(BVxVeSy+y>Vzt}|2w=)XwiH$OkY z3v>00%`RyM=5k|Wz$g@4yQ5Y4@wS4rSied0XOm&m327&FAF%xXzul4A42Qeq$9|UB zuYQLLWjAh}{aWDfxqp4L^S;!W?IiNAlKzSBk1-iVc7r1b|MuD(diNB5oWQ?%p%8Kz z6m9-3>pwqO&#?P%%>REOAJ|t@``7;@{&T+>_sLQI1fo!OXK*5RY0WI`txGmQ_xC5%ez^w74g3Bz_Tb!FBix^?^*=ttHUX{`|Kjz@@Rw#M zs0zSF@?X8?RDWSOfGQ3BtIXlVChz}r22h^;`grsA`JH%x^m8(RB-Z=))PCcODwdz( zk9;osH;Dg4lC7XW&Y}8oHYnqNYQ$Ls(VAKto12>(8alxi!5=3h{V*HrKA1md4gV{i z$xjS9{aGm{qL&y*@CXP9peJeA1;y<1+y8#7`1t5=We{2E;FPH!lw+qm#^p9KF6!Z; z94F&~l^&e6^@F7QmEWD#beIYRLX`HS^!xi?stg29p&^uHY+BjigVV5%rm*GmH!Xmv z|Ks`nkkzFhsB$DigU%ldPnXb-XJovcM`s>{AWMIE>e~uC{EW%uTt4w55~)8N2#6zo zp!PQ^b*X-wdyp3-oU=y#KghtxCr6V&LAeb=u^n;keDXZ^H;wm4RX|1JKOI2?ylyzM z0+7h>S2BH)P?+AR3NCQrFD|`6$jJ6{aANm88 z?U5tMX3*(FpZ?_mH|8k*yCwI&>F<9@hUoUR;gM?O{gWe<5R&9klDn7Bi-w5H58h!L z%pXyhy<8f^=`ehDjz}_i|JNswcy{lPp!QJ#F|{*C_5Eej-`H0{Vvah3|9%`4Q3W79 zIznjI{r^VkAfg5|foS^?85rY3l;UK{5uGoK6Vc~?{|-|Dgi1toZCfI0fb&P~E7|?3 z?ERUw)4@k*(@F$g5LM?0rPU?yC%s?(y??}TIE{ehse{uazpt*Lr@y~~CVY#y0+yeD zKY*XF`<^#=vlD#k`)_}`BAAO165Y!p_?85+He%_U7X9|iUwOuIKs=pjXaAN8jyuAf zZpV^-`{l1ZBk{K)B>VZXe|`}5^84$5kbmgmWsu+> zM>!wP!@t5`9%1<%Njyz904>`JqL;;p_bE zH04mDpL!szK*;|a{Y!{=`W5rf#~+pt2>E|A{$P3|oT*n*|5O=&^wBlMtIBW2-(%Vc z=RX_&X!Z@y7ODZ_omtgy@+^sP{fNkmUZN-1l3M$0QKMP6 zzdZ=pAyjJndzEJplz_e#ac1`i`F?W^&Ww0bv%igSM+?Vb`G^d({N5^zG#=rYvcLc7 zI)dUuAXgF2LE(Aw`CeQQQaJRI-1~?Pwf(~Hc7F~M5+oq*g8TamT8CFFhxi=vfQES8 z14{Ej^Ly!o6G2i%I0nHhU?%hKukF7>@STs^Oy93QYF5o!4d;dUsyJfP{D{({9I@{L zh_Cw|UV8kO0*)=>%^G-)Hq3uT4IxW)Aua}Z+stxQcRI$Ab7ZRho>ziL<@>)S_$FmFg56j?ueDgS1PA9 z>JX1&e^%>);ok_+<;p)lJ>vIsrL__}B4vPdieA3>NYw2swN<}w_8kuu9{zx?f7B@O zpZsjBWXte!QsSchw;(6L;be zFPguj`3o<4y$|oW{H46#-5w*h{RtzWUtqq!yQqEYzuomahTpxQ^FX|8{}rBp{kBK- z*OoCzbdE>O-u_DVXYYrNj~;ZtEBn_MM3(7a!ETxCFP9$F0TQlp z*H7rwzi)!Vhllt2j;QeOUt~n#q$768~mK^exZ7Jomd?{&t_`r;GmQhe+h+sR+T5zwkkL zyV(*a@@q=xPnO^OA`z+g|9Do1fd5`isKh_9TsV{mG;X7TUm6g$7t`(%{PwoRA>Lmf zP+3&6Mt)WhRyy81r1+K51_8tGUZGH4e^vN1m9SA@#QD#+&A;OhUL0rEOjz3Zfhg>5 ztT)1x82_=%BVvB=4u$ltu6_0hV#kR2$w*^W{=>M&k(7S>56CFQtl~lGMc{0lo<6ul zO!oD)g3}_4WA1Siqx}8+`nMVXB_T2@+G%QPmP`CP9xojp@bIvZg8)rf|95!*B^%%t z6$$D81>3`!_}{1ht%3hfYd}+log0FLnd2fZcJnwE34ESew|8OUq@Z@1war@kzPI=ecmaKzc-*3k~Z$ftAUFt@YGvd^?~8_ z;GE}D1WZDJDg4o8Bzdyy%sWr-x;YZu zI&)L-WM_uir_7ZT6g4jl7j;Gx_f)!r{BdwUlTnrUU96%RmkHOD>oPnw*5mRigyUjG z^Bn`}SQJc34Q?G3+i~|-w6E1KToeq#tU#~6#(O(YkULOwY~^@gi!J^X!@rdAEt1=(44_O<`Oj1`Ega;CoSwk=><-i zmC29d{wNC^)oWjF-&idu?j7{@MW4JV5PP=AMsTlqeNEqW+RW$kLKelV?$yUWypIM4 zr#$f9RgySTTvaFh7Zj$xO?~*eP4ehyJ=dl za3W8hVC`{6-j}iM&|&&skD}8rt8*j!?tjd-oiE5Z6+~)dq!b;gJ!EjV(5eBnh*3HcRChS^GaRUuGJ82dU0Hr4Jo0!8|=!nJRpwEfM#O1Ff$>?){7om z*z{z4D<317RiD|jZ!>Ov%k>_TCucR@tDxu54C!tfR!+u(`X7a%~GCa;q)LS ze`qeun{QAh`LdQ$ft$di#6d&W#}r6~ClkBFS`)g5XQyniO(S>E;%TvZD&8r8Jsv8E zsL<7irbr)(`!mn=n)UTsgsuy8hOnQc6M;fdv7o3j8A5$NlGFW<6s34v6eO(gPM=Ms z+e0bM^WmyKElCzF;V(_mpw@pwG^z>-DvAja#UUij%UgN#WyqrC)B4)Ty1cH}SyTuc ztaEmCaj`p5Dph{rZEtRgKOMuMD0XmG+5g@DZpNePQ${yf<1G}(CP#SgrP9@hnN!zW z&EIavikW7Wzk}s}OJa`T(WYS_vpcqE3A>2?rlk+F{s^BKR`mo^SlEejfJ02(%~;=jNqxEEL;q0bN>Z8cC|^9PH46J;R6|G?y{No=a)Y8+ygyy_ zUUyW+gIl+%%_ZcTeFrRkF3jeu@?E(Xl&RJs-Q=_N!u#P2cABC0g#b4-i;}ur-|37z zxsD*>XE!rDmZs&bA4&VA#3Xh{l}NMXUL7u0V~w}#pL!yw{JP$?QaN8ly-1fQ4Be=n za9K28`j%_8vMkf|DC=w2faU6mSGVH(m|0ok_&ASQnG(%X3hum4o<6Ve zxMiignqNw$MQU0y<)O%HQF0t@mVlSMA#x&6I(-b}d^uZH57CmqshDcZ-3Hb4TZ!z8 zHOd0Rx0cPKkZwI`@j(?S4P+ai>{LZtZVfoUBAG7+go^FVCi?&=Or}KE8xw%~PqR`TcXCRC`wzjdexU+F(HcVDv zT@G1k={Un6`Yq6BWJL&WW5tAgFHvF*$H+YMKjqhQV{)Km&yote~IZb_)# zp{v*0X;I$g-EqyPAdb1e$Pcwb(|dCjB53VW_7K?%4e9ePXPoDHQmx&(*{c@HwWSad zZcnESNgF$aAMJM3E$696*k>}5F+xCqh6zzXZ1_bEEI-( zP4Fr*jDqrv;G1kAm<66A_wtK(NhqPX{`3)5?p6Ym+yM*sCM^pBtJaVw&eLl7c;p4( zx-QWjx8s;KKXFxB5GRg??@Cu^V@Ai-_&~q7ha#BSvK^Y2d~fA{@JJ5dIAb=VXp<|) zSmMu|Ji<7$EcL#-#o9uazhoh$kX>cQPfILkJs(vJqilO9{7MRUp5Y>M__6KxJNG+S zr!_orI=W-UlWnwlpYU^q&9sPhNS-ITx8hsR;)x%Mu60#ShR1m!tnLJ|OP(+{cMTW! zIcGU!7h&Ts&n^b|v3DfS+$O3cxw$YUZRenvNyvDw0*P{#b#~GD3-x_i5%%h?+6U3| z3iZ?ZH&b}XY|fXLq^gz)ka+s)@rrdA`b3G~ygPq~pXQ=24ki+Ej-VCBlb6^0uD!>( z`cNDNZ3cBE(dre>q+&+eeFgtHm@Z7+E2RYU6UJ;nD@(_4#>2YczL@ z3o@slvCCCGgR)gwHtoGslej9>>Ll_6Wu9X{fOCosjgWAj8uFY@Dw_d&bAw24kQ}A9 zL$JbpRt|Zr*d010sNao3k9w*eeYvp+NE4573Aa=3O^yU&&Q%{CjO4q5G~o^5DC}3w z#nl3zad}|~Lj8ywq0U#KE_y@j{U1UezG`sT4VSwm`|6CbeFMtb@jTg7d7T~-sfFwr zdW?$1!RDC-5$LdNig9xB+tSrx!V*5qFa5sns`pF7Z>NU6_@sed_wj7%ax->s?)$_V z;?<8gXA`IP3=4`VnRgdeUt3Uxuvk+#u*JkX)AgcybNJT}cXtYpshfFcF|9=5DEmRf zGHFr5QQoV{oEMr^n@DLzIbpx5r|pA5h9W3Qg7SzQMl{9wf#C}3Id%+3_p3+(q$m)T z2pcGp7S&gYPWC1?W*?XQbjiY3- znVFSJvdSVU>hPoana$bcV4-Mf%Ve~O5 zkqfJ}I44r@E|4mH>^`O|p3n(RVUNYI>8h3$D>8yDcb2F)RGQ-VRP1aYHaH^?dqG^5<>ED&mEgJcVoNM8gS>Xtgwh3?V!IAenNcgMMPOTJ_ z)B5)~FX?CO3O`otT(N1N6AU=<^^Dq$wxA2D8eO+#eP#4}Uli;hI~Jlt zohap-)bF)OHEv;$zqx5Qh=!*hLm|&fXjd4CZZPD$l#h~VR-r3HRqQUSH`0PUT~+wj z!m&t(is_Z3C&iX)_)7d5zMa zMjyjYmy33hb7$3^;(sri{s_K(oO$o)6utzzv!396_h=_7C>EV|IA{og3VaKWbFC9 zoUjyB2V38dY<Hk~B0O{QI<(*OT~HA6PEv-v7G-7M2b+!hi6s(Mv zh74LmHclmNk1>z~&3*6Ln=1^v$+;s5uZ@itN$)Ky5WQ00C&J#dw{I`HM0W1IuV{*Q zOn!28M6g1Wp6$Drm35N-owL`QQhV5!Z8zHN*auES`&Zocr}a6#G)_b~`v_+lV+hK5 zLrq`1^pbM$rIFe}MeaHBFPrWRk0b`qPPZtk=G<27GrMC_PRBK&M~}{ZK}YUgZ>r$V z#w6oJGmq=)j5l}F#8A)@12qPpz0sFoyGR0xep4MYR6+%RYLcnX)#Gt>+l(4^SM@}L zT3_8iZ60~G<0FOBL-r}THN`e3A}VTe$R)gn>%0<7ka^#hM|}PL1J@*oHKqhJbl4AT4b(GOH&`0LX%%*Nh9=vVu2FnK0Ja0vBvR@^VxH?j|7QI>C$(4lp?S&;0}p%nS-lvLC5Ih0MFdwXqY;<72Z z{WNv8G!L||KmM1oZFBhXg&Y(sSSn` zuCSq1a_w@L=_b03q}J;8ZN*X}anw5dFU-4p?X84!Uv3+FTQy`4ndMXLRD;~BJ_?gH(E=_l&Z_NxGr!%yCi#kP=i<2s}5IYbqeeJ}G6V^2W zs}ox!*xo2vgocH~Q$*ckrfqKR_B@pyc%DoGs0CC33Dahg?b6yt1C50y_t@@b^Ep{0 zCkK#Bci8lu%`TA*y3-QIXQEx_CYED_&Hc7smoT;-A8)6u3=h4dk3k&~PyS}XO? zDV<#jBwQ(Csv9U?5I^L!86JGWP3vQIcRohc;6Z9Jt!#6xc%5CZdp&Z}@h#Jn^I^L^ z?bx48Rk)_6fBuV01TDmpJCfe~_yqUEO8Yc{7mmq_Fo*CcuL%=&b$r{+HesjJMzY0+P*O`?_V zv|@Y+j_IC!FzqGi)9`or5CP7Io}~mzRm9-opgIto%W=upFi%pFxOs0+XrIp+8k=A z>l8JQ*XVu7ZMXHh?0l*4GNww00*Diepd+f0K8CK{c(YZ>L-yK0QS&&+b7t$nHF}gC zClqM(3i6fPdZRjWQcrCkbXI)I8u31ZBpvp^ahApg3OR3@dR*Q3 z+U4#|zzLfh>YY7}jiKG;zH+ZiJpimXvmt`gC6<1{PW=ft_yAj%!bb_f@YACgbiR|K{Qq%xdbUthh@&aahOv%jFYPaRBE`!g=&}C|; z$<1_8qD!+SZ^#`^oJGQdMsT^<3N2VD2hG1BO|MH~gK5g-3-`TnA)gHr{P5BMf`ze< z2jMm>5FB-cH1!xm#MwK@$#uEey6`zJK2xEOhXscW^OGTa7t=0puU)n5k?}`viW->8 zsuL$Isy(CBIOSoqDZvp>c=Lf}vV&q7V|jb}7o;0_X{3hR!>aD-;#|`-yeH&e_Mr{b zv)-r>i_M%eg_*OD3_N?4^FDcWR6A!+QRa<}Q9ltGf2f5eIZ`!=rGCz-(_=X$@+CP* z!B?&baJD3elDWSwWxo>uI#P>`_W4$>o3z{latbC-jC$}UJ5S7avoG?zR=D=zq#g;s zQIo~7-Tq(&+&u#1QT%OTaYeqoya>+ObiES4$qJXd7i8KzBi{(dr4<>FL^9W=?wXh zFhWMQt;={#ru-Q*+;%#m^FE%ORlAaff-*lbYQh4HA>tpp2gUbf4ZNY2@0_J-}@O>QZ0zi;v3AhW@u(p1y9^&IC4 zPiwJ+|4L=>lvu?TJEYI3f(FzC-gC}-jtlB>FETV~Y)&l3Nr#1O2UghEk@pzqrBu~y z&!BU0+KuOLZuINk!et~~TuFX`9~Jm%*OVP^)A0kf#5wzQCn8T5Pu_EzH}Z{UTCiIc z3b%M0Td50)aK$CYt_u(K(Gl&*r0iP?LAjZ4UmlL|nDJy;DB>8(I|GSwikhOGnq8i0 zU&j9!|0IKE#&Ksqr2wK54CiC#I&J7@= zn7iI~YmwJzW<`_va`Wn>*WuMc{`hvi^?Y4LE5i6%^Lf)>fh&1IrITHL<$1N52y4~1iHsQ~(t!Ay$)@d{t!c?4GG@sDM&kM=e zJ-w=9s^oW*$JU*o#_8o$Q!%o0G8Sa7O4|Gqo~^8So%)t=Fw)38(f-4Gm(MUqId46< zfm{@7yDD~?fSA)ERzSO`m3M3QvubedI=B7(1p%$9Odb<~vjd{aP_1aBb_4Ca$Cr#= z!_?K(s~Bmib8Z%~4>YB&+1xdm5Ly&a%?j^5)=mu>UUM#Y(DBB9M2&wJ*Al?4*EcM>!O zqqJ%=w3nwz>FdI2M^t35OGNlgIeYrSRM#SMmiURE7Di777L9tg7$p0>yP&`FMCQ4) zh@}dYCZ%GXdfi|dq`D#!`d;T^oQkxF zZ>7!CqDXPftl<0ffvv;B_zNXw@#P{@@F;lzW!&alwH`}6`hss8#-Um-Ac`d z!X!P>Avaw~Q!=M8-br4YV#s+SQS-*>0p#(RJ?B|g>|98o+#OtAD0Z0}5(nfl>lq&n zFB@!kZM}M&be-Y)mNJ7S?n~YlnP+{rS@UHaiAtmlp*Hx%`K4wsgLI*(Rx-EgJu2Fy z(kNM^45xcI?bga(Y-HBD!gh^~yG}T%$Md;_0>r3MPDLTD&Jk_470cA0NvK(m4tM+<3?03=dG@RX=?C5B?PO8wL`8TO&XgWWx zYb9S))0)d{kIxc*)9;|w(0ny(mTo4UaFvLIz09T4KfrP{_+16hyFh|t?jiQ~_0^xp zny=44*r(IXY3UhoxLw?_I}lL1Gi?%Pv$y$l`sSpG?UtTaVh*9Tt{Ph7Yrj>2_Sf-&NDeHMLa z!Z~Wb&GB2(mTsfkwADNc1>RWv%YEjfQ{x;Z23UHFlgzsOs&(QTG()Zo?jKvIkx@rJ z2}NFdR<3=@Wa1HD{&O8RrJV8UM}?cQgO5MzjH&M^Q#aeJ)uU6)UCqioQPN*V>FImV z4@GzF{gpV}WBYqc%FAzwUQ*WT>}T%EFLqXDNh}->Q2JniaR>VfJLas@%K=@^b!=k3kz5LFd`*Y>)i!*k{Fk&*p zirhN^FIAfaXpUbQP(^+%7H=5$nVE z5WJ=$KA&`l_BOPL6;02JQII|El&4H_TrS2ZK9x))D&2OdUzkA`-J(IL(+6?+)3i#y z>q%Z%`euBYWsI*nV3EAHj@4ZuGIJ6+7P&;nQ(S1(J72xZ8s+B{_sPm%Vr^q5dnL8B z$b54yeJY(;>qVl?i=2BnNDy890PUeX(x+R@!%ADDO85dMhJJoq#WYuW#j2{U<3S+91_gpwu;8j|ogcoj;Te#y=Z*8}Oi*ThY zmyER?cyWpD^|ITd>3SlQp}AF@9W@bhW{<}QlZgE-;kVmzt^(G`ks9x|X)&EV?OI$= z9}+ute}3bnt`>>J3=M=%>fVEf6!`VWS}LFhN|~H^Tr|N$JJX|7Z!$@ZIkKH@iN5CH zA}rxFHZ(;$Ok~evsQt|RL09V;*84Xb&WhrAVI|-p@8J2N(WEhIUWDdeBVWsRuE`pn z!xpyY>Zb0dWMHpe)kyj1c3OGQPKP>=>7%_COzB#z!fjaG>6PPK^-X5zamcrEiaBGS z8^}>n-}%fOh*NvgBR7}nXNYHsUs;Luf46&uV zEj!-jQJX9CPnV=_*jE?N=SeS2oEA{#2|3$#E=aRmis2%oQypI2B026UL7x?fSlLOg z@fQNLM7*2y7%)xU>pZM7lOQuzdhv2Fh&zjL83D*VF3X-HmoRc+JNX?R;@ucVfqGBxUfAG4?A%&R|BO(BZ4|j zc%t*9)w%l|!UhZY382?2n;PNzNIN?iN(Q_-Uj8Y%>ci!Qt<7d_X7|@V!NRNPEi!&@ zZ!$?s&Q+2`g|5HCth3{UFdbK$qB1(Wi(=ckpeNH5kcJmLQ?Br|nXj4@MW?t&cCS4# zaUs=)mB+}xrbQkftdjODlQ3b8nwQhVJ~SEl#*Sz06V--lplQyREDTGYrhQiXS@o=#}T52`u35KBM27upAfPG6|99-gFS zY2`_Gwx+F)tpbaCDzOpb#L6Av0ts>|$lg`F7)0EJI2N~{`Z%wUQ4Fu#Cyv`w_UUo+ zogNLrNAE-*EYw*(M`%`m(>n08iYTOHXIgTPaz9gb`|%YKil94%ttVpL*uLCYF}Ty; zYE;?hw1^d+EcgMroXdZ1nt}%ZGukYaXYhArcm5G??!-2s!)kn z_BP~ggFU3g`9nMz|8+x%fS15^bxc@6@h8{&C@sfK@(ApU>5(m3G>t=*_y}nTTHiP| zylGyE00$pK!}7g|Ok3H!MXGKZ;Om`7!NcW=jFivxU^R@TCz6l@Z?p6(@2DlLZY3Z&~`u=yhkB_$FnJ{13u+R^_$9rBVlax7Ya zw1At2hR4sV`gPf<<0G=R4%lNC_-M3QE1el|y%tvZ%~&{Vrpt36H*R4&AVcjf8kZCk zsYfg*gcz>}2}VHIgo(U37u8xdNuf#fH2M;u&ZRiE`4iKwi~UflNRloEOsCIb!I2tn zjQrI;*QX}!VP>WDIBtHL-Zdw1BBq@hk|!HCcw62b`;dcMcv2!a{Gs*5Y4i!VqFd-A zt&G>RoX_D{coI4;LB-gRocoXyq;raGph4Zi{V;(Rt6|UbCa;uLC*7h5NW|;09(vqR zH2u6s_DZLnUg>)ixkYLChCa1oB6ocFUYk;SJ{a%VtH~i$Evg42mqR#<19dLWiQKZf z0Yz;R5~Z{gsFJp9ZHbR1jyBB_XWiHF)!QE}Ssi$8Q-@wDt8GoEW@DJ+NseSXAH9;y z@cwaFp>gnjcKL;U7PR39ky(Kn+(h>-v2;{WVh{~IIuTWl4tr2qs~(8jetK@Am3A}# z5e+_3_P{-4#VK8WbVb!imba)mWhUO;xmOb&w@OuOJbQAdZsa)BQ4tqKF#N)0K3So- zWAcTM?}qt$r8zMvRwspUsc?qiH?Hx)YTYXh=-k}-oLdXr-4iu5@h;;EJyEE@ofgu{ z%ccD<=N@ zRB=4yDS0}B1u`jX?rHORY|lB{jKfu0UtDzA#>>I)R>VPyBcMVKmg?F~zm}oQfpYD- zVo%+DLM$U~+ZA4d#Gt3C=b(kqmiw#E-KhkV9Ir=X4tRS_oEmOxqJw-W-@toN=}8mP+(!ue&L^9;j44z6(Ph z#LpI%Fj7dK*Ei-6ry@`=a>UHGH*ks(&O46ud8j%6*qd8vB|0O=k?Jy3OBc|4JcUCd zSsJPCbB3m#lv4E26C2*mG4=DsY0eaC#KeU8GCFOaxKzck!GDr?NhDjOUg%Vqn_HW? zcl{?{9F((Z#l6lgkyo$MQm)%~G0KRCI+J(cJQFOEQ@_=Z=a)A_j`!^BJt#3B>ab)@y-LME}G=Okf> zWi7$#R!E=iWGRhM@K~kCRiGA1T2Je%?{>r1Am5ws<|_3brKG&Pw=s$3jKAF8CqRHQ z=ECr0VR~l7zEf{D%;QXB>0L|bk9&$eA1GRv_Lxq&QE;?wth<(fDH3FI?;nL-Lzy1C zGcV}w)T&DfRX=G8-Bjb{o)Lk}IL{!}>1NRfvV3;Eqc5!KT1NUn3MqxCWRjP28R^UW zT17v{Xqe8j@438Z&=XK6b(7g3K)#V&q?DYS;&c26vHtwJ0Lkl1ln(y~R6wi0 z<)Jcri2WcdZcbE@ddPV}9MuSec8Jaekj+(zSi~Uw0IKAlQ^pGJ8r+#SnUM<>J%Og8 z3Mf5sq+Q4w#56cJrP-E_3TDrd$9F0**v7U4ZKq8okev4|F}TKv0OdKN;I-HcBO$` zd}X=BE*fQrMryTK(<{R;X#GWu=)T6&3P$`DV)6<>ZL2>?DOeWf?G+Pgpw`>2; z@BZ%ZUiv7_Cj7_~fd7JVX8G!PCrziHnT?i@qNY8}B%b1kP8*H@1g!mz1q+nGF&#~B z+u?!iK-yY__0mmgK&KL=0U>&YXl&@JfG0t+%K;X%JeKM6yeEhh$D4Itr^UNXUspQnz%?lW%n}Ztap*6^df&lc zl<8R@k>9|#ZVIt1^2JZwx1LHc>>xc_Hg*Jvh~j}db*Ff4#fwMFSlyD#mluxIeq7ycMk8p_g+^t_LJ}E?ITP8 zjNNy=UTo}q5pMtMgWl{P>`y9Z4yF}m^I@pdh=U`mvbF}X5*#2b?_w}aB6=7wTM)i` z1CFWy#-DR2C0y0HfFsF!dDG*3_1)Faoeq57AtIE;ImOVy#i1~7P**b8c z7hWKv7JbEG%jP{<8c0Pw{bMaWez_k=A)#Ooa+KYVXX7=uklt~2iVNgtIs@<45SXa| zjtJNuBA6~N!vq~Mt@qzv85}aXJ zmN^en%BNLJa^aJ}UDSkFkRIY*V&azuLY5^@Vs$2@g91`Qxx_*y z%q&)*aP5RJHezE?Z&Z@d@XspF$pl8T0>+p@*~}MhU8<0A5_JO8M9OTeD&1!BO|%Sr+8@mEK_f;AmdS^(FT7dOE9wU#4P9EXS-86oJoAb(y2ynDs-ZRargtXzaw+MhVNN!#PLA8Ha zhDcu^4>|ecJAE(Ts=BbQKp>}ju}MX+(TO`nkp=2jK{ro9YLRKmefhDrkt}csNKaW= zAWw*bj#S*UfkPUTC`ckWimVwCP-It(r%u$>xfXKbnA6X>G{JAAl=w~#*@Z`M#?TJ; z^f|USDcirut*Rnn@onbi9zPtY2L`@{Z~_5XNVO$QW!h;$tI^(?Ps{iUM63k9Va8km zeFc`NKv)^$9Xfr5Jpom;Q*Yi~n6I8WN{WL@zSv`($kE+*-|f;%R{f6LK5_&AfGK{j zm$V7v`q;9(_-lj1;XfSoCJ!GUEpok89nO#brih667mg)hpu94AfW8l=V^;tRp2OtH z4{(b3#K501H8$L0$q)f80LTz?g+R7IOo2mW6S+lZk==85@`ZRG*vSJv=PrVKN_pX= zvd)(g6c9}$}A?Z0uTg~XPFa}MGzv71k}JH zj@=iWXTg1OAXF+P0GI5D}A+K1AZB zQWT1XG&!rx41xu^Q$(x>+a zCDxIHIzwY80z1GY|BJC+Sp^&Y4ln`_9@*vi8Rs`mKp>I-5P89Ht=}Qy1GGi3&Rqf^ z;wWV*@(Yvd`GNc#H6rC=0uOnGSH)Qff(V_d2pi<*Io>JUyX&1t_84m?XCQbTZjET)-MwV#rJ`S*$b7*0FVvP<(Y*kzPPD z$Or&9lJsb^E*~3?qOSyaGwoNE zA@d`MYC{mngE${w7iIF5)F1rhYpI*W!>ruP=5}yFM`naU#3nC!!o!gFD;4SyD!I_T zc!hVNKxC6hXjC%^Ekg7pR2`(pd42!^gfQ?>wb6RIFdFT(j|bh0qj#1_0)O_{V|QLW zfsYIUu;X5zEbk{a|1b83<6ln4`Ln|T93`zVv4^o~2!IIw!<(Ju=B?m5bgZ1xbn~0O z#xJu0Y+n1rue$&jY2=hEbFOH(Ap#PB&G>x~1Q1s)pNq*na(nWMK$a74$bKw4ba{yf zNj@tf@L9$1a8yG4c3!;dh8}BS{f4W%Ac?Ey%GE_TkNU5R@J76u&BiRINdz1mBKk*N zJlR}YiwQ3TyqG%YJtP8v9b_%Q?INrhjiHe&zExzSvKa%0&?s$CkAf>36I8Q7RNY#W zpxms>*WB7pDlF0|F|cvu$`VxdcOxhpN^QpT6U#%d&%tH$l#>wv2qAUJt6O7HQ%ASG z3t?oX3^A8Y`#{LxlxdhY5i;DNK|nh&)CuY?s+!d@csjlXtc)_CFsKN$uvC;o7L(BO z$O|xNchJ2!A{|K}R%x`0BSqmjX4w=jk#gcuL`L>4*Z}MW4gM?&n=scbDf3O2AsD&6 zQ+IDyYyV?XnWXoe+rIwFE3aImGIx|Z@R1+@Uw>`ce(h57&~V)TQh&VswZUMyHyjm8 zL(D6{4x*Uiq+n<7!$rh4H~Ly?FBpYhNIp554rkB+yHAV@%%p4Mn2DDG%K<0?2z{UJ zUML9&K#YlJ+&@F^kQgWf7`3DTMme;=>h7ydSIG#Y97i2=oUCJ-d<#5@>cs3s0Lo*D z_l3H_(FsPA8R4z_$4Q?BGY1@9LOa$a#HZfXeH8umj?iky#2+elG49D8U-XiZ2I4vCG1!=!kOhaJl#$^Cz;HeEF z@79~Gq(Qi?4kImfqzAd9Q*DP7JcekEK`sVt7*Z?Zz6)m1F%t;Hly8lG5Ltace-f5; zg{TuP%5}&<5_Aa%fA+M(yy)%giAWbCzVNSo(iMBE7puf}Ks^Abw!c5`>SsDGi-tzIZK8h?PJ z(2YCEQRt2*003kDdzU6>=gZ=+4=4G*Kc1F;vD=#x@_B{m&yW;Asf(^6Yz{zD&$iz; zTl6)ZshB6{i}~l=3?QdNGeGjVd4eMVBsNCc2Kh-7xEz;`NhDlLJA&)0?+ZK`OvU4l z6&C=^Kwbb!_!Pn2L#+TP1Nm{DDN;#Aee=TTV~8;G;Nv-ltmIDGDJF>ZBL9sWSCT7N zE+yA4zndJgG}a^?g}8O@*={GN&zw&7*o(Wlq}|NnB*VBnWz1-o z-YlOCkZy)@P3kv*7!4SMA7YOnOoMMxu8BJMULK*}IMNQ{o4Z6w3Y@2nm6pO!faim7 z*f%)%gapAmRvHN{ga;*P@i9>lWkko3M80fhX7(O)MH~pMn+x9)yYCV%c;C9DP#@?U z=q@luM3J@qMS`I!RYYC7@AR23v>VMCW{8VV{?}+V=CII&J)xltIF&RHH zp5(tUg!3E4XXupPvC&-MD)TykBHfiM(6#J#-UfgplCA_msxDy1T{5xx>oXzh`#dlr z#|UE{1>J6e4@8c8iufQEdFf-0yxDVi9(Ru|3eQfXF4RF$-c10aHtU)IBmjRE&RGS4 zU0l@Yy{W-0S@!DsmCMP|;bGD}K0*RO&t%s-Z8$&zV0ZBgDV_$aB7sXStD^5afL8$- z33;7awbk9D-qsx;j_{%mFd8Gk48@n7%jOIMX_YtM^xu3%6CWz}s26ml*lyhw_ zMiW*tuzg3w=D%|7N^*pha_QZx)PZHv(Vwdr(W*!WwcU17+1X0AT8(6i{h)x|f|y(w z@d(&WIeFe`6k>v#xh;s+ZydGx|OGo)8v;2!}(*~L9){uKmb7g zG2^iM_^a0os2K;NU+70WRnD*e7E^O0&#m?jgtyn(0vV(8xe$&5A=bozYuWim-_Ili z^YFe%=bU0fmnX0I?X^e#BfAYOaqx9M7h*lgAHg@cX6<^MQ1I2qI=))C@MQ`vjNq_v z`{U!};@cOJ>({QaqRl?wg@KB~o%`;xVKCLC(yrr{%6K&sT}lvu2(Zch3Wy)kbC~2( zK^)b9mk|ItlUz*1=Q{!BI0hC7(Cjqe#+BCVP$@)65g%lqNeECeChX~ibkz;gCLnM` z4JME6J|zGH9-2vy^1waj?$?vnb|=}MoJu+%b`_PR$b9}L^)VD!1LvC&+PcIG(r|eCBh&yHf%imi&&Wud1#zIA12`y8Cn2}U0i{@> z4rKO*43Xn8%hf#<-YIFFbzcaiIBUyWOz_pl~j?l}sv+_tX4yqiN-tgKqx8{-9VG^b0Hs z35R4jX5yG^AV1tStiQLAUrWE@estKnU&X?Jb1*UphMgGL2{4GHz=d2r@&U2x`4kH# zb5qgZJftxYo}F{8XeeSi)E8Ol{1E3kT9m|~OhUCHK=C|3M5_-q!0=;LIDP>&V8LRX zhsW2F{p(kf>z6JQ5Q8)f99NJ44i68KF5J{P?{%#1bwuo11HHQ5M#ruRV6j|TNiI)a z8M|IMY^5#^u~_N*&L{;AYt*$^;FJ0OzzWCv{V1#?jo)Y3v{36H zIC&%hyIStPgi46iegj2tl>5^|=7|I{Ql13gk%pLdEX)MwP>#v|!uAlV17$#{d0X~A zh=XuqL#du<4~S7BI$ZhQ-dF{|4cVw z_Kw`9X>W^g&7T{Mik}$`7k9BvUvWGxgZQA&j2%Hrz1R-1!vj|3`h1N(+abc5NdjSC z(Gi*c7C;@}>@xuifym~``V><_Aaroa^*vq@z#{U5|NhNqBqjk%!8oEGmctu31UK-U zD#f`HfE-&fKM#>fCm_eThe7W!xpw2-f8oj{v7}m3)l?F5bD)KvSKgB}dJ-nVAA6-w54z4B# zhgXxs{p)c2{aCuY&O(3p%Yo+RNKAeuJ!Wi9PR2hvXvu1NKLr=&LhgLfQYx!x${}|SO zbaXUhrQPWhPdrhdbh}#+6V~EQYNOeRF~3rWV5hC+awb8jq9<>m2He(}F0cD05jp5(GJ`mpu0nnyV zWetNe?T%8x&ak2?2ArE7hp0d(?ZiMxny}_e{hi2QK0BJm z1FF2uMjE{-O;QzzKZyqCe6M%1JY*BjGFZ&c^+L3229CjJkX4DazZ8=F>Y)UDn>7oc zT-BDp@cl~T>hSXASHH}df30r=yK7c0B@?Q;wc|H^n+{u{mi^6wsxOSLYh z;r^rs_s8lVv-9Bc(a%|D+F|l7hxcOYuNGf7u-+UZqkWyxcmTqC@vBt8*?PCL0}i;3 zVLgB)3(CQh4*PBHZ;GcBed_hMH@ud^Oj`DxfTI1}V2&cc95RsoyR1(9 zp9yC14&&x2iR{`v({`&tFIy5WLRr zspQQ4_a==~JNOVN{)70ra*P&qp{QLtvybD=5JVi&zD`66X^@W8D=|Y?WkWx5eNDeC zQ2_!DWdHnYGgKm2X$JrvaxfM9(4k(g4OWVgBCHDE$qhGCe(+XW$R^TD1boPIiF=l|CKi#-x%Z+RkzHWn(%nZmQIeMH>oOY#I;{FtfJw1m z0zZWN4f{vv$H&Q_cZ8MyMsfpd`DoaUapY=TPpR!-+c=jt2FbA6!@T-* zL;wIF07*naR6n4C`U5c-vWzz>!br-lHz2)Q$5xO>5-7Adb)) zL$2#8Jg0rb-8&mVgxlNqG)nYYhM))%l&WH3r)BTRMZ^;OP~N|`4|pqj)q{6}CWMp` zs-$_93TSjU@0-svlT<-7S7yiv1j|v9JobPAz7xmg9#bqAR(ZKvDZh@P z?=?bMzll_E6(^h#LzKPBVsX6tzy9_h_x$re5ji*C`9U5ZdE^npW4j;S{+`v#FPAU2 z+xK;6!=IbYCXY25&F$GF{mi7FRz~G&xdtU`)f*KEK(32T;PBu8Vn0oeF?b%~;8fVZ zh7@orX`ellH1~Fc5V%kXTr&s(ILym7kTCccfFvy0xAr=H6oy0+d_IOqg{dgSJi=r~6f)v~VG-)Hpj;=J z>5@FafLUc{jmF$wd%IK3=Rch`>(AuM#Vf%7b(jhe{NZkQj0C{PuU@`5yMH{IeZJdW zlzSrbA^xlPW$J{EHbpwr0fCBK9!q|eHL!CGU&V3f219yXqySw}RSP=pD#+hRcCdn9-Tn z&CtXE5&snbGg!&g11>Uv5p>qG3ndQ94NwEqLKxzT2V7flvssSMDjfexHM+Hb+N>@2-#R+l2Zk^6EH(S^wmX6V@ZsgxE?zu!I5_^o zEG_;#BSux!M2tlG`s46};SU>85CHliBhIw8IDd1y{haoX%^tNEH=F5pkbmBCr*Cc| zI%>LNJj{Y42+uvo-81n%`ZO4e7JM5{H{Eh@e(u3hDh%~iBR~H7Sog<>_J`LP#yhx< zj(df1eE^w{W6m2xca;F-t?jL(S+5T|jmGs(vo$~kc&lD(|CegL@`FyZd44h)jj*tF zPd)l*fAbk1mE-fU%;&W+WV--fzVOP08`E~{QnfNUhgVNhf^fw-HzpYeD`QWxYf&jd7;ld7f z^)JrT>6gai*)t%1Zj8l$F)h)6gsGIGi}*?a(J@&k*en?l$V4yOJ-E3yJon%+&a+PR zd~@?X$WMAqx`7Dj4-kM5Vjlz`hy;A!JQ4+d!n#jBqy9L9OfR`wy`0p`Sbmv&b98Ws z1CK*`Z{f*zBkAL{r>I|J@Shlx!SQ#uw^m!*?G<|QMV6)hL*mQ*0AlbO!8YEg%;(on z-F^4bSHAL<-1BnzkHSGRe)iRh;0YvnAfj= zf-wj(7=WpTm#DKktdSf&2hOz>lqsI$-#-$RJ?=_GBiJSIAoTh=f5xar+j8 z!vxDEW7Gkv

XHS4Ozyd&EfYbTF8y);W2u@yWHRT*Nj?6qlabomefT9OR4Py`zp&5 z9qyf=>YPMJn1E1b8l2!}7%q%T(dnd%`1r|0Ihu;JA3MxvBXKK^TKMJQ6<$)|8Lj^% z^v{x0fe8;i#-Me4<7#sKs$=?ZLkO|ZD7#^qh$V^5Eo_#i5Ahl=<##b0P!e~-_sm=rVoT=R3wBCc_C!M|dE zb=6qz6^iB0*P6{QH5x69pxy9$XyW&9ghY_y7&SXQN)GT|m|-AnY`2p-2Eq=*qE$vs z3b;u_f0JPeS8*pBk?-b4U%`m!IWo8_4bZpY6(~G$sj6p=5Z-|$C5udp+_fD8$iO@ni|)mk{90 z!Q>kDAOLvB07M4vP!%Ymj;C<>e)mSQfB6C)eV3DChW9k`)vBcgPM_@UfcWk9w6nE+ zP_H-BTCK6q@>Ktxa9iKSa{oFO^vevbG27wezd0igKp(#J(o2(vAAYzuUX8y~omKCi z3?^4`CvM}2)WLBHyJs11;WC0kqj9i*ko5E+*xyfP9LN1uatcYH0YNP@T7Yn^)fL=k z$6L^;$1#OksgeGaGjH6S?NoA+2UrRbPsk!sX2Kmse*r3BM9K3uSPHN+JBTj5Aa@3P zrn#vmn1B6@P&)@k3i>V}&f+?WH!H(}*a8l4irQ|r&h|#*YP&Ihb5<^0Me2K#r#Gcp z;s>_XGcnB2dd7CwW+*h6@d)+fw!pAgM=#+biDe|lAk{Pt5cHJximinKqn znYKyaoUV_7*tX-Hhp(9J=|w`SQ~-;p1IuO;e{cOnxd{O zl51D4C08z;PY&_uo2LEf!EKCJTidN<598IT-94RoZnRsie+|`tqgbfCHJ(oYv_Bs2 zvyXo7_kNEr{p4(NIX2mJ?^1GURK8Lk^x*HJ5X7TG`lSO}Td)Kj|XsA6$Dk>0$6o zarkg%U#rncc6QoHt5qLv@9bP@HtIJTwZ;$7^}k*!)ZQ%BY8Q{MT-nDlXDl*U<4@YQ z{%*g0`r7ifUj8^gofIaA)Z-Qs^s0qHyQ7le_#50eD`6Q|X1L}PQ5kTT8w_1&R=cWAT|LoF* zUwrfM%~v1Zd02zs2fF>t2ta9;oI<*PW;iYXt?8=x**?)~dL!L^880O|#8mE2D{ILO z+@=|_F0zB{L9RAE7IK(en@*;8wc6X{(Us)Ma7+E4bRUIaWFkNJXn{Bfp}CEL52Oak z{;`?KiRZUri=B`IPD9ZFJMqr*~F6 z+uJF@G2Y(Z-uh;>TK%(Ht@%A5^DTykuM#o#j*JsW3;j&CNAfrQ2?YErmtMSdzOh{< zv|f2osSd9&qp@17Rvsh(2@%PP#iQPFeuytc|HeLqV3;%*8f@>}T(nyD+ zds1R~k2Lm8^aV#(qMsI6{uTKR^6+2Vf(VHCc2?GB@rdc0JqQ9=9(hTppR+ukk{Ib( zQarR70njF(&wtGJg9t>Bj+oiUup}MV#V~dG^5t;w*}r}XDPak>ZzVh1O+2(A zT>m~}`+vlI{XZ@gY8UhQ(b0`7)5%j$_3xyk8(`T0127VVf&7_20Tv;_5hRWg0UPQj6<7n5Y4JRHI7$O!Jmk8>eq?1`>JV4n z6hC&X{9BFb-tN}*PNz9Uq`TT~H~u}xmzYEI2CK#N0F2vA)3<-q$@ver0Bt1$?E^pK z^XJd^cgy8oq0h9*VzED8EDu@U>tQA$+z+QO97Bu;coU9!LTOER?2=*0ib#{j_OF0= z6c#WgC)VW{#786NkTHQQ$`G9#h$^74lSib!Ng%8d*OG^T9Z%Q0hipR%4fKbZZIU9t z#K$_o=rC*4V1@!Ph8yLem+X$W>b+tA-bS(f;5$STz4x)lj(GPi;JjJh`*ZT4 z5P&4PP#PaC_v*=PYnZOKS81aIcmEPT{=c54mBN6@XqmSkUR(5#o>Ll1=o%V}W@W$G z1T=+|Ir@M$FfuGn8XcQ0d9ALO^^tib+R-su{v`rI2#^M}0kFs1?I@gb$QaL1fV2Sz zKLCkf8zid#3s`x){nE)Lbbe;<5ti%t`a2NyMMSqbz1Lpu?rtC5edf%!8m;=9)mr8I zbn?Hw_ufaYeUP0)hF1@6W;3s^l%A-*DhU{FJtfFWS+{mAm6UUc?dW><2-Ss1 zPTPB*Th(fBGD`9diul0%0zM=HK*Q9g`S~(mn@oxiffRq6)_h_*V;(tCTc?Oai~wNB z!DA|h{V>Ie*U@|)v(3i5eWNQfj?NPXyoI>0 zy+EH5C-QhyYS52liE_4oeLp$8evQdIM-hpq!9!qjHpHqdo2qV938E_yVal*UT`?{6IX7jkmJ+DYJ zA-m-r6Zz%n=mT{L+f|mFjRrtebaYcP!P7mWI_n1AD^>HStIcynSUL*)b29BO%IT_j7o#7)y3CdKMj(8OayH@X^_hkstZe}5PwSSt)uPdyUS@YZVVBef z%jfp`UT9y@gXWY>xbZ83OAMs=K@>b_G=y6iEyp8e+9+~+9ewoiFJrMSx^y-3)klMC zc=%mP229_bGG&pduR8Zk_fG8|wL8u4Y;Em)tKO)79}(i^PkriB=Y8|Z_Wrd12q!l@ zxbnsug{^jjH9-h6UM$1p%f;PUx;HQ8%hMx_f&?RR(U%g$xdw4=a-2cQ7jV}Fqc}tC zO$G}!YPbx*MNS+oP|wfe0cz<7AhZI?jX3HwBqC?f>2VET9)KHR*6?Jqdtol%F0yck zB0{0bvhot{ON1am5$=qeW9AC<@DE^Qtk9oKA3hlL9{<(9@vASs_10TgAAImZeWBm? z?L#2|x!mJZTpaiJ?)}_DgYlx-V^zt)q{t8m5npy_~sC+ zXh8qg$ZqGjhtD?IWBxwR#5Bwh^Hl}NPydBC4-Fg(NpLSheerBW#^opxaDfh-`4~dV zVL=A*>F5U5{B{rkeRG)5gQ4%*rMHv)E9c3T&EPwCcAL5St?PTc?XMG@R&%R zTwQqTsfRuh4n6NXME;Zop8b4!*PNL=H3wPq4J?7sQbI3)J!X0FSa`O z_2j_79o>U4*wl&6)9JJpOy0S`Y`&KX)AcPl(aVJ4dgrO9?$g+}u1HksWC+TB=D~fPB zcEdQp=L|mVd_zA4tZ2ep1JW8$LgXh6Xvywv(z#N42rkd9K=c4JbNa`2J4EoDJWiN5 zygKf#Yop*S84-MS#0<$nv3#aCzyYdO{=u7XzIpAThaT$lerx)^Z668&KyS-4Tvoj{ zp6;g8{He)w1p$EaA@a-!?G3?$@M0X10QhPAXehC5-=B?w-cy(bXN4>NxoH))-(wXV zjs(78s;{-dH-Q*;*Wf7o*Kfqa7!LIjs%wsy-_g#_&L4CBEz-VCSgY6WzyJRC%!!Y} zPkug-762K<&?5qpjF>*XUu~adz@`X3Eq9jFS(BBaiwDFQ7(omSB64b{gIfSWRT&bl zf@H)~z_AOM;~3L=15B{_Km;OoAgCsi3Mw1$$)F`mPy&@8C>Xef^cg3l2Oyic-c7%4 z6TU55m|tK7G@DVEBZ%sRP;n5gPA=aZj>b;_g5Ry|)i1sN`s?S(X55AMZTnCNz@VW@htRq*fPmmzc9hy2c)}S%lf>Imh{J1Ro)+9!Rg=F9FbPG$nW?2ETo#^y+Ma z|GCk2cXpVQ-f6#yEAv-zPyIIl!S_J^E4^ONsjny7hui|c^E9yTr8GGrI_Hi!>2tU~AtV57O5y}# z<)|k;!lf6Qo!;?dxl-d7u0s$a9PvYuwv}(o2dI!Zb6|pop9T86e%GH>p$iJdk+t8 zB;BI}h!iu;Fd)_I&i3wo47nAKhY$gL1r~H}g}n?{XyP%LVxiagJY|gnS1pdfp}-Aba1-@^ zY<)%GH4+EN3R7gvc=->*?5z^w&ib|!lprBIq%lypcZ`z6y;Bk(C~GpR)(XS%xY?Z4 zA4W2Gv|MZaA(BHE;<2Ghw+jACo_#11z%(6IA+~4dv(@%|UMSi3MsqsLhJ`OHg~ zU)raf_bq;)gzf{~pjgFpQ8_yv*MA{h)Xt4(tmZqdp!=86{h3(1sKEWp5J9S<_%Cg9 zUpMNHt6a4E%^MjNjV|%WFN%Iq%*yDoSrPW(`s^BhFxhGVDso+<(WwbTe&~Zt94Qq> zkf{KkRI6y?ORDa|Cd=TVfALuN@>gq=6dqICg-6ZHPPicETca3DB5L zCDyzEqs?GVBzISZgfQGzJ@I|B1-^Uj$p-m1H$D2`?GJOg0e1%#(MG~eSjIReZ2W*> z=RWp;UOG9|9jBimT>39{s&_v~(LYp*h5zNY50n6a{JD!4Uungo` z#o#wHxF<;HuBUK@#(V@{r&09Bwcn5XMCE}%^q{;MHT5^^7k;Cu=(OlKJI`|m{_ytG z20)>@VwT|gRg8Y6g5kV+ti5+5=`rTNT(Z0q2EjIQ`e!5l{#$(irqazUr#q5R; zV@f=dz=$Q+hAb5~nogN1o1SHvmM7-v?4Fx1|LI=y=jQrA2>@@-*Ne@w#lq~q*(^QH zl+C*9)6X#mEJECJ92JCTw>`CtI$J8Rf8D@8vG%n3M$+2kQFHsTVjCH5=Jh!{mR)f_ zCjQX*1$IKT#nGj=SrmgIKf-un8Eg?Zzm3(u*=qgasngs42NdJ$_uO;OmCbukj(1iI z00XOcu3bByn@<0P^%Bmru0n6ucnqgb6L!4`m%X*lp|BT=v%0bp{coH>$K+k|%@`12 z9rp7A9{+1V`J?S1OhYl)-+RyW%`WR#1zzGOAYQmY$}C0%TzZYkLJ+IrXjn*R(=+|a zXzMG{kLKt2mUHQ>RAS0Mx=oce{RrNOrDmZDpag&c zWQ9cF>_2EP71~tm_6GO-hZ-_Ov@9)Jgoe(DvvPsw@q6Ky)#*Jjuzrw0L`*;O1v#ec7+O5h_r``A#E@ywpl-?JK)9EEkjYNGfSY8Jfr+=afWcdr?>$?Wvwr-xdyx{?@;+C!(io@&FlMg6kX<#P zpN^}e$@tN9ntt}bJ@}vR`ghCMDz_E#mpKM62_L^{tKM6eI1cw15 zuQx${9xU*lOzh8yKt<6X7r<%(57XH(^Z5xpIk<#h0+TXI^VL?nGTq*4yuG*8`Ono# z@vGnX#y7rC(8iM)eOZ&<(Z9d_+rK^ivp-wB)vOnPzu7GR-+P_y3#YcWC#O4GNeyJn zV-H|iw4{pB%@vAUIDXaHD{y{i6K;&ceq_c_04DM&Wrn1=Ij&nFOaIAD!c#Y&y9JjS zfQkd5255xDFCe8(EGQZTpo@F(aGI7U^ZBPHv-#t>)#0Zf>k$O;eb_!Q0&wAl3ocl< zvsli5vzT9eX}OpX|G;IV8DDod&X-EhTH=4tw*)r7C*?gCKaTpoC&N#;h^FQzYjTd9 z;7ZD#t=;BCj6;G>PEb?ItWU=f(yY_j{?1ls>ko+B`^{#xd1bBhZ%ETmc+*MpozMc> z1D<>Cu^I6gE*Em;?^Nowf6bbOFP%PhYN>dSB!}gH8tkJ`V6)3b`2YYQ07*naR3+tD zVx3@QzynLJF^a$*NS=MoEd=Iz4FMZs5CmcK(E*@BnJ~t2!?`y1*XiENVhv<@v)hsy zM#!)OWUDS4bS7mXnQ;tb&eHJ?D=>a~(w{VLEAB6S`hgLE#l6*bc~xfSU-3R-=ys-y zN^XY7ABI50e`Eb&1a$0^hEz@3Y@4Ro95?Ad`FPvIn@o1v`p+cGf^dh6>G1z z~p?z!ha-|wzm?k1hQ1Cv*zpb7%3 z5VIxvcX9OUGA0~>BvJzwGer%Fu7hjBuWOK1<>=X@bJ~3>wBQ*Iaw@{6oh6>7qB=KY zAViU)6HKv}O|;>M!rE)7&*d>ZQ@=2%W*hFtTl;aqQSJt>Lyz zA2A5XN$cTz9=>$^=*A1ZZue)b!SLFVwe``F)pf#h(c_{9%!uf$31CtK`WSvYM1N5@ zIEO}bCz>h>&ba8p8nWZubWs7rAuhj0^TI*bt7%5z9@d%~q2k=v;Hu3$wAznV?*JF2G{KD)*AzA%9Ae)qeW)L5$_FS{5t z+7f3p?a>SzvUnOP1Bd$M1-K?PdCk7be=qxnfw%_X4i5I$tvXb%F#{SnkQI=B`CkvV z2j+oA^hvisC;tAp`Brflo@ND?b5B0`$%@!3Sbdxlk#=XSP<|f9|8mlaUg-CFyQ@SD zQV%cOl=WSBD*Rx{Lbee6AeI1x->3l%(H?}0?x?ZV5Rg;j1a+yaj}P8_;H<6%<^tmo zW72zJ4AgsRzvlmZ6HU9=6+YXV*v8mwgg~lZW`5% z`-dzbLEvbXono2l-76qLhA%+Kcro$h;$(=$|J5Lhe?e(twYw^ozaLEq$R40I7r?vI zEGzc2-s;LOL%U|{(c~Wu#myM5YC&`{L&D4{usw^RJBf1f+53>K#$n-cjbW79J6haw z^#Mf|Q`MVms*7XSIr6LbK{R$=vqTWaMVPf>uvrvAmS(H)gnMS=;yAb55)I#WSKu#y z*$W4$fA?$_{z_hy|0*j&hA(1A_3oOdZL!>_riCJ>H2}1BpRcvq@c0W6^Fo|^r2n^w?^kA9 ztXqGPXtEg#bz=M@pyXP&`$DhN{SQoF`r%9GPhAiKHBVmdKe`a;AHMU-Y&81G%1X5r z5G%01dBrP75FExpt>Da(=BNM(^8x~|fH9RbpJZw6h1JDt7 z*kAs@MeY-llNLq5GN}!+jal$e_KF>t(K(q{AU*;8PSWlt0GB{$zbg+;^3j7Yyl`G| z0vy3uO*i&)OWfmnk4o^RL3~+vz`eTx^wzZ!o8=8-TdL`zR62_dNCWKd8h~Hj560BTxVnaK~;)&@B3m zn7{aTAs3te_ic;IS0B{H4JUPcRTB2vW;9Yu0T4@}NkWWH(fcziHDht1VttxrM4jJI zCw`rpuB!NLPH#H{m`t_s6Oz>glp8hr~_k0H(@%s!P zz5d{Xoslfbx?`C>`VhG5uDjAxr%qk0lGfkEarkXwIb7=XdIc&Uqd$C~7yvto{>&$^ zps)=Su*F3hyadr|6g~(P!%?ue;RXk<+L>LuPX7KZ#itOR@bAy|`LChRDB>GUQj{@mE$@|MB?KLLV;Nx87Hf zV#f#Wk(W;35LY|dQ9wXjFa;G8wCej8T!DPG$^yVmbyVpqt{>KY&GYwJ*h4E;XZa4p zei!Kc2i~F8(7WRh;k|nCGz{V&`%&`a(I`B#v2oY3_ZO@`KB;0A?zGDNS2WnavbwTC z81CO)Nji^@@C+<)!U^bY69}*izV^{6Gdhi&O;M`Vafv7m_rOb&8g2qMhNFhZPU%$@ zl8l?AL$|rGV{(x%+St0Sr;r@RJx4`nEEFKl#=FIeK9l z-$7^pCEov27VZnrJ4IiW$Zwl{qx0cwKKA0u5?X8BnU<|n)NoS_;I^s&XWMH` zsA}EKsyY9h*MCg2nq~l0+^a;#R!nw-kv>dp)K1}UlYx9Mh z&0lj>Dr&PiNkcX&H#b0Dv@_HI4GHEjft1kaOs*ruv3IPT<~QvYd^;t8R_RYxdB;1_ zq7`ZBqmuAOMad>90LK71I5@7>eDYO$aaT?7X<#XJz|uSMsfH71$PeQlq4WUE$>aeuI2;{zu0gx!iag=509Z4%(p-w^| z-uS9h??hS6E54^aFVJQ==00O>8yEq7t@T&p3TP|*LdQr|_A!zM1e69a?vwS|+5`<< z)FbQTW^c?v`Bx>0`<7e{ki4thZm&72oH@h^jG-XnDgGPozO2#ebqEp{CqbIT@oC== zPj$WU>_x1DwWxU2!04fk6v<6aaAq-Qa5jA%~C6*}LzX@_T!u6QdiXXL?UDg4~RO%oM~ah?nn|b7zk?+6VL}TijjY7a#C2p-T*T38Ao{zen z|42yhe~J_T^ouXPxa|F}2jGuc-lLCt1yP(Yvgq(xRu;OzD$cnk1SuJQ3fI69IqgXH zhy3f!unnXZCvcn9;C^)f+hgp$f;>@0tvK0wlI}E`q0`h9;R*?0h6(`DdpV;4 zFoiqF`+df?A7Lb8o#Hn<8gN?}0B)AV%^$W$87qLy+FwWfUrCF=%b4z4%sV(VB#8fv zvl@eH9_g5X>y>Lj(3>k{m#2;L9wP{?xi*ef^CA93h|Bql=V7>?P^IOWG_LfdtU+1z9Jqwu8jmUc2|+5fNq^-J^cUa`$nB zfTTROIUb*ztspwfxPO^2%T_~z5hNHgA`lTPFag23;8Tbbna`$ZL0Vmy2p)h%s3HV( zBLq5h!YT)12&9GuXn#%Gf6;EN>$AeJ0cb%c$&z_3vQ|wgGvOU3EMSt(%Qi6sI>b8+ zac6G`>zYTrQ^rl*bajPd0K~}C+JBGu?f9#+D(Ys;{9{tBmsbhH7cukeR;=KO2S2xw$0(RIKgom%|S{ z7KbGsJhA@arp3rHfG(baeU<_0ciOXFuRV_Y-WY@^gcv+!nLZ&9puKmX>9<&A>`E9! z=}M0|AT1(9;lH9_Asq<0ix?@-QDXp7B_Y6y(IcUXJWUm-*CboY$}82@2x#^oY}30p zJ^UVM_YfNS7^#&_dljfVL~3LSGrST(=qV&JVH9hOQQhJNXsU3Q`pjGLUwGk#J>uGX zz&jq@76$OtQ%|)Cjg1%Oyd_JeR?D@I00277q+3j=uY!3V?D)#S*0 zUi7p=D>fAOu{_)P{0lVu|HKsNn$6)y0OMR-wb|fsgzjFd-0ADC3pv~&9pR1|CS}uy=9|A$dEK(k@TpTNbEYl|r0^>K|OtJrLGDqaa zC0GmUlbN zG~kQG56oBx0{>chjSv@r-xwWS#$-hjGGibJW((C?3G#$@qF6TJeSOG*uyKFlTzSpb zTyoUDxY3>>9agtfv9SR6x6h1Zt z?2yxNREM?+p!<*C4ZuQL6JLeEhQW5f>t`|6@xO9nwk z1R9RqLWOWEDIq0bw5c%ONc@jj9fq-gV*ba>4`6u^rd+eMx6HcB^a+H(lTSX0(8@@V z-+q&cVJ|`F!w?nGoPWSUG=E%N>VZJ$Osat}&rlJJROj-xX~7)cwuGx||7BwsHP@&K z-&6pebG0gfS8G(2vt+m2{oo}4qW}0sd)_UlGXj9Nk{kkL;bmx$Ise)NI$MDqfy##S z7}j%EeTMfcge%+Nw2Tn6D)tCm`!^dAAm5d%hXqxJ`?P7uQ1x0}tT5EJVMFRA-^>Wu z2wDsc;_(Hi9Me=2bpHb4XH=kr1c19I;vO)al@~6Ye-BNyzm=B99~TH{IPm_HCwGx# zx00Z}OC+c~K}D4KFRwsi$AD9qrl9)0&RO<(@XI!JaMz;G+{AZoQm5B!+|hJ^QX6#U ze)gaVne!)+UmYsPY^65N-5$pv)BtVR1A0gRYY77uPe1*%nY;IDdgr5Ck^uPN)@MHR znHT~(fyVz7FuYF^fJ@Cs?!AEEBfh!NofNaL`EvF2%`Mm4?^OdJIjOgr5AVAwF75t! zkUdWqWiT}T2_Uv{3yv`KFbQOu<@x{JZu#FPe*ErmxRe0)0rHb~$e13h2~EQ&*dabZ z&K_y8)*pd}k3L{M|9)I;@YkRvq$Jh&+Z1z|H}>0*fJ5|s_v#TafhJmzrrDDoKr8q> zIDGh5@4nl4GVQzNoepnUWVpIA91tQn<6Y4GZvvdILKrztC_>j~>?Jz|p5i(Z?<5-$Y_3we__g;JL{O((C zy|q*Ut^@W@QWl*;Mi}AkAdK<|szN33+AL=$1hs`i&R06hpa(6}Cl~^)BuUW@HxncP;c$!!OY}Iyr%o%x2?s`iZmFTc84~PFdZzFjm{mv! zhKHOOB1z;Y2`Aq+QBQNvfik)WoYpll9Ai5C7`^C(e9_Dt1TJ7yG6h0wK`Xbi;P|c(Y=a8do?n8Cmm2-F5vF>ZGH+!pg z-#yJURKIOb0?^~-0Hn=Y2{37>P=-M!QoKccfd3DsbcH3%VA7ZkmmYuu)F(QvVk`m? z7y-ZrptS-q1P0i%ytZSh3;Q-nqgQX5q^7dAZ;o}& z#xqRas2PML0T+>IuX!Xqt3x$Afl#}uLRi2JF@Rf=0OSIE;lc$%0PNsHBTsxu05ub+ z3AcG&PL1&BeD}IpJLX3h#W)=IXl`-&duMw`5U|z9ub6~x*6m^-$^cfzdw`t*(rgao zH`R-w6y^MPj)CLh0(Mb|G%H4ME9s$iYG-xtV7{W=yd8)9={ zcrAoxa~+|2zos^xC{+LnU|yBbK8qVGh`|k{8!CLAdaLw!I>;`1gIU?GW}1%xP;9tF zat}^CKI`ssuu+-4f(4K$=VD_MA98=C4TKrSf zIJK;IjKx3nbHtMjPF~>8jemE5n`g@(;ro4dG(Nv`@4XMn1F(L;H4k29e1afAjKEnr z8clEm{`>j3SZ9psE8tYDrQ{VM$ZalO;EbNS!U!i@w#5I+iJCwQ8UW9PP=?=*W)DJ9 zG$7nH{B?K?%I=}FMIdnWVUetm65PVY8nt5MSOj!ng|16H0Ev$0C3RTn$99lsZZ6PaWH|3e;0B| z995v(OEoAL83r@m&GVa*0vKc0y=6acUYE*&s_<71`NR3RUxB%_WTJXj8V+A3Sy zD=T+SsE0e2X$b+(J9+YC$)uWb)Na3u`1mqn_>&U(6%#Tl?=B;S3?#gs6Ps|j zF^eTMCXpOQ!MVuPeYi0VUKJ3UUZoFG`Kyy-0ta)&0QM(6)pRp49(sRZWO|xKb(KGY zxF({M=~gTmN9ZpR|FsX>U+MDr69obA0h3lWeC@T@Mu`7YVOk&%a#m7Kt}*R04rS3V`6`D6$4dUDz1FKDbfzKvMl;dir`T0um+wD~4l-xFzu( z?Kx|~^NzrgwI14UwOamL0C05r^yz&sbfC)R$tM&7&ph+Y9M8ZZ{viY0#H$DjjC^`s zYyk<~Z9*?RmuFDzaq7HZ!BB|-B;lfC0P2}3XT+~Ol~D=(xKb*r%XUY7Y?tNa1_Mwh z%Pa6~q*o+b9F*k0;ury9fnzt^NT-j09rG?XMAJ7^Sg(F{>StM$@nCh@Ex4V}WSdqy zL+?i4rgpN>za^X#)IfgA8a5#rOn7~9NPVPBB4WOU^X7VeVPBahXa8RJ33^QQ(FUW5 zVleE~LgyKQh=I4uk&*N9eK8CStscmxc?P*c|%G_PR{#)7XkE!2DE zDYYsh5mLW8zP6UIgO*qi(1x#L=C4H9v9y+jRDt?g>S049>Kmfz8!BvXcJPP!2^*cN zoihiZaIrY;YxxZ6C;-81l1-2gtR=<~Rfl~r@4RpvQr;c|Fo+&}*4v-e_X+Ugh`!?g z^I%Q*5ktTn06^u`33HkgMx`BY`BAX@r6)eOoc#0lQ2j(-Y2~dc1U)g^9=fZD-xuLQ zgfSxlbAp95{g!jrCC^k>hlA4mb$zdMyB3@xb_Ej=F^FtT_7$#zsqL0RkxC5^DWL|y zWZI|z2~sT!ns@=5i&x;J1fH$lG{Q0OhG_bR3hNd3Cq$*z*S_|Qx&{eQUZewi?1Y6Ve$` zDL@@ytuh8APy5Bn4gLn(^Un+q71!A5V z%N9{f0J3kXGG+$K7Ksf&mbUkvZixXPDtQ;roawZxNk`GjOa#{)erDB%yl^BK5_wlJ zX}4qJnvP5Am5pi-G60ulfP1%3*bl^}Y|GC89-TTckf9_Z+K-la@@WMZwuuJf1=_)k zmsOzhWm-buS_pjeo8R>Q{LlYgxq0bw=|g=O0+|ei?2q9shB!Oe0xC9jD#Bx2V8B`S zwFKZ8fa--X>6pa~n&gc0a0bhR*x?d(s3LVS)rtIS%*E7UQvv8$VE{?gX5&3z03kh{ zsSsi~Qn6We7aQ9L#{l$Ein6ah-Ck4~yiw!vBu-}YsKpQwp)jvfm^}Mfw?`!B=K6lS zz%hAby=yYPzb<9Z5;tVX&kaEW9Nt9|7QgmP<%=)3W~}`pez{Cb2)rW%+*rGL^a*#6 z0LZC_$xAz@Y!rb*yX0^|$=3*j9_E8p<6#%)7LVKZ- zJ_bQElK5j2cYu_=tUlTRV+^2C1Kfu!)P#5Wl&cwlr-(lq^S9wXMkY}Q%z6TMHpax0 z+Z;E#DYZ!nG2%K8y z)I3R)s=WH#k+{l`Vbrb*QUUb5ngJkfm<<5D^|7rfHlYlFwpM}ETismOiML|w*=L{S z%^^ltqbhvcbZcbrtk){;nRPL)b-j7c@OlcJG2F%`QM}z6LyQx+7sG)BHP$GBBBts? z4Ji6(*#O8}0DvrKO?2h<-dxam`y{}(n5-c-*}~pXaZGpuswYN(@?=%DYYgrLGu#Ad zon=}=;2j_!Dl+FZdRmqe2ZumdS^mK_`J5PeLVz~>$2Bnkv*}4r5PFfPuFVKr6l%x9 zQ$4gk*Aa)>_G7q79eSQ6Gbv)rl5(NvV_?TFVJrgfqxWKd3%#&>61|AfW)W+3q~H41 zxAs?A*L84rYeeuAB8PMiAmBbg98mmFRuKV)bBLZ`p&aKBW6rP$5`p&hHpfNRHp{P% z$*Zo~7Ui1q*X&cC!~HD!sW@qhpgKp*pYd!A|FnhPOSS-UQGt*-U(2+Fz`H>JiZ)T7 z(JJT44o=IXRn9-!)S*d=RY%5N=eBV!N!E;wP+AOJ~3K~x+Ir>=^GQ-I@fz6Cfu1qOd>k zn)cxkzlj>n_w1mKQAy8G5x(kzWuck}%c~;}4(-XqG z<2n7G=wJA$2-t?)LR;aSNSl1u!>i}#dX3VS$93(SJnijPvami{56>ZzbA2ym^(?pX zUfk@AkFpQ6k~oZ>;JNotCaM3yFo0*CsfsUuc?ZFNCnxM~&fDi^^@s4=m(WvA11Kz| z0KvKciufJp)Q8yz>!WntPT6xVnCM5x3%6j)botz&5|)gSG{?-+BDxn+n)IXu*WVRn zxHX=?j;^x7{xG^+Uy;id7i_r(Kn=I$?jQspp6cSoiv_MD`8>c44_p$4&gLsD(Al;{ zIWk;>wOA*yd97muh1u8SYg8@dRi4CmN9Z`~EshU9ku!?hZ93$@S7{WbijVfHbkDsf*SjL^xAdo{iQVhY?-6Sh9o{`8Uc%er$iu1>W$uk8H{04fJ) zFadh`g6GOYDnGzEOO&bch^EFJaAc-&-RN-1)vvHT|M)@x;%WWapZ!^z9+r(Ri=Iex zSY%<(aRAC$E-c3xNZX9%!{qI^h=l<{ip)jL!f)4sIgDP|y%rdL5QZ`vxO<@s8MCQF zG{c~ylP;)m4mXf%jrksMyC_@~;;2U%uo(@1y~lJ*4B+51+^e`3ak|e8U^HSso`Ns} z-p_FX07FTAQ2nZ=-%z3Qn&g;5bGCo4XSuZhOIb*n3N#V{(?<87p#qdxYo!LTWD&qK zy9Bn|IcupQCM?qu0&fEWUmkThlp1kKrG?@O{W0BrR?zRWm0GBi<%Om4I=x?X4+(>m zM#%g+-vIXzyH;Om9_z|A*V*f4-B&XMM*}buaqS|WFQF$=ya%W?&)7TCZTru3)g@>j4x^!(knmq~FGCUt5#Qui*=7_#%RJ8Uup*u+ zOhMAXGA$wS&Ja+~F5;FHOW1oP0M_Wkg)9>j$YBWTSm@MD0-&tx!Nmb&UuUll&(Sn{ zUFDB+sRK&x5(LeG?hvBul7w7Y0mJ|TdP$<$bMB4{o}g1ilG@ zpU)XslOQezAUsy=J@x!}y3o-S=-as@fSLgaRfX?1Bxe+?5jm1NHA7mC+1qfL4iPq)>K*-sgQpN65931Vjul8{Us~P0Dk);9opF9L`>NMj+V@&7T?3>NeGudoclQ6&&Q;SxJ=HCMc?d)F7NvoL*SdOX@$nRHEMVA1dz%PZo zFX@fdx2Px9rgbl+c50`N&aji~3$ztp3$g8ra9F*&JJW#)3!J(;oixzc3RG4OL82MW zA&`RMZf)w&GhL08BtUSLGVdkL=A}IJ>!-R~-tPC7B>j1SKC=w8-6ABKPs z<;YPwx5!e=KzTZM8-k5p)3RMUcAJ*+;<{~Ihj};v>$=MA-;^hIp)zn++hlZ<-2op2 zsUPhj@f#+T$OXz8^gMQ%mJqlK0^fZ0Sy9speg7Idb=$1`AiAx}Loci$FXhPy=jH&= z1B)R*pbnG8Fr3stF>p}lj>zqtayGI68mhdgVUf?V@VTH+h;iSX+?v6$Ub z`je{LJvW_iB6<*#uw5G~T$PS@$t)+YW%F9#1KsQC3rRX}_RwJi0GB03;4bUed0Dx0 zmD@`V{W`J8LX&Y_uQDU9+6>1e0)YV;`34M`yfNule*WcoJ3Djut|QM80CJ1EBr+}2 z5(2`(7VAWWJG1e6i3EVLubtDe$ns7odGvb>ZW{Y<+=0nA%u~mwzC?jjkbj<-}uau4iHA)Kt2Z-C1BG*3EomuJT(C%waXclVM zz@k(E$rQ3kFaiO4)G97zYrqRnSLW^VSMUaYB?*$l{0+H($p9K4e!}+zxoJQ2&_gjj z+hGiNM@r1?1wDNMKH>j^B!Dyko{~jR?zTpccQ`I=ZSd9My2#YfUO2vhFL|7}5+Z)F z5tZXtmsF4gx%-+O03Q}-3vf@-DqHe*Y4JV~zsjmtopARJF#s`-+mrOtbxlrisl^w) z9Iq-Z#lF_gZ_gv&u9oHzjYJ_}n)*P)xnsR)6_xpPB?xwI9}M2^g_ijr83;V^zys~| zFif!2e}R|&GLzjxbNG?CZx(y#tX5+s;+r&T)cW?HD10>He)}SCbEUqE3e0by?&_Hv z?9>3Om5Ue(ksx@BR>2e(nGoJ{x+Ub-P(xY9aGZAL6F5D!~wPGp1na#rm>YtXgzOwB#1w zao8^U;(t!?q5;$1r_^gtvkKnj=av|NCL64;uMdi1KJfjh3>5bd4}1mNnqwMl&GEuR z|CWeiVT~bHMGbZzppQ_{5QSZ1Sgy^W9U;Wp0bY7+Esn7*@`dmhHbokiufGQOY_DSr z_#vi%?L%85gXr@CoRokPOHL^}0Cmj*j@x6-E};(W94gA`rzTZFIJ0G1Lcl@be6L4f zIWIxnZ)Fnq!6h+(0?f`)WihxrRweq& z-(Mbovi_TUyFBpt6TX=mz&)k{G|&^CHd}otq89!)#P15Yiwpd2t~OH2L!~)?^&cdUnP0#9 zOTFB`;?1M&2TT9b?|c66?&qI3JAm$7rcXEo(s6L8Xhn}s%Gvrb%e_fH_al)CO z*snGCG|gSJUE6bcv=&A)ZWRIFyCM0Xr`<9G&@0DrD~tT#GE~P}dyuI*Wk~_j|0TMZ zCJMy=`A~hVeYEbk zq~aodtKX0jtNLDaGRKbW^XAr0mRQSqXTXK$ZVb-qRee!hI>z69(|M` z@Dverm`=Bb-`^eR|Hr|Nj^_GnvX7etyKDm@XcMCt2|%Qhc;t*J65E?0LW%c4T9FY*iY9Dc zyJEn)-yjA%vBt0>TtHT{x||v$nl^V}BmntQlo(12<^yE#suZCr#zQHt4&B#nRrj7g z{j*EF!w^PvLjbIwyxe`nA#fBFi{t*G`8fMbHkJ3(D6qcuYC#@PxxcZ{25k>=vjkwHKnNi`SZfA^Rj?B!J#O z1nJw%cL{hGtx{B##Sd77`+LPKJNLpJcifOu+ihV0eO#r6-aKQHJ;AonM&$Wt-hW!I z8=C-m0U^O=(3a*_Ntd@THW3{T$JYZEvh-W)^$@b)qH-$jPF#+iLfnoK=&VW7Jycqo zV;ll&%G|GF${Y&Hfrd2}So^JRsTX+SQv?KqkcA*aGaBf}#tJ z|I_iq2oUvQ0AdDOnRL#mmO8wE1wzQVNXY#+J3%n`-@f_H8^Yh)!T>6!KJ_|r&N52O zLgKZ>*|hO$5>fQ_TIoc(%S;YJ+VsJ2FPomPPmn(}%t7R?JC(#Kb>hzXtpQ6(yME0N zb?R<~N3Yoi_aIf!a?I>9%dMQ0h+?P#6MO@(SO($OFcD^lR@c891lv2*Ik~3ZWyZ%I z0%@9tMHHQ26zxcw5y48Kz92G*{K6MEk%#UH9CW?^djyH_SLmv}e)KEcuk&=^&e<=F zfm{UnmGAU^NdhnctpAj>CVgZzlv#OK)y_$1PGizU-jnm@UCf|KRikEyC&1THOcT9#TzKa z6DVU-EI{)Fq>C|m63J4Gpy{M+Z#|THDK>D`ZJahZ4c<${H{n#o4z8+0w>%-OPU_A2 zP0EVo<>(^@Aay|Av~VQb2u=zwcyjTF-ZTn_dsNl*hGVB16a1P zhysl}dw_TRw>;-_$$TX_wspMq=yMOK+GY9#Kw#9GtftlUo-E5B#o~Wg4g<&;0xV$y z1*=RoVZ3aqd-eSKQ}4fT<75G;g-9=qHT-qG{UTr4`m<}oeMb+C3LvK*6YaGIAzi;# zCl0h6ID65KlDbW;4;aU+fQ{}N1j)sRl0?4pHoy=xL2WZwGD$CuLN?T!wkoMugJJ!Gtt2sYyeJ66fi!83b^EUKDil zLj93~q--8)6sZCFN421?InS)-7)Z#g@YlAw;LHjWUf@$L|LzP`AZwrUm_ij!=aa+T zpm%rBIdW$V_b{Ec~H?K)F3zXHwXS@pRT77cGCt!W%<6(~%+oV{BFo zX?54?(1fFM{4s(T&5L-2c#F+_mWl}Xw3%^RbgT1v@6>~);}bc9^EM);USATjR&;$?Z`0jl*OlR-}a#9sU(i%!ZhG1_c zDJW>_21(~S<__vKW`bU1u$A~__cHo0@0jPI&aEbmK)^6)UxowQcziM>7`*AFRHens zeDBzisdwzo`J<~ZwjTS|4U1Dec$q$85TF-tz4ZKN-SP9HaNA z3MvB=Fnkkkfs4{SZAcpG7U}Am5xQ_vXh{;rSthtFuT-1aDr8r2<*`#ySA#k{YEs8X z{S=1=Aby6>Bk;TH$6gPK+Ampcn~t@GD|?rr0mS|pQUq`8ZF@&XWH^Q(a5(ts zNM-o3&8{qt-K$$!eTfb%3A`>zGIJX&K^g;|BqVKRA7QcYQES?@!0mhq5&9WN8Xs&>hGL2ploTF{zOB{B9bsh}q$! z!^;LW52B24y1`qiyHaN*zH4(+dck=Z0p@$I>9|&nbq=jayeGb&7skM%FWhgD2Ygj@qz~Y!PsKc-L za7NvT&>OTlW-kEn-2lo-0`6GAf@|DrOBe3;fAiEfY;kbYSF;C8711_f*slQNs{w*9DR8=-6J0=g4Jftnb^Xazb=2R)qi0+FL+3joZ# zKR(R>L2oZ&SC1`VSQHcyHh-2+HqlyPB$^(R{CWgcRcJ~D zwgFar;%3e3F0c*c-mW)gCE;kEw7XURiFvEj3EHctwG0&LNBUW&k0%7C$;MiqrC*== z)mL`C*#m?5+~0+`hTzZ?lp*vVxC_yPFwI)hhF6AR!l|p1kZxZRF|$$KUea;H477KB zq`Pj6z+HCp{WUp`(EF4NxhA{X9BEaKZGQ52>of8nb;Go5pF#XYezs2U00l=_R zc{NUghkHqQr%P@xHPWHCw|S zcn^khoKJvsJP4ol3$t?MO>_JL@z2tu}1jiY;sur_l7)Y2BJ%<3u+h4c3BSJjbL<{#dD@C!%(j!G0de3?E55O@xR3QnBZ$cp0M zXWqXCi+Z!D`OAqcJlcC*)sfZf!@xkP^B4tlCC zTT5s!Re(PTUTeL{>YJBSBmn#Z@@X5ru$QL!okd#S7o{D{ugmmtfxyQ0c6V*o#@T23 z=yW>%%wRkUhLb71KXd;r0uX*G;H!)JLq}shzA)1ryYs7{VYs#rKkIW7_YH4_->$b` zIQ_`zB?&-(Dz9?t?dkOiwJ-6W-k*^GXhe=k1?q{O9YvwnNs@UKgl{Ik|N4d9Y;f}A z$s2Y6YQ8`eb6XS362YDPiv%fnEehioAiAMo=oV7oK*llCv(?E~#t48bM6lO}jD_nQqSfP+O| zq}~*DO76jVK3$!pyZ3G77mmJBy^#QX)v!z-aR}tQ*~YY}K08ULC-baa9Z#n1$#ja8 znwg!76{BDPns5WbOM9f*9U5KL{4Ues8O541uCtQ*N{;Z<@K`u6QrnlOe7k1FR1`et zKDGen5Q-T{9pGFZiD{N{H%38>1KlK2}W#heIYYYEsP2rQ6N`c#U#A9$&=J zcNzw8Itaov!1C`E1l=jt3Fy0q*v(e0!T|a>IwVYXpp&gdBEHweeKUrT=ZqpPTZGiY zg(2%ZhaxwvSfc;}wgws<@FimaR0zB+L!)>Of94)6H9{MWs(67~;Z-pLAbtKId?fvG zb>17y-t;EbnFof|tAFfAs)R>zC^YX3_3j{W+>E||Q)BkBVFCRHS zo_Lq@!rR3dFe&i6D2ScQz&b1N2>rBWtKUd^lx0ceDB^X54DvaGu8Ft> z0ZY0C3(c@?hHDe|mFJXQ)!^`5IS@B$amW^7--;0TwS`)BI34G8f|oN~cJYDQqjK=}j=@Cg{mH=viL!v}B@`t5w0|Hjd~`^W$EPyaNoYhLa? zVi2Ic+h<=od!(F}pB_!7pBqiH)$QTPlksngdaAkqNdENvbPnt#A-djQc*U{(Xn$~P zI(A*R9^H_aGNNqZbi)`R{zV=)HL8GffTIHwZnIg3N7+alRY>QbYu*YHfH@Gg{cTi$ zSNyPb_MyRGNA$+iZaUpoBjEnG=ycWw)9Li}PNy?$@d0fNEKUp(48>brk1Y{LMi2UR zfUe$I#0_t3L@z2eI<|A&1L3L>D5o}Y3xjZO_vj6-2aoy&T~-nUa0mP{W04bCeOovN zDPEu)gr*n}<7(n}qWq!uJpDr4DL>QcPBRec+^quL9=-hepdpYFDzL^8+ zZwvqfn;a9)cDudr6{ECj%{yPr#xq}Bb|XL{2(YYTsSkaijzO?ue!j${N}~e6U80F` zMF?K^D5jTmG93nD6oRKb24M^aB!H;t!s^vEA^^Lg8rkop61dH7=878Ea%7J{)RSL8 zj=C`S@v>@-Xp9R;EE*w5y&%t9WpwD#mbd-qRe0z-Vb%ICZ&cs?@%^o@-}KN6FSSe` zAOtQ9hsT~j_10(456A!A_SXg^`UG#S2>G{E%gAr_9f~|&*wQ(3OZ0@ z4_}!Twl1n^fL%&Ge;d0*hmo^>rz7Lwd8Fm%{Fy@W-+FrA7yz%9;>&!o-#_$YEbB)J z67mGrb;2xp-pUHo8=1z~^1G-qiqNYdol{`1kYNIaIIsF<9XX&XHsBlrH#RJ-N0LuX z$YKU!vNGK04m-9gupt=RcKO!t>o!WcBxWFc0EQMx7B-_0FDp0>@l|c}33KYVXUCGV z^Di>L7t$cpethCZ?q4#128d)cUm7V+3toW7D6tBOcx@U8kdIQA%3A%s5 zCJ6uO4%Q3vMJVAw&8H< z?e31M=^*>;&eq_|m(IPp92&eXR6lf?!LdFwJg37ijVHt38V?5jo!xDEeF%$TwOm^r z9a>(YM7A@0&{r4^@ng0r>(%%0%6fcPK$vNNx}o{AqLxawBJ2bp zu?@S2y^a+=Ovx5hq&XCU*f_VS95G9rU5Bbg<@ICC9$svmp5KCQKpYorfXUD}JilTZ zW(!q-=;tgNG5+jIa`*3^z5LT3-FY~9qt$wd3lv(W4;2JnzH(*thp(M~IIX;ITp3qi z8IJN#U6|xuiT;Bet3Tp?Mvp&ZGOf)J6+&VCGa@#MbvR-T3tZ6Z7D&7CK;N1Jh0C+*Aim5f1gOPu>lXS0yeiC_QvQi*cmwb8E z#TiIM%NDk9bYL*9QoI1&2!I4j1@jam5P+4hO$L@3K+}+(2vnIy=+vX~nqwets#t{5 zQ3GnJrh#WdS}z4-1w$mT`x#wBli zdgX;}Zoe(u*j{A$bIT9_ciN{frbou@@(VgF|P)R z4+a3U7XkF0yn<&(l-YxST7wD6q1;YQqZ)E725hvGH)s zKHV6NCcizM7GIC!)yKTMy%l%Y^7q36fxrIizYa&!;iqx%d17~GE85xKLWSG$Mq@lZ z!9C6KH@|u@__||I(~J1^yFUqk?9VQ_W5YLa&Pf2;JEC^Obl_5rlQyY``)y$ysjb+z zWaE-VAZx!C`KY7%nhJoL3^la83A7>cU#|2F`7!Y)I>p%kf5o}(Cv>Ric_;N_w0}2#6*8_YhWO;Owom1VdY6G;GBHOpL59t?oWjz3n(qX$3Y8m%gGvgBIQ8{ z)vNUcWUpuwKFCG{ssU_blXvKao$~Cld3*S!AlsStdhN&GDxQ7)XmIp3;!mAKRt7umd(+wUv9rU`Z;sOGla~h52+O~>Jt@6mN|fIM z=@SW2^ZYgDFC(y$S|6F*9a(R#4#aKh8JdnlnAI=@>&dNSSI0y>bWG}$Wc634WgVBg zd3CeyF^gjb`qVUEiT<5>>*M~5%b%BH_$x+?Mq!ueuk^$OF2-T<3VOk57{hI`{_{qb z-ggF|ryM$TXgVJ6o=KA4KocwmyA!XRGErzd@OnLrffn%uvCQYgt05omBx;+G@z>y1Y8C2*0EqMLYK&1nTMonRB=`WJ7Xk@>8AS~TanqfNs>o2^kWxM z0eGfaC~(8nOHj0pkuX4vtXg+I5*5ckodidDbnB=1KmVWH`@Q>G_u>AGTVah;P0h>w z`wM~Ryyt>r(L=|_QT97k*8cBz(&FB&QP#gSD*VeV@w3f1{}fNZLP23^M|1Z@!V@uD zQ-L@v3r0H&Q$+ZpY;6q%s|Tm7ZMx*}(iyQ`Q6qfBePaSL>x&6k72L<(buPwg%)v*i z`gHO3>4CU?imZ$N(f!i_bTAwe@we@*A3o%*B=J@fhi~P1aQU&v?!T?0Pag;a*bR46 zKbW7vDd%2t6tsoIL+xqd=T*E0_+-`h5_J`4fK0;vW7y`FdKv1g_$dm%aO@! z0TWg^f=SI+a8p?EL*gEGLjTk4IR9_LAUU7=o3DD_k(-{Mef7I6&)*va4tt0F;b{8U zaCh{D-O2Rct=&<7YdG}>lLR1tf7@Ub8u{XdFi3)L zibM?Jhn?6veE6^j-E8Agd#=+Vg3fJDJoWugvx~DVYV|JkI=xrAY`yMk7U3SOpdeXF z_$8)eDx|q)2}XoJwpoGzNaLjkLcM&lM^9M(wt?^iv1 zwwm2IKB|IA?BXPb3z9ap!GK@{LY5p)k&MYgX)*F9>9#kTT=oVdtbn^06hmM&%SWG| zX2Zu@MSkB;F8sgCc?0i1TG2jL>*=ST_Ag$%xU%&^dH3dU@THx}@X^aVJF8o}yZ+W- zfZmTjox;Fb=9`Gsw54MJ(7x*!*jYzlZBP5tvsb7Bk|;p|!-t0W4IhP95UaBpxnqlW zNe`vjd`|xd2gMMCbKtNs5D-hleKzDL8vuH5GviOQ{^asIt3#1ja49DHXh?8M;Ey-s zxbu1#w$EWBkoEtzrVm5{kR5==y8P;^)7NKZ>$Q#b;MW5DurquGrz2Q0p^s%L?Xu2U zEXLroY7c;u*dK_yN?ylmYx;fNZ(9SH&L()!Z~$DwTB9~-M1h${#suUjRMCU0n5+U4 z^d%$M9B<+V%E3s|f^b0QUM*-MFG6^*-6I6SF3a(No}clfa?Ak3HsJ)ey!rSjNVV;y z$!@FHIrh(c^CRW4u;_duNn+Bc#i*iT-pn1f!A7PgE@_QenQdUPYzh^tuC>hj4|uO9UMtT*@o- z79Ev_l&#yS^T`Ic)SFWm#H$Xx5&aeD)5L%FwzdV8iis$`zexb}^f5!=F?Gb?dBQb0 z{-irwUOC+HB6|PTjWsVxqBpzUh_=EUB0479k3%kKt`*K!@lBSP`ZH8lclgI#oj{UtAV=y6ZHN zk??J7|bySXlCEMJu?=}uQSf*D87%~%GcHg#o+X+WrkiU1_D z9NjWa+VisaKvj01=mnkQyGM^|oxvpo5GdcL^vENRFwNBa(uFIRerJ^Dzp*>a?%AGZ z(e7~Kjgc%<#`{ZV_LUI7Hc50MeswBNJ>36dH4a#_^HcA^D2G!uZV11GTI>wR?A(Z7 zxX6KRT%?dw56B~_yd;H?gAfGNoPVwOD_&vs!H|YH{gjij#s6DdTlKoV&ICugS8%I8 z$F}>_EzbPg|FzBW2gv|{O)?0g;*D<4|J!hvV8&7K@N|M@78M|7j8+pe+Ds4j{SHH@ zaA(>?q8iNw=XB0G&akJR9>5`M0b3G)*#QJ?wV*PoME$);xc;|>Bf~KPWon}$ z{n@eo@^E7{Ztuc~>HW-c8UB;8DWVY!pp4Pox~(``Uqh6+zl`UfnOB>|h3aqK@~` zwR|V;tI%AG0Q{5qFS%1hZhAkb5!apW&z|!(lE@6be}eU2&Ixfhs-h^)dcFP@&$z-f z1`j^?;QP{s-9WVTQyWDZ@@WIU%XGsKfOemu8NcXx zC-Y?XYgsY+tz9pAd~-C|*q*iyk0xzz3xn)v8e{E8=a)M-qBOI7EfK61W{nibpTfW* zzzGpZj`4#z;14*&xgnIqOW_NYTY*#+?Ja^g;Q;g!a*Ii54Mz0vp883~Il z;x(fSie-^7tk>Z^H0h@gZuS{rSce#9JG)-Cwe7VrR(2Q)T*Fr`YKQ-+)9e1n(P(rU zTfj|4{MFaJTl&BlfL;VnmYqF&c5^sP&&6@@T2aI=MN#LTV%F`+=YGTpXAB4Eul3QV z(7$N}pJ};r3s%2dF>6r4Po#Glbomj{>c>_aW*@?Fr8bE08C#${*V!cgtEjar1tb$F zUrk5t|qNGaJT)Z3uPF=D_sD9V*i*Kk0DbO?DOV1KjWQ zyw#OH>T>kcPTcw7WHj0S{O3P^+l_w>{6A0zpobDUxa{})=Vr6`M+gNQy`dF5$zK&)HqqpJZmaY6wva==NsqodUWnRbGhF#=m!EFiy5c6K{LG>+P~1GuEGHEJv5a02I|1CoV_) zt~P68JFE#FLbY}ldeGk5B+gq8uJ#wvL-1k>W>4Z=hG!SBqs9>tJ8iDb4gHBjhyl1~ zYA>8^@h)O7THz;-*rWe<6RiB;ZHnGMV9Y-qj|c+V@p>!0YISv0aW&uMolX&AFJ=F} z;m8Nh0I=MatYC7<_g8)%CbO0P>c($rsj%I_mY3nGIY8W6U1g0SSZ7H59YsnN@aD5E zYY6G!wwQ=u4J#(HAO;=`^NgRWf#gg)7n>IGt3lO}qGuTk$Z94TsflC|lGBf#po_)` zgbW%A63EG}=;?K!5=vqdk|xAg+)`0e%@`$MGE>Q7qXpiK1^c|(WV_g!&dxIfV&${r zYVChu;gLUH|5Wewk=Or+`JWtqqwvn0uBsngYPG)fzHxYa>5u5o%P-I4^T)jhuACh` zHb|l;1{b$}ZEHOK^!9L=Y>(5{4lc8!oH&kn_z~C#;z#7i(Z>R4(4I86C2K3S7iVfm z5&MGBX-l=PXx`;>tUQ(HM>wW#->%g|dkg77_yz7d34z*WUA6}Z_2k#$#*(Dg##FjO zJuB4NCqF{xpEE0Qw7u!2lc9BDy>-^oT!~*yR(j78@#n_`*p&^S`3(0lxB2%2X8>A8 zjBRxJ^5t{8yYru?-SHcxaFt|8FbL#zhkN8~N4(fIqA>+) z7uQHoxvp5~Fg2w80d;!&h5-nS;sF)nzYL7CS~AeNLlyCQA&JCBHKrh~nvCS#%^a(Q zuDMp>SfW6MK~OHm1QD5O@(|3R@0F6|7wohc4J z8HO{|uENIn71c+?uO^{I=_t(755cL`7Rm5~Gws#JAcYZkL*&}Cx$EGBxB-N(AwHXu z&NWSA*Y&1@vEe`617RDz>dsP#Xs8?e6)GVp!%RPV{t?rYWdKZ)z*|}G zdvU*Ws#p17`ru!;c&}GDRIYz1r*kRZ)!R8ykmyzZ#^$ zINkL&uVC{CVz0ZV2q=OU{X86np|PgTCBAJx*7#lLAtxz#Tu3)Ye(o)=L+}% z-{8cdg9!v2;_5^Fs05Ug6R@tT@nlF;D{H4)>{MAw`%lCyR9TZaRs`f6hR`CS(2VKL z^L)oEFj9?n=eTtqT`RiLA0h$vdr@!dZB;v8`%(?|;9(nRo`%YQ?Ron$?{P)CigENge+2Y(Jhteg}3U7k%a zqw%G~8`4O6tO?)z`s-_^`>Ti$MIyeKgt`yvwW>MSUQ&SS3LyN5O`*O5n*ikDL`FQq ztxd##ItV(tjn#GU(8eJziK88ygkC##?BoaHP5Mh?06=Uu7!1a1YrU7ExPQm$>g=v* zHoZT~i^F5|A_1z0#bmv;0xw|FM1UuroOz_fV-(bEu2SN;08M3T1B?7CN>+xb)DVOM zWC438p$QRcCcqVz26OpNAP{;u`WETS^jP=*i+e=u7F1OAQ+f`eyT+%ajFoic6%qll zT~Q1055VSE%(e-FvWf4&CTq@JX-`{Mx?z?)5r@4bh>~ATx<5br*7Wbr67}Um$B)le zt+h9Iy>RzL>*m&bA&52us-p*$_` zL#rh zy?J~$W4XO7s}5r)WLU8DwiC>Qh|OJ~8buorm0|`=i~$r78NA;bOHC@Pt&Dc+j3w@i z`$`;>`NbMv6mGb{8H|B5uF+V6G5I=|Iv_iMR06Xc+nvTgOz$^mBFdRMVfE^HPTa)e zNaO3_%;9D~$1*J2Z;8>eVDZtk?09(#m7v-s9sK1|gRouFCl%OoaJjp5~L!c=%!M zr@kKh74T$*5?qO!Nr+42+rZ`oc*Hd^WQ(DdWY0DzHzJ;%NA zbQhCnyWM%P-&=V?dUTrZ;KjA+oj=b=ZAkfK@j;^t4-xr42_Z~NMi~qz#YoT_{S;bWIWQ1Em-^vY-w|>Tf{?q@)kWBsvk_I%HNauL}*jBOfP>HINXLu5VAQ|tGq8jv~SwHQx z9$xA7?@i+FU4hr#jePu$J?|3R$26VmoZf98BT(GqJCmJ1m}cc~ZSCyzcjyEd((^|U zy-ni5qi;&DKV_~+4vi~(w{-nnZDiy|y8wTKJ1r$*G@KCO+rQfjh^7{3FG8oB4cDnx zr+u`E@RetJKYL>YYV*41*F1h_3s4=pN0mqg&@*nALp1VWLVg zS~2kko0pjHKQYfhEdE}1rFWX`Ct)l6eiXM~fcP&zM5L(?qTzpO5irulIa0%kSu~962!`mf;XCxpPIa1F`Lg3EP?^G6)V|e01x;AJ@G;JpchkMU@i8 z)Gl=LwucoMk;*_LlYs;Q{@ss=SY@SFK*8LajEGWsmRB7T=3 zP$w?zeD5W^C(lGlXWR)RO_v%oKVjVSx^y3BvJd^}0loZRz4A)*FCThn_Kf#T_3P(; zJsy#G^}lBpn^+8fwCLJCT#hr(mA#6D2c zwjmMIbJT*|+s1Jz#4eC{<9XK`R9%cf$>CnK@<;9H(|=gDj_lIU{q?+_9XR(ID&1$@CTC=QJ-+w8q3#401p$iBvsKKHpC~`0((W*@mUbo5jzAH0B1F- zrDm@awWDiz>GBqpS43}@Tu*L2yAi%4cy6c}0MCK}Xsq8tgK1dFdVcPQz{z&L{a}J5 z#u(h`u;I#v?q7Kgm8_Nk03ZNKL_t(*lu1};f(0kN@z!NxU|jKXbpQ4BRYr%pCWz6pRNCk7Ot1$$rTXdv^U>o zk&a&Cb^0q_AJ<9KvOoY2KYWSsYM1~~%t%>cHxz5y7|0-T+?WFRU`Rl_THmf-LwVL+ zQ-nWvNbPnH_2`efZT47=PujWhe|i$@IUK>8#2`%N$*>QY1NZ7Z#DMry@1Z9qJA})4XMKTMjHb%qw#f>oNe~k%LlQLKzn^8E+OLdU^VMLHX49yNo{GU2Ulrtt9|27ECeeTUl|iMZ$S5`m)GSc z2@#!CASB>eH|loPNqO$3s5b7WeyIdX750WX?%sg!01@a9Lknixqf4uLt!PDDqKQ57vutx?O9ZNU*$0t=QP1<+8||cNILm{e$9gYm8AQW;+Wp6A zeivR)ca~*(=RBSLOZwe^mh#Ob#_Tm=XB%C3S7EOi51&BvQzWq*X3k|3`CTjq;3Wx7 z)4_qI+T6r=8cjrRo_XrEX%Gix?Ku)|SdRNLBTvdSgsvXk41Xeg>-Dv-_51qVno$fv zOhZh8%{9DxlCG(^$;YtC&NLBwUKRXb>?083e@;)JV#f8ZcwH&WMs0lOa!X zrHD~J1#<6HaYHV>40$sM-gpPYjaoRwdUH62d=rdKyzI(L!(%qk2DjS9|OJ)9Oo=lnFg1=Ve4ft%oFB^cf7dhd2!0vK-j+{ ziTeMBA;hxN?h{U_KerxGE{neRG7Cz-lIH0vVNh;?)tlqi^wg?%_kaP&>CijH%&Ob7 zpvlU~F}-+s0{@FU;%Mc_yj`uQv%Hg)#hrlr;YmJwFfWRu!%>PsZyryG0XboUPO4sB zK~~Y@r)7W_AI?z_zJ~Nl_FAHChUhPN+GY$G$N^eN(Fk9?aTA<_#&ED#%S#$G;^!-s z)C6Jx%PFXuIJRg2udg+S}T^jC#iepw+&&wz^h!R{DRty1M@VFmK^MGf7G9 z@=Hk{DhBY-Ll5PbE?wFrD(Z{OgFa4Ac=G6xW1qn%V0CNzoVUF-px4B)#*e-JI^1@J zi32P)TA>QeS${s34qQ2i=!m8k{$TORRu>fJ7N>iJjBLflr zwLA!CJVPtemOkO2nQa)bh-%hOFo;cI1}WlW>UYAT7a{IWLgWNLr(BOPWm6s(5=Jh$ zD{7^W`yor=1<@pG&xXu+{Bb)-{>yq6g)hH+W$Wz!ad^`IBd_)3lU}fU=1ga8wW>DD zYiMI9s2*$%fB^P$jQBI;Aul2y@;Ph-0fN{8{GcGV6vvcf zTNWh_qNElnHpiZC-+8E8b5(x7wd*!pF>F&7Lw1vO?mcy;efHUBpZ#CcUVCk<^OeDP zSj3N`h3LM@q^e6yykCKU!n zRl3JG)QtDB2Q=tHsXzb-%2B8S*eW_BgutEvjCPvl;3EM-KVEPE&+!q0Z^I=Ny^C<$ zk`s{#_c+Bv01xx;KV!>zHRJ(5p znP!|vXWIqX__AZoXN%L$kZHI3RN{zrh#h1k*JpKKeLse6Q_lfse$=u3MoLF{xT+vk z(uwM%5|#AA&Z0CnKlY*k^?L{4V><`7U?$H1bby(}aQ7tMD;bctA5gL1``qWA2<7d) z^E^2l3sZMSKo5M>AOc7MoW+nwR4YP#k+w@@pM$;KjIj6+cL+7HE*o@dH=C`)cDwa% zrCNTKF2J|XoH_H6f!`N-s0cvRELdaalPyAt|1@8zPdn`bVF0FIU1|*e#iU=(AK^ZE zON%<=zKJQEwO1KHQEb6QAONTYGn{Rh!H347izovwj4%vgkdZsvYU;CB#FR)NL3^H$ z7`ncIwh;n;!KwtHo{o4M#zOB1+-RKaOC}&D zQW=Gg9N^HdAOSKD^JnCFHY_owAdjaQEfK>X4UiQ{Cy%UJK-)yr=R7M7&RLd<^d^*Z z6|SH%U=b)1q@*}vzvr|C`7cnObA>^L>Do%8e6>WA?9vC(fC3&)a(_6ekA~wiA@S!()&9B%aYFEAV-L>(f7dJVHH-TH?L^Lp2I<9}o&ief%BiHzpHf zLQJ&$TX8JFH1aWNRIb3Kg(rhu+6wge;3ks0>#^gO;|Etin39JofLp(x5}dM~ZX^|+ zU4dXXxVOeftMkYe@~%;{xbbA&+qbg)t&OaAuos5FW~-H*IkVxCJFY$ckl$ z1veQpK;y?S81o4LcE$0r;0M67aMkrA`l?A^l$~4aeoR4Xv$1gnJOw{H0x_Bt=M4;M zqj4Cq>V+Wpx+4`~%+c7?KV3i)x2Y0vAy#f+D_1eUc#0H<%O{{uStO|8rcxFTR-qLlzlk*$GEN(`GWjO0xa1c*W*Ux9-%Cb>{+mx}dvvCu$e zKsqSsqO7vSo8zfSlMcOmJe-^vPn;UHxHd+gpVC!1=;O46U^N8PV^+j{TydFG4=x|T zm#aen!a;}1hbu01kZj+zr@JR*WNq{pM`~-Y@uC{~|BOni&-{ZaRv$ZUy zAvynSAqZ@3Ns9b(dP93Rhy`F@e~I@TIjP6a7T`zpCn%}u+l2R8T3#A5xyRcOg6}ok z%^!>lg^gc*>M2Fmk92wH2*62gT)A?k4-sIf>}&^TiQ8E2U*%&ySt!lw@7%bFXmx;y zzDxXowQOAM!w29Dn9w776`62m^v54h~U2(y6ITat*4=dN^miX`{6~r8? z|8u{3x+13l)j$~cv9D=B!+O{axXWjI(Le8x*5i@;Ji{hx!&AiwwZmt?34<6Y&IUf% zCf1eC`%DQ7cbk3CBezN>NEiwkHZ-_0ZJP*57#ZcK+NJZ1PDGGrJ*I)OqB0dB0EHYg zGZT!pfDJ)-&ZbOAInJHM;5lPZW^O=SgCS$wOw0#@pr6Cs)d>d@<>9sobb%pQe(qZ->VYLcgm=_RVZzjjW)L4@>9 zm+z+u^+0tY>X@1~Z$Tdfg!*S+LtyI8qrw2JI==)Wz7f~l2Df+1bAtf*#i(|!20izD zIGgePluU|r^;PZeBhds;&!fZrZ0GJqwvXtq^x0;F-^%jxbaicIf2proe{=Abdc@NI^%t3XuA-Ih9S| z?lx*crX*p*OO4Nl06+tXa&|$>wfSG5p4P%;*7WC|jR_vg&2n)TV^XPTa$c$vPA=}6 zF)3`1W4OAV?r!Ydz=<}QbTpZCK-5s}Oiax?XIRN+rf^EvoNir`9U}xW{tfDR{ zsP7|DbK=b$RI)^UhC>a7jWF7HcZO*1*HFLbxU9#g^ywdf33eP0`RF*z8lDNsKplJPm>Okw0H{$%8KEEYjkmLK%z2|FNfsi^dE^cMhZ{N=DF_`VDuyJH2a}f}))yFFZTteI zhS`)cYH)vhQXF72VxW!UJpK6?52tk1VZBH6chxg*$g>7igq+=X3_FJv$MdY8RDOXZ zWJDE!0whI@E!KC;i3fNJh|K~rQUy>U1Q12RX01u|d_c0DL$w!DDWYQxv@zHm(2)}$;dBO@ zbS7b2PtxhhM~VmM z2rv)B7lAX1$Z?)?V*u(q(B{dKe3&C4859+{!4I~+Dd%RW3^UY($r#m10zhQuA*w`w z00CgcQXd;YpDw&1=k>)Ok!PAL#rLEL=1-Y@AKyyCs$nuo9UA!1mod~8Svd(Ia6$|u z5V<|aQjJvil$ZR&K?)+{3^@jXHIS!VKRCY+Bt!4Bzh1!W4j$+YPCw`RlqYm|%Yt-~ z04@>Wt8*Mu00&X`mca|=OYMae-~kou`^29~f>K4Q*@z|~se zd!zAarPFTw<}5pl!Br>{RTy{DgL^daf@3JgSPF?H(5#Xk!x+vx>d1m9j5r};0tk$R z6Re71bM*Vm0$8F=(kK=EB@)3IXm{%RoF*Xv2#Fyz7QwFDaZzzs(N_qPmD0mJ2d>+U zBD-i<(OoHWO(B?dNo2KQpNOn^-XRxVo1~)rkuzJVRB}R!C-EHKPvE2C*sGyR!%mbV zJ%W7bg2UX+Y8ZX})r?+?3ZNPQ=hweMj*GP)P)k(sOjLp}79-`5FqortA+fYRBjOHN zNiKYv!*?c7q6e!l%kNdn4LM6eF0&>pVVwi8rHkFSDJjnGSQ?wyg z2v!{lWr_V&zFEzwy9rKm$YdTncW*LgphCRXKc-?q-4mS1yew&9kd3X9~mPNLj@?{Iaop^m^!1+yz$7{kzs>_*@kEb_ZMM#zrB!NFncTpqzT>| zc7Xtk7XhLx+za(NfCTdmfFyE2qbE!QJdFC7osE_by)ijKjMhy5*4|0Dt_)e=~Uc z>8Ee!^Sxd82~&zx#>|^|xzovCIqv3uD_fBPrAtx;^wKW*}=M8 z#aU)l$D5AMC~OBdIN@W6rV|OkFGEvEAhi*&9|1^dsKI5S1Yw}#S_zgsl~YO`VxKe; z5`h;u7MKE%HM|GaA?;4&sZs*DKETx~E_a3T&4~n=(C8h3=yfF)TXb+&R2!S6>x4!b zaN6 z!Ao#QwkKeKtFBXO8-7diUn8WC3*sV%D7^6-LwcdisvJpNro4el-h+CQj!BfP7dteA z<5O8N?g43R4(Sa@C=n3Iyt67yB9IbQr4C3|T(cmkd1`ik2h+l-ljYxg z642AVB!pOnXQtq{z_D0%xj!rSM&~HU62x*9+Y%9d8+4(TX+sl6EFNIx-`?5C?rv=0 z<9|TCOtRCbReC=m&z8phu zCsP7;()DHZ9>sA1lY{Gsx8N8wx)T70AqWVGQUNW9ZGO)D5`Fy=1}+Oh?TjHBa}ZJh z*TS%uFcX81LLKqg`)G?cbe~}ZlBzMXu7O& zM?C-L=hOB?2evl=0)R2F02kAP&|U!?S9y~*^hjr+8qb9EKmhcr91vT7$P{fuYytWJ z40)lX|5FE40!8MTER@HB#q%T(p0i9636Ht1WMq1ci{S~#yT-sD)Utk@)1N{W1pf`v z1wn&QC3T25G=BkoLI9{Y4txyt;~QN zuZgHnEm98rA|;u2qT<$zEQwT8!_Zu#-O#yDEWeFx^X`pohyMN^v-#-y+gw`B&YwPq zpMP^>d1?7?na=wv!9d;`4rlLv`qNK7lp{8M{YQcTSWp)}s5m>C&AxH1cjMnPW6JEE z)l~%5;%rnbO-=#m>TF!e?d>qr3Nhq*1mVolpiiHzWlJj^95-n2s072@9*rIUY}OzI z1n9_65mL95PXc6sZ$ixf@JzF<(Rd^PLQO{;8KHd20tw1F;saFZU;X4!b}eXQ2$js4s(3z!Vz*Q_}!?fR7eppFeg%q$iDw zaBAImDR0z)?>XP61L3`Wo-gW+t$+|j9gqj*3Q2-I2Ha$i!U*ExGtEO1s6EB*ObW_+ z!tps5oZ1h;N`7G*2;!s2rTRe{g785j;@!O-X@dxm&;9N~E1XL_!t&*L7eNr@k$X#m z9|EN}Ngc_i!-uR;9wIn1hixxAwyW>X&2015t?Ur+Yl~Q2JvTjj=FIf$+UX2upPQAe z@;si?&;OT8f3WdP_6$Xdr}@~2Zh52#z)9^nzus*2O#k-n+gsgk_eU7`oM2VA)L$(X z%jb1;>GrqyK-<~P8w0pm1@6_PTZWH`VK4_5oB(>KCzpHThvI$s|4T^d%a?#KgA(VJsCeEStU-XyoBJbE z9vXl~`bif1KzDqXAaFc55joOXKx_b}6OX%=*Gu=#b9KXlIm~<`CFKiZMImj}TCyPqISmg1 z9@PUHJw(kO2k)XOb1e~!cv>)_`NORX3Km3;qRF5HhfDlgIQO|WCqZ#O;wF+#^(O`{ zaW)wt*|M)%6QVqv-Ff+R6;`L$bCAJ9F&4n#zS>~|_!1jyjCTkz-}efza*kauiT3E4 z1Juc2M_SX*?>W)+Jr2+-@z~1{REZ2IQ&J(^=k0jcQ0q9fcE~mc@uw?YVGb;ot;wCvghJ7;Qc{sUoqqhmbm?3S88jCn#N}SehpU2+85JBY~0A zNf2CVmZW715x@zRJ8$AmuNGbp>^n3{~e;mYnnV&3icg zFi;ZdK=7YFeLA~v;eu1ty$KP}(dUQf&!6A>)Tcf*{q5iWZE}A2mq(faSiKSfA`9;^ zuKKllz1l@@AC4y-Vh2s1Dimjz%iFo^sJnv*ca&}1-J&rA@Z56NXrcEtbfeWN#gM}o z2hsn=jx5;&l@8TH>S5-f|rJG?}CO&pWrh|EjJ7%iVecBtU>|HpTG;yQ?@YFf%5Rsu786`JYIzW{4dIP?e5*X2M`3F^5`s&JOQw( zAAkIDJ%H}qym=GGG{f1eU@+EcshnSdi(=`T7KS@GZR`_d3(lG^O|qrcQnrNEs@|gC zyM+5NT}U<%t#D(Ff`Sit+PG|ZcJT>A|2($8*fbuyh#l_`+}Z=wBSX^W967`_TeYbp zHi8BFE(s6H;fqYEwOgIt}A0A>Y4cckc3llg#+*F62 zsp2*<1WI(V*^}Tooq}dc7MQxUD@@tXxGpr3HC~rsxc%G(*pS>k#Q7m*HmFCJz$9VO zmjJ*e)$bDlP$ihx0YFo*vD#*EQz|X$&Dsd?`tGa|a?AR)hLAku(!RTg)_qFD0couF z*m{|Cu`i0_UebEqd*f#6ggx2;b&>#Y^qx4!o71a`^pu`krmp1bH4L9c2_JC{4i)+D z-my`|+Gu}#{9Zup|27ihRqRk3OysHl_((0*$H!&?`^8sY zd8GnXZXF*FejO3?-|lQ}{i|-TyS9b+w!629*We!AMdPf)=)qIx8rg-59Y$!60MZ;wh62$7f-i6UssFtgZ0O;u!=tAQT&pUUd0RGE)xFlz$Dba2Kszf0O z2%L#V=e5M461WA5Xo)`>pjr7ePs`iIcyte%zXvG?4)}aZ3V=Gg0qF^_&tH3hTve{8 z)6ae9AL1qy3u(o^4G~+xy1fRWay&qtU?l4B1w?k@5jq9{_upgK_c4J;f(QgbFp3WZ zfcJ3?H(dz9iB2CtN8k?bPH6&ipiR0XI0d(0r*tWe9NSBtqudF+>{FKD+@wd~;WRmk zWdJl9=T>nVsL2kQ6ncx(~Q#A8=q13N*DIx0gj+Zg%`pj$U1ERhrsZalyQ8_%RaC{8ST^05EDz z@~Sq}0J67gfw2wJpddz3;VP_S6Stt-O8^w}5rYAIcgWx?9CP{*fdOgya(yiL4je)| zVD(*iHWd-Vb#QRHECblUoGn>G07wi-1#QyB*;3vR>)A>W688_Z$9;cL`6S)4-ArX9V?Ag6@ zCp#eU%!qL$O=hM#wYnnbAFnPiz1wWJU#&G7KPr{W-^2KP_2R{g+m9mftv#2I6#+=E zlz!LNR<{R(H{cLNKQCy=PCThf2VA#mkPvb8D zC#clw0VXMl9)>e4+5s#Zu#GO(^c;_Z%f+>HF15mWT3SDsQbSFwEFs*af=7U`4INpG z@TXdk?&dms(d7m}B0XEDBlDiLDqTRRaGY_lP@s)_{O4xq2W6)KF;EHnhVv85kZKs( zgY;q_IK2&wiI^Cfh`$ke^+EQ@2tWq{F);!)$KO!iF-ZAbrZ+CM1=&Au^N1k&vuD!S+-FEsWJ5e z#fv<_dT@KmIAl1+DAdsL8+>CW0B7ly>kqLH_31I%x^pwz0sOj@=jah?pcbsJE@x-Y zoSLpJFCBDNI^VC>?0wZ&98c8kz~JuApaBxx-3hM2U4zTu?ixHuaLeFMa0qU}-QC^Y zFTcCi{S)r`UcP?l?p6I%)pe>)opbiy8O&Xu{4~mD{hSV`3iwqbq#pK7TTlw)7TWOS0JMNY`dX{s@$q121sC9 zw1r(2_@tX*y#foK9mK5Iw;BwjH=ySJ9MZZ|0E1C;0b3Wwb(WA37J_-A{E<5)hM^YZ zf4Nt|cY#7HA`S%VeDkvs7DdVz#egxv(Z>wzvkgv6EHm3S!%Vt_SK;|Fw(S_5C9P(c zjSt`y$Gh9gyhChm){nso+IXUt8C;PWmq*|4FGfQ)$r||no3`f?v+>G>b4h%w4vd>{ zAX757ZW1j9gjU{FfrU#^NGms;5i~-1Z^O86DnT|o}dmcBB;kt&<67Z9sS$Ugpdh~lQvE>dn08UPBSJ9 zSvnEgfqmbxdXPH!ZDl{N?JE^}`2+^6B}1J(71~$RI1#=Q7T*k-zxlT6m9=^9)(n6=#~fyxeQXg$yabov z!_toDtJW#c) zei-8Sa&5!H=f)q5M#N|SNvo|fv(J_h6m%(!Rr80>71%R1KWB+-v`c*CO{b+jqS0b{ zFjma1n^-d)Gsol|{$7R4yyv|Ik?pP_Kom+f3yZOrCDXd(@{@}v!-+9 z@*=Wry&@9sCU2pn0Yqo_Z=4;4=*vfuFS(-H>NS>1048peCtT$vI`5Q&GtN^4qigMoVq z;cD_TE4*;b6dbYO*GVU(^mc&n22qh9Uo(B$#BQM@_Ph@+z9;?0*6b8W3Oon4kz|nO z0%XH;?Cc$|LD@*F^ z;aeTW3SX(1&*2{ggfHx%v~Pd`{qYZ*SFnEzIraX5e-8gBFC?Xi#>DuP=lNv(Y7MOc@3Wj__M5 z$7v+QbuUw6T+P+p=T5EOG^`?J+~smlU);Mx?G~{6^P7q->3IW8{2usfds_WNLV09q z7#zcgS|Nr#-AHN=51U(ph(J^vqot4CtQoAk5h4cW`}J&>LB+ah0>Srz))y*TU=CMs zhx)=0E~@$M=;5~1Dw!r|Im{9j>EDeIR6wE}!HE9l+5_Neh?u<^-~lw?=|;e(f)BaI z*f)itw32Tt?iwBJ$(2Z8%8UKyvVLcL z9$=N^;SzO6dK(z=x2b0_CmEV@3PaV#Dja;!po}{r5z4B z?N#Haj$Bhz(R0dRO|>2+*q}bN?~#Uyv?BQuzG0`NC<82yMkD^>zF@OB`*s z$gN_dxPf+eyjUGxkg4r`$=b;j zFs3aQ%mnSG7C-_Dl?d!={q-}f*7E}MM+hl4%&&OM&Kw>|?gG~3M*EZ;2?{Z9XHM6} zLE}N$5)nmERQDeck`<;c4QmX{r-nlO;Zi&bA~51bfpM3U*DL@f@JC|;Tu^{9OF1qE zKpI$sKu)k0Tnzf6POMIG7DS|3q9X!t62J`i{$k-jwS zV4-$KiPuI0Pgr3;c<=9vAJo_Vhr{U=It?oZ>6I?OXol(O*~4Dizd6*uMeF#YRka_d zWc&OngXzLjKOP%!eeA^o2p3Ov{zu`+h?nBv!q?a4(tKoKe^_gJ+mDZoyP{M%cm#Xou>z}}5o=9fi z3IxECI#}=yD%}X{pi(Ag1Lbjy*to83BmV@7JHU9m&Wg7MjKo z4xUpbEbE5s)}Bx}b@>p0^+)LiyI`rr7pWF|!Y!sh7j5EVP}(KbLMzA4l%0c3^HJs? zbWi32YD&dW%5!hppxh(VTP<`fgjw9y8=Onm!#*!eOfRCFAvKIw(vaN^#(mPi^^U~b zYp4jfk}JaMzD6$VOC_!hFCI4)6~+^G*D*)hwHn@#S?i8f`}#BTt9M2fe}hbovPNdZ zb|3jRsS?J=0pCXnY=H3De8-#XVu{WDS{rv2ZmJl0O3n(z^(;wt7ZdQR2AMQxn-~0z za&C&l@Ev~OgWZ6vB*1aY)CWHHfE6r}XWuV3qODaznZU+s5z=YS7E54mOD=;FiYCbx zP*fJy-FtaEr+WQ0vePJ+gM1<}LO~Y>fM{p$TSD4MnH{Q^}yTl$>rbSPQ2=#zF`HTR|7<_4v0O-H}&@oJcv zOuyL)7VBnE`$aP9y8*BmCKTVHL#rh2C~Jks9nB(*nGNYoI5vD*$zheM2_crsI~r?w z5pv$-A7FgJvOM6wVg<*SWew;UJm{XSmvlQCadO*RVMF^Gu{2bEgA+}oui>iFFrsHR zE-E>1j5bRiUaJx@9xNhy^f!)uY!lC<1Ok?-T;<(?}$Ipx-bXpa!5_ew2B1GKlE2EQ_0+Uq%Q8 za9elB|IHfX|M}&Qg^Id#Uy$>__D}qHo*|Or0i{b?uC)$#nGlx=$NcOqUnub)4R7Cj zPQIfmL#P*pAjke0-Pi?Hc+qsC$v6k;N^f_2i^h+tCAi+eBn zVg_&N6!iaf8R1VJw7fdr0D&_rZ9$_ z6rl41MhqhVKKJmS9%2_Vszzw9kc^w3ITIIh`Rm?sf%gX@++@?^n2$~HcKux%wA zphZ%i2jn|fjD*|s6b{`*J2Xf3KcjU+pO{L#QazoVHVT_ro&_wr56GiAN>s-(t=9QI zU0ohX?3x5=^?P8XS@fD^4!CaJVnQ4;{9eiGiDI}*lebzQzuj_%MPhSp?Tu-+U-bq! zWYJZAe-B|ohQ7G>d8qO|wMqP7S~4HvL$D$@T&q+ItBRAnwK=vKeTAHDe#p2V8yXl0 zp(R3D?JI2p=>z2`O`Z8V=}zbu-j|aGO1RzsBDBlb#N@3U5&SPXbwt3 z1zpSwE^?3t!wt#{!}1lZ(1P1TllwK`>r?JT4AO>K(H-2uZ)~S~Juzh}&2(i=_X*{$ zNeQio&_$H_b;96)OtMbr^jEPFa_21f8?KpfLz)VlTw})(M+y2MV)|_F6ldg;p=D(9 zpd_4ppc`SKJzpp2F&A271feEKio&>QA~{oMScWShQUsBtNqp^&Uc)%Ymdea}z_Npb150EgVGIQ|fQV9OOfl!2d<(MO2wpd!`QiX^buL-;1DUWfY}{(w zsa4?)72v0?K3MduiIUO3U+8|wA@7tKz8=wnUVQkL`b<36IRglEeXd8jHT$>cwx6Zo zJNm)fRTFLMAe8VFxYsrO7J_^a{mwM;5?F{N)f>DiA0RXCpg0TB``0|PKgOa zd7~4U30jdsQw1LYkJ)l9aWQ!Cjf2a$cM?)URFMi)Mm(rN-3bEZ(i{Y#pa5_Feu&-- z`~v)Qd+p>!1b_mXOQ1wjSw3|jxto*ecJjC*9YR8h!~y|-S7Z2qF$5ri5D!cE1E?vG zAW=NE0I&vXk%ZXgA7JEJLB1u>-jIYj%Aac z5N(@|5zFy&NaZR8`3M@Ow_99nY$f}r`L_GlJYi8pe5_99iyEtgJG>79PVV-a+vkpV zvbDq87G@lz#Jjn40Y`HKPp>Vy(?ZOC;jG*jVnMbC#jo7_grqZ z%kHWCAT;KQ6wTwsL-wqbmp<2)GQNO%JS_2#KwmqMT9o9M_ae0vw15|SKKx6l8phw$ zRUWD<-tictpWcC#Fr7^nR)&`=Xo67I_xW51SYbS5O30UbXy+#~Tv)D-g4noXH}&%` zmR7m=qJWXId>D+46au=lq8I048Oe=k3mx;D=g*mIB|WY;lVtt@Fs>;WdQS&s%Sy%c zSbeH=(9^BEY+?>Qn~npL0~T8MbsAM;fam-*+Y#&fC%?9thcfGeN9Y=gb+eU$+2Iss z2jyP6oaMeNcbm?tHm#4K!o}0Esyqz5Ykq)9M~8n6H_4mxgkzwLJeg@*i0s9K5sSwO z0y+mU!Hv*wDL7-%8N9+yj<^kf@qu{=k1gJ#=x*4soK0_oJLs*)1ldTGR`5=K);KXw z^p$K1{A35~X?6-tum3AEAc~SOfM5bv#>h3*$*DpizzzB*0lY|~Ss>@Q7N93+R8mt~ zcVAJNrWy(y@)E{RjKX0K6DAk8^JD;=NhHT9{AGI)jss@92FKG6cV3$|P+g@e#GCcE z9dHSDr-;c|EzlmZQvEr>jX>y@+}V6#rD%MfNen?W)rYyXobmM6=<^YC6a~0+esP$! znbnP+Y1ODuJNs3dtlPvxn061h>M!VJiBw6)o~2VDqDXkY%S}d)olkTXd~?$BVQ&fqIJ)G;Dx)o&{`2T+*%>+7&L z@4g-aAE=qrjAsbG2LjqRXaQIuLhE#6{tQ@!HmF9w*zcwGqIbay!T>YvafL)JNBre> zL6iOwA|>oY`<{v=;j(m2IkYS%7GWpx;yXhfK}Ff#2Fwp%kT6)mM}E}&Npxb|m5{Sp z4*XfJaD3ZOBkmH%Ghd})bH34a5BFRJc+#!?c4%;VwVR_qvKiT|wdRh>pcZhiukP>P zt#`)Wb9-yMeKdP+D|Fe5EliK+Ts6Vs51@VHkStQg2&UEQK%w9fXBK@eZjCEDV*MtH zZ@&AiA(7`(1u{=Y@ls`)*r03U<~w{l7Hua10(@l zd+*@R5HgG!4tPLMZVVcVl#(t2K%#R{BW$FU-3%~>xza|Ah55^;B7))T)BRyKYiaHNM)`JEn!Zr3}ArMkr zl7kP&3TC4BUBw6$dIFP)<+gPm7U7&zh2UqdiF(tTbo8@~i`yDKIQi}U5l_Ytq0Lmi zNRq{X;_c9n5-}JQsXn&J)z6Io%#8)2cd8JfoTPu;1>z#(J$h-z#li^~&>;emXlkI-+nD#3%I&M6Z`7;;fDb2yKM1 zI7IW{SX5@1chuLrl0sjg9c%NvKMv;m=dF|70aOU#KT<$dp^SO!2g|3S1M1#~fVwR!UbW56KfeW@RRT~=b;!sb>dtSB!I~%m zs$rlqzT%#6K`6(rK#noQc!U7H4ts$hEJu*1fmNq#|w!e4yi& zK!~%*`fCoZ+-}OhV^VW3ez9I!FV#1$Bb;oXS@EFe=u{sdF}2C0$)kw1ktjeY-rDCq z4UqnFcjty3x4R569l2ETdv=P$OWB{s`O_)(yq!4}4E zp?#onT`X7*ZiPIvZ0&ii73t|>SBfle8TEpl%TPod0KKq4NuV4msuW*~YLd9lnEb`T zxQO5veE}wn$-kGgSD(B(O{U%VFcm*dn~FKt=tAF4Ve%>pZ17Qi#xN+Ez%5M{tWVR1@=33cwT;ZkgLtnL<kqV)oIqvP85+gko+3-K!dfR&f6+t#LSvZ_E>sE2{$gLfOG z%*d;bYyJ+P1`=+Q9_;S`eR6s-Qdi_2Ei0A~Zzju-BHwYE?y5pvR`EdQOmQVhimC{xr68wUM1=x1M3U?oa3i^%#YZE#A+RMwHsU+CW`}_V2 zSwWbP788yIGVeMD+;v*F3i>S~E|k2~^-cyJ@(`XN8_ zyD^uSh*g{n8!plkp>zIn^t~RDqK+C6`CMs`LiAX+ z@Rz9oKGEbX)cFe--CZ1kRAC1!Eja*qo;wn3wQG#vJ+9qTU)VO9=cRd^L(@+&AKn}0 ze5Xv$Q$$pa$IEjC6k>PI+hNYHw4^Jc05r6v(E56z1>0$b$~YsZFrZoU_BP_BBB3)6 z55x3uyeUXD=oCdpR|r0vnT5ZyG*m_MVm=EC4n{Ke5j4BW=iNw9GEYW6(6hPi>9QEZ za{~2%=@!KrZb@=$AvP^@xm#o!322s>$nKdu>Y{T zm8Z9VIW$nsZl^krG2Cnasw?rwE6X$cOoR!y0wr)j`-+|OMmiK1{SQW5#N?&Jhv(QC zaWAlVun!_O|1CxnM?M^Ei3kG9Q&^X1e`vY6ix&6Hp`V_-&Ls5UUyub~1BY}@n5>Od zfKfzRGo8QT@HnfB1HYm&{Qqo<`vdpJEKlDn7>0z!4*0lazA8#oiWvo2nX>Q#fY@+9 zloh2=k%*8!KB3A;OMC+Wpg&%r0SIs(j~kDfCjbBlvJw|pmJt^xS9WqRx3c{X0C+%! zs;%2hE91N^eb1`ijN-GvL=>|8X>X?LBA(e`quDa5bHvq1A)QfZrYzZm7f z*XMo-FZ2kHvKZ{(oq*bpWBgDaK`DPnS(}}~p+Kt&Y`$+@e>~%*-QO|DeRR~B#$iUs zQCC&!>wf9o!CAk8G9VGBf3Z}E3d577krF|l{^NOGdyNL_w{**z-ntW^YqNM^y~U>dj?9YgiYoP$#&9kCm}PmE8l zNrWfWYU3JpVJr}5hnA)!fhkI$?CUq?U9+!$Q0^|soP)0ntBaEkGb;8mV(iih6(@Zn z*T{H%!O?qw*)Cy2f;Q2nEU*^WHiXkpY?QhZlcsI|%*878^m}@W_Ghe5B-DRR)GFC+ zt@esmwzV0Z*vM2eV+3dd274B zb&A~eOt?6%1I5hV=qB)KEHCej7E<-}x~m8SrNx3`NzCXreu|+Or~vy( zSK|VR{U&$wZU(Vk88U*(TjXwyw88fwr684gSFXKy)sz=k|{g@w-zxX8w(>RbUPWI5G&8Wvh<%pM08c>WI7*>nC`OXBF4UYNuAnjO4vq zQbwkQa}PS++tOp6htF;u(cgS(MyOzNqKfUtzy0b%ana7+E@ar~5Mf-j+&*n~3}Mwd zZk_(w^1H?>DQ=nePB69o zxH1#XcqMuyhcpy}6cm{%=|!e${;ZX! zE1^P~>&`P|`+pUd9#AK3+O?ks9aGbWwa+!Y)tN1GYZ4^n@@+d=e-N=F`Vg`Y$`{gZ zeDx1|!G40j7W+Gr3c3YIb0vjw1VteRgjt$Qh#;fhXy^~O-Jhgg`+_j{v#r&F4`^}? z;vF5#Z9yPVw&#lATzP31DhUA(3)pC*vokOd2!w}2L;2r^|Jm_~V}d`5W3mE%2f_;( zkQ_X}wfQNXD>7d`>2h4wl~M`~zr`8CjpLVEzd>3$)tSvT{z`8Mzl4{1o;4@25EcC4 zeLlZk^MrrkM?C4TDD~ezAY*|ZEnVvyETaV+DveWfWdy8$ncY$w)@4AQK-j*B- zO`SyUXf0dwsr5?CA7@NieL7oheYsmeX+r;)Iv2xWa=(oZlnns;f3qr)M0wM{tW*cLY3LbqQ$}NJ zzyZ^qUd>v)G48TT{h+9nX3bXfHAK42u2iL?qLAO`nrna7R{n9e(mGU^`?Yv2RL5qn zS8B)g;k6~*U4LEKw{`h8vAt}W_OrKF^dHw%QSlDquO4+L4Y$Gi>;^7Pg~!hj(k79W zv!~O#lG4xVTxKwvU+OB0GwdpJ%lL>edClxITxY+NUtlW!EVMET0ORy!aDWlXdXFdT zeDl#w-4yu!=yY?9!XIm_;`(itR_=)rN|!3_$|?iv+;sHsW}9pV;@9IeAli+q>Z|sl zyw`nCQ8wTZQ4CzP#{Vp6EfM(A4#)jp=ixUy$?|&MNLvmoS~OZ`on4j{eCiyX{WW6d z%KUsnxYF0q#B=o%+t<_}GP-1T>Ibfz+Agz%7m4=z_1iM8EADjfTqwbgtrbNFY#gS7 zrLFC&zb{nAsFL1XpZ?lu-kQE_3q9tkK$y#iG+@Jy`a7Ib0mW;D*}0Sjp5a@ofYu%c z0?ySlKG=ay`ScdSv*)w&Gw;obZeL0GErNqwjXy*fuKj`C7?^CY+Wy7H+XT=$Jb_TD zxlvlWIi|gT*EZ%+xfEC5llqxIH+KPa=9X7~J9O_Y&Zf#LtQ!$op>dpZ*fp-&x8C|p z2`t%J+ZK4WdAn?1a@%X_(Dq0J@=%yE49+6m0~5!q2HIEwCt>KxwL?4{Wp*l(~>*msh1_)vE{gh-FG8>wL$3Xcx=;> zz2;(V&hL;6JEi>&5aPO1jC<>7OsN#?-D>`^+KiySHAYA`r&+}(r(5hIMgC((FR!rn z%4fHl<7Ido->aolbML4YTD^%?QRF(SmH1q!cuqd`t)V^ zGx0gRW7FBjRY+-trdq=hert#CjgjwU+bv#PZmIoxJE=H^f7{#f+nAg?<;(0*nP~lE zi*X(KrE$}{xswqA)WfLHnRuBnIlS7NgSrFhBx@D(@#I0;@Tje3BSd>uURf%~;Qlq! z@2Ge^x3M7u4_C|3R_)Dh+O^eOE_eOb`GyLzb?eb~sPvBnBBfGyi|K{)$&h=sF#V(f z<$PMDaA5g20_l}zXzWwvys~9p=-QK|$+&KOYq#tLZ2&%nbEz9OH^8xh}( z8i}fh&4;r?wf2+zHc%hBLQHo@^`{{krksUD1Lr_ep_RBEI|gTPEt-}QPOWVl(}<2^ zU#;5s&VSK$__kPI5G3sU*S=xYjh)C2|MmNO^0J}!s+UL9&){}%?|ZlVSKamXl`{wT zxOHlw8f606Hw+GHhaqWZlS z8owGSGMPAW-Q&o1Xp@yFpw@Y9GJIY$&)~6tDMGhD;U)n)x-T_fyVzPs)rHST}h7<;PLQG9~yGQ@GN@6t-b%#pQL#48K%r zBE_{PzV$ahCgFX~B_EG;+?uD8z7UmRN)s`8$kxNZJWXzv1i%IKJju5wX2%4M(sWHS zNdZhQb36%92rx>74)EJ~Ja9!V!K-gtI)9&P+yKVUkD?mZ18C&KA(tZ9l|p*KQ1!nj`G?%YDlW5A5R@LjUUF$)qUMG@{qNl!~L|!%YIlpc@84I)mry<3uqkI zw!HsNH(0aC_8XbH_H$-SJ8O>+%E(KOa7UAhI}G_V=6~yOI3Ns+v}!ypWt3Ikm0)gL z;#I3elQ3reh7qa$sY*1rYPsgO&`sL+KUWIcUxXu2iu0S)!&+vJZHfL4m)Z}L!c$h;y2PeOod_J~$B*)%#;eSH zY-mF9 zS#SE`7d)gokq?H%eZB-Pe(aFU@X9==!w&ZYC$(jo2dVrJ>mj_>NjLqPsq&V^s!~KE z+ac>$o8|VFST+&|u2nnp$STO#-oaI(!u&fIpM98yO+&fTyi{_`p{-E(k*SnXUv)du zx4vu~Yq}PBTLS34+?NRl8AO_Y(3t%}JTAsrAx!Y8@5)J!DK?K=YLSu(s*Xq&Q1j3p zX}`AUbd0qsdD5%*#=b}8)w!<}NG+)>O->hC#Ifckpa+LuIMpJglRjB&lK0DILu(a% z2E@QDYrHT@2BlaT5TjBZCrUAq^fnqm4|11(Q$oG;37Uc1YB1)wya>iIZxplFoK=t- z6$jB{f3~HvCD?Hw+_QlG&X-ocbm_rvZPS1i+q%8I81{nJ&a_{<=o=mRiA1|0aqxaw z#e3{5x-Xx!1IT(`!#9_BxKm-@;ydwy>4rWsEIl(2k&0(12`1*baNO-=fxU3_bpo!Q zX8`HQyx&Jpn9A-CctDd73-QNH!rMzTgO~&4HHl=UNpWYs@y1l{_9(HmKldlicB`vj z-AN%hidw|?-}`SY&_s(o-|~C8GgNL80>x{1CB`tCWSQx1v&FttZDmhTRaTB`7Y~#J z0!Jd5uTUU)V#yYyYT)WF;US<2W@QL73P5v9H_~5{{$CGWZrMt-N@e*_3ful#RKnbW zNK_$D>xnjbJPzlGi4x`Emvf2j%HXs*Y|tih3XX5I7hqnLHnp2vc@Ungc;YhxYE-ID zu1_>|JTv2N(8|H7l(#KZpc~@mq>ygXl(a*v`807XoJT}=1XqoHK!b@ihC3at-S3Mu zjJ=@pPx=&wDVMl{p2b84W`OmdRY1q-9vuT(e+li)iMsHlTw`KfEb`Y!wd8;;6t)m`0o^}<9jBw(WD8=PfNO91i%;e7yuyJSjFr0?#Q$kP!6J#b{FU|Mln)O zx@s7{>d!KtD5k{v-qqw@VArt7wt2H7><{Noq(>yE=xdg;R8G+&6V4B3BdK4n$9fA@ zHJC_IPGR<$a^Aj`-zo!>pX%2F#;Co3PBnX;{$!z@|6}}NEGZQboy-62Zd}s;GMLPk zj2W-Bs{ICwUu3!&m>V4MahQB~0F%Eb2noTUC!vve2$P7^a0v4VcdRl!XJoXe)!M&U z*4qe&$7Zqlyr0;&z4~e_2uB4K&!fDzw#8*zMpctl%pWg=HExPzuNLO~Zkb`QJvD6w z8M`JAsdO}51%HW@>MQ8}W|3A{1+8=#GP`R)Zp20bJr`t9*>C%irSk}salF+qo3v3H z8h;8#!`%G#3dfe|r}yO=0A)~M7sp?wbDDl~vgv)OwqtpyJoROn(HrMpmMDfa5|pZ+ z6ZTj@{n@{Z;L2jObF3bEYeUCj?x7P>ol@rdK!j2@8(9yu$~TVc7`4G@AV8^}8DBN0%pr&~dCT zb8f;IZmHTuq|{MM0Y6_h!j7*G&~lrXmoE<;^LEDvUBT0pkD~RJAo-%{)z{~Hvi6%P zxt@mZ8^YZ98OG`>>G09fBrT zn<%^?j>sEQ#WogcVEA>k*fl>KO2lI%L1MET00wumc^Gr-fXlBxXvNhHLm2Tpy@)WL z*1$jTHVOBn9?NE2zon87BJbv)1Wc4@*QB?fQp9?wbZ@FUg&UJsMo4|%fVFQUtcB|| z4XTBz&W=B!l+oB>m#;ei7YM;S17X+)J64Y&qZ7~3HO-M1st4S50_^Zg8Y)?oZwBSU zkO()(0|8Pv@Ia3CRH1lJoY)d*%4J4kI8Ka_9++*yJ6qfhV{XB7k#OHo(FxK^EU=NP zZwM2BIBq9g&ePs@L!|g-2 zNb^zZ>aUB-uRCZ7B(KWCdpdtLIQ`#Ntpz%G+Z$IaazqWtY8_t9mS^uV_Eb6k+1(6r z=HHcMeZ5hbBmP3U|AUlIBQH>Zo`m!7lhiFXh})MLw+zaA{fIER&9P4Q%#o(kkqe-9 z0=lTG@}2@1-SW!{yWl_OqUd=Yl!2c)upJY$^|bMg^qK&Z`(LKf8it#$H=*2FTtK)| z)-9nX%*rY>1>j;#1{vd#-@WvoW!^E}&4Xt>LK2-UaA>;u73H0Vf46yma3{%~LYqaU zZw``~(l46AzMhX+Wd0V7U2}R_*>3a2K5l;Po|k?2tGwhoj%cc|Y$jkbKKLu8E%72# z@{hAJb05@>)%8)?EY2sbs<_g@rZPo$#un(TXUiTIe@!I<2-&2!8NETDSew@$ZF^xk z<~P*{!|~A^TaDIM1iu)=v6*wj8BM8PE&-f2lOP9!BXzQ|=Z{qCr{~o)TX?+P}ex455=_qk_w( zi1&I#do6N?*Ft)jWwd8XrD9JIVtyz;k1No3|Xg!%bCOtpmmvXNXPGyg+ zF~;xec!&7=KMs5_^ZaQ3@gaM-(HY(i<&XHEGf}evfL^(;e{~S9RMc;kl)BpKS8<)M z@Z6CfV=fzqIK;-^vTOXx`7?3O_h#F?;%n7-@o*e$}pMyu%B^Z^>=IJVX(hh!f2 z(1?`Z?*COK^?SSEJ|@z=ZG&;sIxf@=o++k28?+kH*)T2d-s1qg^Xm}_^Qyr(7J8e( zR8)>J+NPLs21rvGN6$STA9I%- z+^nIM2W4c>1s#1k-YfGZ5rBBCJhQ^d@mH-o`zOXOqQj}(GMs2_LPIy>kY5LXr^D+{ ze0)!)Ejc)V4UjU<3M%O>13#{`UKOUCRI4V}v{`-`Yra&!a^ZV`qwxY?-+8yVuP&pm zZR>b4?|bBFuZwzl@vOyD~~o{%ta`ujQo=KX-3 zGpiLXI#0?5=^PG&r-Q+wMn+EE=tB@L?D6<&>%cuEOa(ypxFMv6hN21}V|ou?@<&7? zFBfM5>i9p`&(lcZ&zna#Hq<(LyYSk-rh_cD<7k#wkBD>*BAmC)4+2zOfK=7xEAC0T z_b)HmwRb9 zJlz|~xFvLov7?yx158e_@K9Px)+68R#ONY=y0~|GMwSSjaCGPgCdj^mPRY~}%0SDH=+1`TuPwT$f|$x*Pc5+3 z-}34%-J^Sn&BL-WJ*D8P;aC9&CC)~a$T*&Ov3Ms0AlcmnsP2KpAKpGrUVBHrXs8jI zV%FVg@l4=WrobvhBS45F{7l2CZ}o!T@+!knlUwIcL@5-2gSHa5-#hSY&(GjkEe@YQ zO?C_FQ-;LObd%P;i_sR-yX!Bb>zV%h{OgNGxi^N7Lz;Cx?ihs+W|i9tDE5NwCnY5%R57xw8rjy<9xz z+|LVpXB;ujx4OQ=Grx2pJ>0WFf+{z;z#E!_fyOW&_fe?%s!ZUz$lIH0l7H>7y@UIx z<0_{`#8FE8xD>+e{)5TdIeXoECh6MlZjj(p>k|4tn1`&a(WERJva`)7{`y4}9=pY7 z$DZzqpm1C^2Vybo#)^N`jH(n`3YGi24-{b_1dq^%AhMH;`?}M-F1WE1bfL%RO0xL1 z!XRdbtPm5AhUB8SUiqfKhoIUUH6Z54nlCx32sR}3njq{q)6|wxE*;q-JWQ%3Ym#1O zYmPM&HUc6a+zAx{gc>hbu|9G6K1gV5FZ82m^o{dS1g1D?LxhwfExG6WPXlOa@VAN9 zYN}7&_gYCQCP^`FQ9WKfiQ4PJaoTs|#xM3`_P`iu%WrA?H_UdG*mTPn`e2X1DR;9@ zyYS4Hlm;@ajwUT5p}C*$pQTE-c?rZY1V0r-^%LJ>QTG8FTEXP zgLBlD;BO$XLjw&3uCpq+4R}@L2*}gh@wtt0IDyYE8~0_z3%a&yYkwPzJ9@gYlQe!W zCu2OTw|FgX9=t!&>RLBkW;kxO41t_Bv=6hq3!2>uh6=Uh(EC!;gm76|XR#4w!|?z4 zsQpCBBo?ea*sXcz_j0>inM9^@krgj#r(aY*IPl(k4OQ~Yl*sT}B~jAKkD0Zlo z+J8QTx9UzVHkM(&RIU!BDyT+R!2m?0xcF?!X2k-`TFFJ^k}*GXN5Y?dQ;`|qmgzgv zZ%5Q*!oN8jK?jQPya`?lPvr+o>hNXOHEXfH1~W$}=3}EQk!PNxOQo^QAmF0xWteYQ zpL!UJNN_A27x|aR&9GXXEvsqsGn`*@jAIjJaY>&e^dnn^WgQ!O(m)+V#}R(oSPsGb z)t8m!Xf>t^2R-yiy6!a5ELB~CXAJO2$9UMQd~i-8pFD|}sLY8Skjfae52bKly!c5e zNqyEyF67(G)%$enuE8Mg$4pk9e=`+u_&dpM_^h+qeknOCutQvVta${vgGn3IT(YMapO3D*5?#0q{`zb+ z!{dFojBR$oD#+h(dCMN=QgzOy3sZuW=+It6+NqcUoka*`=6)1JAB`F|zMAK2`sGde z>ETN%Yf5JVMo%Aq8l2K0;X+`iXneE&5G@WXR;~~PA+cjH?Gxr#D7hRC+(z_7 zyvPbOl7|9$9BkfDHD|=CQ%cDe@$En=@K>ja!ZTSZ+)w$U(@=iFU#wKlu79(!(z+j% zT}A6sC;c_HlwCfP4`e2UW9}kCIKYXcYpgq{?3HB{*cALE0E|CH1%#^y!I#LFG8#+H zOQEKtIrV*e^DM5SrH6Gxfyyx~M>aZMcT2#-MdG&*ErJNeBJ@gScm#GU8}UI-G!{;@ zkUQzbqV=>43E-3iRcddIO)#1*Yx+KHSfp>C3G2Fe9zQY;`{MSy5b2=Hq|fYWHmy@X z#we#E&CUc=yK$#x9;Mnb|Fluh*957?5`BdY0GX7m{o6#vT$;di^NFQ9rqR`{psU z$QU-u9Cmt2Y=5ZDmOyOm9hrAMDgr#?LE+|z>HyKtpcf&@%M2>@;-phfzhLE((;{KY zA&C9l7x12AMN!^ZkD>07NnomO0&SA#!HB~nIwKG}s@b>g$+|=pshKh_z1h=wgT#iS z2z;1q03`Ob3R*}@rg&4;{(Zch8wJQOH^o4vT(xV>>NITQYSt)hROICB7+l9BHv*;= z7iGT_cp^?q$y0z3bnCI(lJMQ-&9W((c&3h@o`XpO~}40&U* zaj^}mK0?xU{DMMlV5GpqxSWfTH|Djg_igm^SBNt!`bUhrD)_&na#e5{aIDBl#CT-D z<0z{25a9^|Db+Tp4vV~1JCV2^rXO&rQ;gpwsn`8K0LVZ$zu&;Wg-o5J2JUj*qmOs2 z@K-mgNRZWB^xjnb0NaBL2M|ZRbMe(U;MF1uWy4+$;p?m59R&zK(v72g%%gYbMN!g7 zkHR71<5qM`@vTLgiZl2`{2;HufEvXZRYa2U;ts)6_%Js?nW9=PIh;@vd`R(<3LU~- zMPn4hG`Ns^%|vjePW_+1QrwckHUI!107*naR0{p%oR`#gIs_=e`xFwMqS8~`63Hk? zh?6Oh>j*<5!A0SGi&>&fbUeF2oLhF#mQu#&z@|Oglb*Gt_)diLp)#@)(+BbL@WDqO zj`@=by9NN$;8FRrFEk7QX|z2W02{1Mk0aI&lEc0rK_6jz`FjJ%MUVhVZ;T?47sexp zLev~z$ng|RQ?QD)>oGNxG2&|#WP$|a_c6Gc$dbJ7+Pw8AF}Q&PzdjBO2Eeb6w*%(P z0fc|^?QegZ5AwX+9Dn8pUs^r6!HT@m;&R6acj`~Cw%v7l^b@v+e+o(M9{w3yy{~V# z-AzR3WVNi1Igh{fFdk3n+wz@WPU}rdMJ)MKGI z?;z|+fK=lPS877^B449K-Z`Lr7x^w48UAdW2-%8>$$g_dKA z)5sHt8u3{m%S)ea(0H(|E=XLF=9vRqPB$EJ{G(?IOjc}Lglc^ZO!PnBW#m)G7%|hP z>+iI0_Fw$Vzx>NbWNnU*kEwdsjmP8dtFONL==Ah-_xSPS{;Oa8DkBDEvWZFuH*ny6 za9}V1-UqV=&=1Cem-~Kv_Tr1vZiS@Vj!qaU{=&N7&FNWvX1nhH1-+fm(0OXk7waQN zR5_o$Zy5;~uh$*x0jp-cu8&xsMK}rfu5%x@N}EdneXhxDj6gP=dik+f54rDZeir!e z4S;e$PcaDebsi0y6sqFe1%>)vdZ~Y_dEjt%G80nP)9*51k7c~B;kE}HjJ5h^KX-&M@v@KwPo?X*SD1oBbzIu+d2 zNr1p*C1;+C1B-GGd>N_yDh7a!2^IftKX9iy#3hHrI586LDGVUp=dP2diDzj6Uep)N zB@^Wx7ogT)p3XWLa_56e3CHYc9_hAW%?s(J0dR47UM-j^a3nxwKbvDHknZk+#bO!0 z@$*NI!$>GQBg~I_m^N51q}8dUx2tQu;vATP(fORPt$eRZ>Q*^1Q0#3E)teQ4P1y()g6KepR#dXDm;Y zOA%{G@Fo5fphyzx-7eCiZG`ZxFVNXqm3~&Bu^u5n@}t=tnAM&}fj+Y@yf zb!)zWX!T=W%pG;;YluwSal3&AjveMmf#yy%t-b)SRPb-(Kj69m=R36d2aGCgF#?!U z`?lt*RSs_8!29OFU;w;tmJJ2oivtKKyXd<5l~=0K@}wI7^(zlJGv~&&%TarZ5}aQ? z+MP0peutw}Ijyfd;~*c7bwh%oNZ9AkWzSn9072|9zjBlOkk=u=RrpawDEg%EH3rfG zJ{R$602Dup1bo6{$#CH0Ka_>OPKA>|y;HYc(10TxaiLP)ySk@lJmFL_@fQelI}Asq z8PssO#2Xqf3PgPcVHA-MDXSz$^oiu0FTAe!Dny&2f9M2H;}WTTvDcooq^eR{!eW@h z!FY{>giLaSMf&1WF{V&tL|UovpmN@mlA7>F%(UEqeQpi}_*3$vN8@Twmu(~-|LMhv zqP|2<14IUGiBg`yGa5LSaHQ!36ynUo}mH^1Qa(PX^1U?yla~gMk&R1 zObc-&Fr@Gvx(mP)3N;k{!!U^{jfjCg=%X04Z=|31-CYau^+U>IH`Eu+dh+&aam;bT zqX}DG{6G=L%9K%rc2jRZ#kq%<_4aZ#zIN^LpZ?w7tti`_o}XWyot^D4O#1JA?|Zwi zfBox&VesyEX^20;fv5bcp2!~}0|$O74*bcV^zA?U-z)a$v(Aa{sIXP~9*X!`)=nKi zyy*THPVajOS$7RtI9hG$V~$5{k$dy)#fFvs_1s>j2e;U3QbFj)+$y!(dGL#$Je4eXO5PQm!^+_REPS$%sr#}-)PKgk z@`yKK=36-1R$oQc(ZjVt5muSI2oe{g0l=tIy{{S44j2@s^X@5MXttTo=6sfzodfM? zd-wXyN9-`zF*Weu+U)3uN3;0@-u!b6lmEhz!}py{zyNvpJ7>>3THs?F+`xgSa$pz% zcq&_l#P`dAuYIkm{=e6&G5vYA=v8xGtW!EIxBG3+chs8~kz>DuV7#~=WVTqU# zn!$keCO_Q|fjmbb$AKxmR@0z^?9=^b(R1Xwfjfh^bKb3ms|XUb&JXZc*yHG9sA|sN(458#7PU zoTy2-NW|I6w#LZe7BDc-G!cexSbzeU(3qN>6&F|X!eB`>C9GHV#{-IoWua`q0nb|# z?^H200+qyOs1jl?2`oy73JJ6GB?I3U-}Y=#+8O{F0>1pN6%e=Q9dLqWe4vPhEhv8q zb92x^qbL@a0Nq+(BjFCHO| z%JL{G{JMY0*_Yp)jbsi zc(!|Zx?Q(3PCYr@j3>8|P4~8aeY)QDx7b#Fou2*~9U^*A2tWlV`rd_huIg84r?(6n^IQl==V_6-Du3tvt*P1ou^#R*DqAWJ;&*Y2}Lkd!jrMJJ7mPHboMSXg8IVdh~VrDO^$_ zd7t`j3KBg@xgOqyORE10qQpb@qFDDiSb-6VgdC>Es~IF{f*i{UCuJoZ+Om#rWZqG%eKAA0t0Mg4$!RX=XRVp%;AF%S4Y$St;@@cAO7%%+ZSJak#8Qw z*9Wa?a03VS92g9MJu8O5`{e+8`bWR_d)3i`(@+t3v$wa^=iBl2WK;Low~P7<>|6RW zotvYJW&5f1ruHK}q41YW_e*O8cqj|QqWC%$JVTx#jAITz@7H%XG+U|3LjZE~qdSfU z#OZv#!CCPImMr3uzz(#bL=>L71XDCf5$!oMf#kbsSLvZtB1a{VrU29iwj;EHUIE9q zqvi;DBuTg7Jy9v(-Tb|u^Gz!h5)7BKN_6j+kqHxunlfB)M%cQO_DFak#(Zgv`riw= zg~JKQU&fQd-hGGyh{b#9_Y25>ZiI{RVmqBMrfCI65pJbC%yA>1ck_6>dl$zF(`^n_ zX*s*l#pMTUa;&~5%W#4)q{A;S0AZ_-d>@E`@?g}FsbJz)l(lHaBY3=bJb&SMS6fD( zs)jWIG;G$-V#wcNe^%_XxR3J&cRET4#+u33|zsENT_zGdYJmKqu7$oQXB;FpMH{ZE; z{aRhmCX4UB`rU`${N^_w{ox<}Atv|W1`b?tU@!o#crk?DCkO7{twwxT<&0wU^VQ!jV=~7U*%(1AHla-JJ+v5VLp|(zJhh;9%vN2 zy`IUWtOL)LJagoBA#E6t{6qpt8L01GJ z(RUPDd^e}FcwbFuJ?$^Z0Y5KjtcL)WT`$#c?ydwDx~%8b0_Eea`e`cUhexmsq_tIa#eX*8n|ZMgJLJ={w<*y+=r+==rN7Sojx$ z)M0-Gs@I_l0jrSBmqY(5{kTZe7|8xW6kvfLYXg|5r#|-lGflvh0e_BuQ8HkxBepS8M#G_Hbi%chUVh%#6EIm4 zP*3@sA?A^YnF-j9HyBIcS}-x~w##Kty=M@F2p_ z`cn?_i8)V%=a}<><8!=(oen_Cg<%j4N%Ag&hYDOF8WmUvSM=_LqS7nTMr5Bse=~3; zga>G&z`o2uM$jMAsUm*2+ar!q>QY{VMuGy3zSq*=)J;ob^z>!07 zT^bb6qY)O1qT~mjJu8TtmS`(r;d6_lYVobBbDLZI!6M=FE&yw2`Vnj+gwkTUX0FTy zfgG|B4CQ$O7mAA%d*iVsPkJ4`4-1fR)MV}19R$T-UEu`HF7aW{jYvJnE?{h_gTg&N z0wuhENhrmCBxeMU2JD@1B|ZEc$p8_f4)^~TSDtvXBXa!!#>tXcs*Y$(Iiy%cufq2v zL3!?!ht?ZRkIo;y_ILj;TNam?3XlKWeD;&|$?h@Z z6I9i&_#Kuel_n1UCzu0+0q_ZaxWA&N)$C51UUtK1IFPPcW5HQ_hdvHl_f=* z)v1cy&+BF9Y4fRUGDMu#5Jwwd&r{7?AGksnfCJ>hOD%m+0tr4+Nzykb*$WsFJm^s7 zK5)}56H@>w9yR2=pitzvt}q}Xz@~pZXy-v$+G4qtLhYUDwlL6&kH8_iXh7UjdlF!x z72v?b@w+9s$7>+?AFUKE>sGLQQ;D2epna~1QGqA68 zSdiv|9~^Qw!IG_m8YjYUFc4e^AYa9~X%|5grv}DHiuCkNAaFj12T|!sq|UZi0thJ$ilGFB~n{+`=UJ7Ap{f$YCU)Jkd|QKNtX? z_(%M~8i=qeqO5smRZTgR?-*5c%n;-?gp_;t{WR}AKEU%FtM{K199b2gMNOYob?6dB zpwPnr(Dzb#gnU7&+L=+H#j1Yd`&IpvItfWP{JrGOL3M-q-1ck34q(i(yo&za zF36!?{REUClEBa5eW9Qt;9{hqbni<(I5bkCjU_GnQNOo9L4CQbKHr9cbk(4}>Es(1 zIQcd$so3O4Q%QNZ%h2K`Q*2kxusC&=%;D08l2rT*7YaVY-clJs#8E&KWs%4&83q9> zcwssZCU~5ywH-R6PnM75r`}3y0_{^EY`t|PE+nj_o>EitxrZ@=A1{FA5@GQnp99>9 zO5wl&^CL#8)jf zE;KmUX1fA#zT52XQ40$hn(bxZomQ9aqYFrQ{-|HDpTHk0I&iU#+I<-lM7d}1H% z2X7dHMfoy%?X}m&7Z=mf?&yRAe7al9t~=dr#y1w*{uueNsW-51SW(ak2s`;fK-JGQF;K7Qw7|Y zN@?B|l>4Gr$q}-iEB8d?DmhoTPq3b-ieyxz0Tu#set%@4dUZ2-f92#en09~Tw+{RR#05z`D4@MswL`x2hRgIL4ZOlD1cXSE#7 z8Hw0V+R2?M-`m1^-aK0WaPjh6yXw)SM+`Rfeq<=OT51RPN#sC&6+ekh8`|_iIlzy9 z^y;e*&&K1ai~447JYN1DXZHNoX0yAk(!V(0{0o%)Df+%Wzue6^zL&XvrY0G_j1R~t zC1X=MuNOjY!tkeG>T|eL;WnAY*U#wjyTad|f1)%kqDo=$dFj$Aa4D=HOIH%i_H@%r zj6jS)E>ytvL6rMnk?I_*P62HheU+p`uwqT|2nv?)3mXy7yJH`AiecDfTmfhK1Rpq3 zNsOCpfTZkz@s_9(Q_eUb-CkoFLKf0|GC>;cz*tHhG-@bFBL4>q?*(+*8uEd+5ja(S|}ELTfg2dV>ns`WRFu{Dz<$&A&|IA3ngZr zJx!q!kV7V2gD~@=$=F`Tg&lVgy4A3wWGBUCbLxGuck*GEe90G-D z5&WQd0|)h7Ax-$fRg!2W;T+=`q4T48@!tEMq?aOpe26DR-A;|^%O~gX<&Eb7 zEZAOiad88^x zyM4q~#0kRzlks?W=lZQn43CaA1V6rc>-vAbcJ1uP99ncf-PHg2s2jZv)yto@KVALv z7ykJY#+2qAFkuLOQaCV-0DMxO-ta#9Q>Q6E5I6Fk1{&BgY`3!36v?l5fp zDK2m(%zzs!g;I`;NU1SO8@YQZK{MFCSgE6+DKN!1zmzGkzl3iQukxr1ijw|r6p=3N zKFIWc1!87)0r!-M`Z~2e0hf9U=zIF{;#}pHXc!PV;=H=fd#qtd(PT^#F03ms`d94J zASyB2r!kT7`O@cigZcKz^S6-7rx!jMdEzraRVEJOTR#fKwWX}P2%HY+HOLOua#%zP z9I*4kK*zu)PD5B-zz2;e%a3ctD4XYKFoobN%3^@sS?oe+nunxa!&p1hQ0QjO=)r2u z@|#V=$bjP&K}!%l+&+;U7z}_<9zv1u@Upc4vNf5g&3N+#D38mcB6TZsXi{*+$!!Po_8FHT=7qz{^1MX%coTgI%zIhT7S-=F)3}Cn z${FKT0qe_ri9Ce7zIVo9!q<{&>E$z!yyvsJC&8(PqGYHe2AadSz0pf2;Gc@osT_x` zg|U}vn*LHlFUQ$>7U`DRGEWnwbJ;ljC&CVAs~mMgBtIx3KT)Qaax!^EUj{F2St>|7 zgM4oKv_JV7FZd2HMmQ;E>jc(J`RBW0-8}Q~4^Ro2un3&pL9jgLNP-Xg;_$sH%^`X3 zA`hrCYNN7Fq)^6nGGYk2Qzbr2d-1OF_jfeo+ZWx8PYSzBV2gUr-yVq30zSH-FTPkC z0B#QA;+gRdk=C;2fU}CLoJxS%7!>=7HlhzVD~Eqf4-U=073#8hFf zHjNs9z&1sj@%M^@K#&UXRcnCV7tZwMxYg*aDGm9j@=~bs(8(8A9x%eO)H~AcO*Sk# zi(VxaID%u23d=9%#lb$rw+!WPdDi}UzWsvjX<;A`)&MZgGAba;hj9Qd4FX+Kak);s z2ze{)3E$QlI7lpPwjDWH7^jiK(XIf&w^v^?)et72Vew}}$*rhG$wuWe16R_DA9-d` z{)rdU_@U8deF0|4)OMm2JJ63&##Ee!RI&7kZaK*WX`iqoV>F$|Z_JSi zw+P0O!YF@74m8&Mc-DD+NzdOW+E*7Fj|ld+h_cs`=W>ebf@e8*Q6axcjfzVua72NR z4PC$J``GI>yu_XhTCezOEl(-KqX=TTp)V18l2JgcL}n1+YN#KWfCE&;sjLinsr)o-I}B5& z=md@x^M=DkYLqc*SyN)3m_W+a&u|B2QoUE{*e9tzag>4?XDL>a!w0CqN$0*iVS8NW z1^vWJIpZbm-QnD1AfmYm5TK}|6tet5ICx_L($YzWsjjd&NVZ;ko35!aoQ#u1O`$LF zTltta9pB6cN6!de8btC41)De#+zfdaC@&MxGzf?`|EX)S; zDHG=%7#8@TgX_C(o1#DvECT7^JvJDm!4LByUmO9FZzWPvfy?eHbOmbluEq#5K<)@Y z%kRkN;bs6x!Tilg?)%GLf_8W6bwpC>fP ztB!4lTa2C&XE|_YcU3Rldh6+3{dB%wKckT_AusBd=XL!CQv;93P5XUlnqYXmb5wsZ zY^~CU!T)4%U@!na8PD+pwc_&i*GIGJnR&NbAJZ*4S@qp@M9+2P$r%@W`Y{j?MucOi z5*-l3@(1z`S)p7&cq@dJ9SY}s*-?&c(iJY@5L6*Nm4c=zx$u)5H8cK662Pmtf}q4x zaUn`h@vk6$HKC}3Td7OES?b#J!py{q$W&k_C1E-HiP@2ElSQUySYH=BNWtJ`4o1F; zARJ!NQ}W2?qc1&}V|%({kb=ZW=Vp#!or@9!o%6{kgfg$J(*Ah^0NP29UOmL>tB{^H z0+7~%2%8LrjW3MU;|2AZfPGseMuihjWiGsqwEPZ5%y-K){|UFub4NN`75XtNFG8`1k`xOO8*et_WBXjZgHkaC?lj^QG(8Uh9fPuy@f=y zr=z$_dFoOx1(qK~iI1LUu~r0iC|GF$$h3&(GI$3ru@z_9Igsvw*Scg(4F`ZUbmy*Vm2d$y>Rh{reX8P0d8Uzqa^y8!SV{$HU) z^ewaO{KkXR1ZhRPr``oF6tn9ItiwMWnkax6iQ*P?`J7AKaBcv(>5okFT@+`E5x0)& z+q$FZHAnw`pBC5?h$X_s%6UTRy-mIZl_?VlB$p8dyZVM>XveTod64*J+sg6Q0ZCqf za4U610=H;);0;}yQY-`@Xu^-C5Hu~dm@s*U$D9{Xdhiv~X&Pe7^{T4bU865e^+g-g z1j&%&>G3S2sOL~2*A2wNTi$~$2N~Jc3l0rhUoNT{K3fEac)c?*zVkpCp~&&QlT1k=|Yn##DQ$D|~Ob;2pkY3zYA(q?_j_tAlK<8S*sHx z1Mv&Okj9_~KGc6a52txL_~<8$x2!J+9$}QNq;Glt@|);n(p1{ z@5T{8iG&Dda#A?o5Ai1Y6D>hrE~^2s&!aJEuHN?W2QR}1-(Kh&=Ff&pQ;)Hw0N>Yj z*kA#0CKX;p9LAev8x0hmDXx8Xi9Yb7T=qIJSROS~XQIlxw5!}SDIBngp?~7hyC*Xl z04R58^fJDV{A4I-FvQvXrsW~%VhDxOOq3=<6J@E#EsaQw!(T{1Edhc2=4cd}Yd7Auwl!bt4Go2*B; zw_$__fZ3+n-22nN`|FEt+-&ZxW|v?5;uqaqARqTEFh01CJ_iN^;G_SHJ`jc9_{KMy z&wcK5&Es3Q+B=hbcPIUv&-B)_OIG{W)7`WD-p7|0<7Zc!?w`;(Fh1oh{}Wbwb0}HO zus^H4m-HGC4!xi8Rf{03kQ}iJ8#xobv=Ix8Qg6*Tm$E1F5;zhqa43NYKIz0Qu)euU zB3!y(4X@kbaP%SY0=7SLH82XDa2!S*wiP~{beFKu2jRo?T>ZXCp(Fep5e9xGQVk28 z^MY47Zj!KZ0kHQ;k6t7dM=@y0#Sa1oB~tDDL^2VF{ga$E*B*WNghTyP%#jx#Mkouy zah5-om@Kf<1G1#59sXy5hhg&VJxhzU?!fCfg*EvVI~SF?+tcvca8743m^2I=;tSKb zA`f4!V7j=rT=56;D+7RvtSmcg2Q`jor!oX@#DlK%JWXjC&dz|bP|gyn^h+8ZQy+wj z0T6li{>e-Dn6`3(s%3naF$N-!_?Yyl+j`|QzOL{$Vx)^FIVjf3%1ua*g^1Y)?Xumb zB?D*=9w;I&BS`Oc;eq}7gjiItFJeBo-k}{_OK*HSJz~8rCkUd{*D!m%^ROVoObe_U z76?LAED|I?i~(R)?A);l8mVp<-LR&jZTXPU_;E}H_(7qKQ#)L2+Z|0O(-$8tm%ua+ z`Mp25e&hNvhQ}rRdh1cU_&z6wzD50@A3i1&59VWZ8w`Mt(Zl*>N#%!FbC{1(VtoAp z^6vWRc|J+@d3JBzKsuiyESdMO=A7<#b8*>zZoTPm+7o?v(el;OrcS+o<`@wail7Fq zj;PE+6rsWVzRF%X~}moiZ}p~&?`%6r~Q9Z|$OGoZ27c$90~o^RIW*4v)x`Q{o?cK*iil+OvBe>VpL#5fhF+`?Gx^ti}PdS z+Tf@25>fu7YXBsLrU76`KBTt9y8%r3NRedeGy=lmnWPd7l8M+Bor+&)p3QRF-aOtI zd^tmy`KUN0G5C=Tfan23yI;|Z=_??e2M@!6a^iLEgvfmR%NA2{`r)0xY_<%~?r>;7 zi~(O7oOj!sLflibSs+MW1l7oqclZ>@p+Mjh*54vi9YGiEnavpwA=9-PekstM7kr4T zTB5M~dV!K>od(Z%$Kf9GJ&}=KTJ$^R=m~&RL&%XS+a@2*7fY^{@$uQ|@fS||?Tgd# zXwAol9&!@cGxexvH{tf>Z8JH4*!LHFAEA4JSB7WFYZy=Js$oOuBg=uo0Qks0ntvE! zXJ>p1jP+maARg1RW2Jv}*3~;cf?|SfrTC$$*1Ek_f<#-k` zSMM_suf|a>whtKh@t2I^XSls*dbtc1IDM5w%2TmrRZW`7rb|>rOo(=9fJ9r1Pfm&h z9k8jrN&Q&%Nxp>PVxkR=D&!6|>m4xAQKqOc>NP`kjG+NyaYxIcIc%Vu7h4QH_kh0` z5rM`g-fz)W7nNwyk-l?tFOMgeBp%`v;ib?Fa)0V zU_WAMg8}dndmz6Ga=!iTYIObi(`((TzfA|=_+oj7<0>J zWw;__a&+&7Fe^Y=*jgy{o)VanMFFnwttb|wh2F>t07Jl(aP}2^Q8pte_~Rrzarzg% z+EhS#3(fYDx*ZJ26&JZKWWkfGiIk|nw24S44;h(Vt(MZ^g`z9b@Jqz}qG(iz_CV#o z;yw*k8+RlorK`wN4;Bxp?M{G{XIurUuZC1b3!nb>!r`MGqYGA{{@9g=lDH@%SfJ-=D%$x8(U3&dRe({5B2^GPy8X z*ekHS3qmm9umHrrSMbE5y(;w-2<5m6C7<%_<)5#_DgTHnS|(hr_r>?x599xA*oIA`^iZfc{KV-Fk_60I3;H#R^iT! z#2yd#ad98C#`^^ypw$x?W33M9F6K18@l(U{i#j&^*u!XoCI?(>k5eHrZNNx^eCR2+ zU9C9GYt*pNa>n?>XV@vQ0d{hBbo_8S-F4RAQSfKhXlvau81s}GxSvsD$bDHWalrA z1`ZW%l7|SOSdE@aMvrALC{xjl@=*Q<-9ML39a0gL7|xLtr;P}~Na7YBaKTpz`7xZN zs8T#F!ZRl+un4F^LN3!oBtsHYlY!GBDq4K+C+ebAsc8t=9I$Ydfd_(l zHjXl3JeWy2VIw);BL+;E8~D!IcWX|`%W#sExSHOtFubBHaSEq=eA^`~0Tp|!3n0Oh z@*+-p9Lhsne8RCZE((_6a{Wkm3L%s)2x(TKCEg<|g+?p%_n>dX{R;+|Z9C1&X|=pD z46M)ufFXq+vS~oasFS{b(bMNCyO>e*zGoU0>2gsoBYY_J4eJ9Ee@WvUfH*~w0v-$D z2nm|K>Vcy`w7n+W@u^`9q-HYVQ(kTJJkta{I}ql_lgYB5PhVi9;o+)Zzj1wb<}S^P zqiaVt#Q7!t!F@zIFc<(I(FgO(l7+xXvH_9styikaDIeN<`)?l~9nB^T^>;VBZF7zD zd~R}P|1IXCZ)_F1^dOw)XF4GKE#r8K8u#ZbKmEn3%oHMCY7T6HbY-OydBT<2EfqiY zxb1)SCCM#n#l<2p!MVg!{Mcn+odr-|7`>br_(b&tP=X5^!=OO<3V3djmh&9pFkjDM zV|{Pf1;8On%~QeUKYO61ynu&QU!|KPrgAB?4eO!>**BaqM35W2)yd=VllMdLPfG^Ta=L#LQhk;HqZHLNFlBFEA0c?5)JYCOTu5U>!m;-^M#_oN~AsLOtiUIA=)8~c?l?mY=bHCGja@5F7wU?#P1|ZnsxQo#q$$q zJp43Dr6(b0o z=;@Q?Oo5s8(&kf+f4;47{*G38s7f>%z z{BG@YE}QNCYy(zYAqtd^I2Aw$>kw)PMB*`@s&I__EZm4kFDHtpvVp)`7ECFwC=NM7yF6DAVu>TJTBpoeUT4PPAy`2op}A>>^2nl(cwH#p6}rv+pbC@76!3F#Ilu z)fo3fKpkmx0Wf;uSFkKqA}y+U+p?iuj^6l>eZ06l;Zg6H zB0>K*pS{?0*P3ShkQK&1y|$P)P5;*BH$?`X`xL@deNjWa=tMIpd=}_{xqL_9v_$D?`mn6nn}cSr8N)5kp)7Vp1AX^!uMy{ToP?_3Hy{#w`LfCeQTP3iU;9)gUTjY`J0X#V> zb!@=ku88JN`4loIpi>~i@?4ly)MBD=__>6AMuvk7Gi{FX6x{nNhqP>r=NBU#ihsZ< zjPxcfpNcXi1`_9pn=oR!Y9&q;IXq3P_Gza|#d&Ivrj^%j6=m2u6t@o#gTQd~etBS+ zTGWVRBq}`=!pu*{5I9LuRp~#9FSI80i)!pKe#kfR>X#8QxdtaHdAYi;3y|bH6|8l;a{}_=Y1tqz^#)q$O#DUxcSgghSw(jDQM-D6sIl;-^^Lvy4q)?xmW2 zf@vhi6^RP>6tF(^j#m=&*(Y0_<0WWZMhF!=`nD^|WfG&U2{rdZtUd}x1r(Q2!x=Cu z@<3)HMdLHynM5&uQEQdYEt_t#5Qn1AN~c z@)AE(TRuy1BtJ(io`8y92#E|ZVQ6SZ#ApZ_-E6l*%FD7XGF~(Zkf*$(duM&3$O)mi zqddxu9)B_>P>i__TVY-CJT1dZ;d`MAewz{XU<8XaB}P&LGC#ja0Y}~Xzy?*o7alYy z$;6951f7nSXFU)o&$b~zY?ffbEE5=dMzjHm(7u+4zOfu{rk22Kv7PHZ7sdyCK+LWV(~(WJc_rLAIcA0ZA52Ea$?VZ2{b?*HBW>CKbsCd2#u z&_>r6Yrf`JRrl!WUuQkkvFm#je9rN^rYAZ${D7I3L%#WgRrup8T={W_kj;Kb5v3h* zMMH>(xG7IlAVdYiJP8f%BpcS1>4b$G7J)~ zm;=vkQ7A_5I2W>B|KlPrqIi-VX$jrFj{F$RE!p7*ic~!18IHt=?qQ-_hBLdkpo56M z?}GH3FQ;v*my<2ks;9Qu|YoQjpY1mKd7o`g7J4&G4}M*((x)lXUzkFyWnXsZ?A z8mt6IV8U6~{x_GB_8Czs(c|x0EJ02AArX@lD%;m*Pu{vmj7-pT-_yG5*Fp!~>38NR zOr9=;I1M$^-6FW-A={qClG7JRW2<1bSX}bV7#-m30KOu2^Km==EXKz}p8QY8?^Lh7 z{`%|v-Me=sa`>1~dVnALeJ}t%^hfZ)2{@Z{$BRw(1%8{)u2-8I>s9}cnbUiA+cigv zML)k-V7&7KUM}j&g}*NT>$o`LpC78ii61e-z%5P+*)fKE=p$qwqqZ^BUxZQ!5oALs zQ-0*il+^vevWRX7azKHV!x|RFxfo1CL|nhfm3(n~Axo2w0F9OVBr1T`BEvVFKJ_Xz z?V~4>LdI;LeuStzN5rXcEYUp7Z-RgnJrkO~Tzog{clUiXdD} z4VWHfpo(${On7_4IN{4hu;4LEfQ;4#6tt11Dz%x5=hN*$+GZA!1n)+CKiVFmYa|pP`Wj**4}v1>bb!5a4+S#M=L9sh`1V)_ z&q+{jJ_~&bNJH_dez-(}#FIghGKUWkQE`|9Ua|uQ(CA^8cQFvAJQcNj`YjuE<@Uw` zM42U$gZhocJbmCwdFIDuOSGgj&9=&P>FqGIL#YGmZ^}~=jYn@eepv9D_=G7jt2Nyv z^ZLTYhqZj~_|pJz+KnKsO=(x}2Z`b29f9Bz%{cgx6*>fyPXXw2gbmZ_EPns~_BGHp z^dRK@a%tP#3zGaDTRE($=abJ{zcoN%w??`H#NWqu$$P+WbIFN>PIt_v<7zgW8lG=w z>o3;p-5sVS9y8&wJZ(nzA2Dn2=+UFq7ryX?pMn-94(`L|z+eD;*iYaeQ2(mGm^S_F zCSvee=4P*LcAIDTJu(8&OnOOwLOEm3JsA7Pk6}z0AVR33|lY?8#?kxqK9`a86h9W)}-A4VOH9UGWVPj(Q0P`OsUkBc8znF2m#PT`6xF;q?o? zJ&6eF2ADE-vr()GmNVXX;B--;=Ik~+v&7p(M zdg2Ak4bsZ95}k_7Wbt|16AZ2bB&;t+aiy$r8d{>Uu(7-kfhUzQKc2t7kC!_2!{z#= zBepY^YIwa9ks1&v`ut)BDTGU%L41a^9R;oY41*Ft+rH!2UDH&+6O^ZZ@ElBW;vnsa zs(}gImM=UbC7MNAs;~Iz_mB!&k7c7YVA(K9$WY`wOOW&!a-(rNR@oB+8`FhFh-fr8 za-p*-pSwns#}KbE7=j+sJaN!71Gd{yb$NLivSl`%j%Ll|=5DsT$)||V`DF1k>>fOu zO=geQ>vi+p?|!$iCh!lp503+b0r27R{(aTV53~NGKdS0~^KXzBx2ogCMb&IJ7dNWz z@_Jp5Q2yN+y0CSdUnqZ+W^DQ6H@ri_@$=?VuqgGC=t%O}A9fOAW$Ry&8}CVA5sU&T zG)sZQbfre*B`hWD=lqh8!1i}ltl|e56G|m_`a||dx_QD-hcH2dq`a3adG!44L;wIF z07*naRE4gHa|>;nGegpWU=N$a#`kHN;3S1nno^TQtpJZeaKDNr;0i1`#!W8eBUtiF zdWmQF@JNetMyT=-bxDun!T}Qb!Wk}C&Leu2_O7sbI?JRRUM?4DIXyQwG2M*J2D8Hz z;F70^mo)-vAs9Bzaw?X9BU0c=E>4g=5s~P7d!T3w0|Km69EB3T2!f@&o27n1H8_Hf zsD&_*c!8uP45*|8{b%e{p(-%=RUX(fdjrzgZqec;G5sqVU&SwY*V`qyf*eqn&Cfx;xI#=Sdj4U_sESzVgg`%d!EK^2|*;8cVvpz~CvFYwBR@H_w0xTjrKm z@EY|LTf*MiEifuMQI6{|LSEldp~rlPW&;kN+X^dm>2R`eD1!~ktsHdIu{b53x6cp! z))`JKPiK_(H4KFk4-jtrr?)q~8;)j0mPKUJK4z%o;9Ez&VyN7x`B6S1X=Kcov(N+W zfr5BCh$#6TbKnV=#NjC7m9{hH^5AhBp;=n6F+U#>o+~|V`S1JYw+PC6=X|{HslUB= zW`1)6Kz9vzFp;ANuD@WxZ@}5y*iR66DFeBVXo%aFJbn`d!;m0RFnOJN_UX6dbBQS& zXng$m5prT1BW$b9iVuSEd0^n<{k1Z1_)hLmLWK4iL;sFoj5u}b=FJ;?o|ydxjB1?C zkE-iOC#-#FcGu@8Z`?dNdyCHuKjLFTKfQf+Hf({_qU(E{^|RYJh=b) z92iCb{^#@jeb;z;TGem8#V`7d9XxeEW2>Kw{aEqeJdbXCX1lE!`e&Yh(@Zw($kjsM zaCpZCBS4X=u=WZ$!4J3$L5ChkBlOHSDmD%VT7$*}uTUZ6g^T%fB??gm&Av>Eq{ILW zkf_s=(xD$=BZi5DjLjKk8(NA&SNthI5mvd3yv3% zKb+wlSBrY#%-*9?5Joz_8HV+}e`W5otGWPm15L^TPM-7aoYq=7 ze3@&l9KR^2ZcfY+i{U76>g>L&5q)7eWepxg+lY#nw#4%};PF`AX zHxDMektYaGL3#xxh)Iv#YBlHV=DqokRs;76z<;!=|H%o%D67ZP$G-EOcaHdY-!c0B zDW_^4t_QQ1>3c75C-@7@shzX2kB|A#IbG+6iEtdA{_I+1i4?`ff%t*x6zQZObBh<7 z|N0TW`warS#|23bNa8gou9z@HTmdIu+fk&CZb)S`wRh|HAoN%n{?Dd+k4&%@Jb%shN$J!%hcob>*mo)y#SY>eLE#0$UQ=D z5pyP`PBVZ5QgjI$p2npD!9;^r(sAw~vpEJ5=R;6zf5eHwt)f{Wo2C9mE*ImR z^;ZE{!B`v}*iZ3d=+q37?(H}XtGF;n!kGPy zpp%?rGTOs-ywmeyj{0ccc&B5NJ_z|3kC^^LPa02|qzh|nQ|)?XhTRO?l&PF8dDk`) zMl4Rj!%({fr5YmiGNkF(WH-3mimM-{bIyM!CbzCV20LaQg;uyVQ|I%fNUHD zZ?@y&At@2A(wn6P+$pPeGwE--%`U|kUQfTcC^HEZc!w-jLGrV~?knoo8Jp+ipZui0812YZ}XS?xlUyUcXCchXn4;KST}OD**oxwfqUwpc4$g{q1kd4X2ME4`xT>;b-6hUz}{W zugq85&&_Xl|Ev@M&ez;*CrhvPW%MzhGwqM<#yD9-HptnxWd=Uu*i#7OLZf$oF#~3VNv*m^IFoSB^UZ$=BP*a5AmALaK^W@oh#b|SvEj=L`15G~3 zYqB7lBpRkye9drJUOYrpv3hm{44h0e%N`($Z}vk@86%E0jp<}`EBqt0^fY6v6Pn*cXYi1vs(J2V?z>C|7PlQmgD&31o;Pw}a{ge5Ox0Yjuu z-1zTk;cB!ArrO0c-n{ZCc{M65!NxWry&9uMX~NN<=b2!4902MRK!fa(6>m$eB7)SM z=D#J{gSFF+(0pU-S7-%=Sn6AtqUtEwj1o&D%&QQP@+Eo8X%r`7Y=Z?8fQFye7#Ku$ zsvvN{`T?mB6t5)_KvGYd1gi*a+wF(aHb!u14{UxXLX)XYJoJ=*TZ58xjcbjl-yJDdIJkW8U^L{8q}I-^3?$6mEJ zxZxxA^E`&@)^xjkcfX?dN0{)67EMwg!3!)urmNdLm>>PZA0mX$H5h@DSK)YqEc~G{ z+zdP~vxf0lKEXQ7elXbLjPGzACg2Bj1Jd9-Oz;G3Mi`ZY7-5@JOZT(>=Y<>glT2u& zFn3UgbPp5hX&%NOx;uqu;R3@?w^MoXfMMFYTYT!^AU_>U+Q#L%L+Pl^E_LG$N52&) zLE3L8E<+i$_PkY9AMF^{hV(tKj4>)XqawGm|$zCen~URxurlLyZ=7* zhT`JvnzJaTwSj$z6JZYlz5_xmNvO#~s;DNY!mU53RqYiGL!=dDqiMkL5z)(IefP|& z6o?}Oib8|meG2B+YlkJ_#=@1e0?-|EgAn@xj)&%LvprkwHWzF}d~zxK{xF9xL~gkXHEr&hHE!v5Ypo*?jrp@n-#{<#_ky z1)F9buLiGg*1K2fS7)om_QB=#g0FEg<&y!u`-t{J+z$-3fr9Lc{Mp52LUJO< zKxI6lXlMdpWC;&xQfg8>Hg5PwF(NUBW+JHQnBqyGq@{do1~H5<$$n5in(GOxL34Dqn1| zCf*)L1)4o~ghwzK-ta3=!0hwDJ7Eai-;6idNhqURg-WYKXB=FA_ANTnldgGJkWO2B zv#lJNE_S%KbV$e|aH~Gy2r=CHIVmP~Joy-nPE$+Tk|s7!39i%}ab^zCI~y2}&4p+} zE6!ij>ESr|w})V$Gu%(%gKL?SQQuh-N#Aj5-!B3NMPgE40##ZZ$Npfgw@)d<(o1a;4I>HBVVcdaEgD(jfb`( zG2DBUykjpI@!L*T%y!5NxEcUI@0~_D=X(Q3)cX;q1O{9fz$Uj5d|^6FRU zYfmoDE-uf%+_zc_15(_SM@mJ`B{~dDle7V|P%s1Qf&1yK|%bVjTb7lqL53bEEoc707 z@UQ9cGMeyY`3!s~EX_WOY3vOlQLZr77H7O{s2llPI09CU5sGQlG_jb)jOB45!4LgE zd{JKOEaH}ph{mk}QsFNaBi)t~*$69Z0@dluo=J~dhfe`xbbh++TC0HkXEfKmVQc`0 zoTM59j_`*G1&kkWIV=x#)ZF64-IF4q`;$~lqpZ|iNF3-NEuTdiGU5Ni8xot!&=w~Z zgUzPSJ%U;3#(FCt;Bi@#q7{C{8g5x4^l|5gHUNZSHoKMt45txUx1n$3eukCx=fJnr zs4xEvt2a|=j#U7RQ%DduHo*u4Ms2)G!>sBV?#$RYpXT}tuBR$_aCcE#co7e;~xevN=JanKc7wJK> z2N$9h1wP5O#|9nI+gGC!*)i~2t;TD6;$_LPV{NNpGv#t1=}^C$#hN3b9u*u7jyHU-{2Jkp z0&sG4e2!M|8=M_jv$XN#{K12Fx8u=|sE;2_X0!h=U7fsrySyE+7fPMdrerqn&8Juc z_X@zLSeZWyDLQ%j9QNoh7RwVl@ac9vynqqB%tb!GO8+{iuN@PH&>xOG&+lh^>wbSf z@PnI2v&|@@-12B$-ew2yCNvNHU6u=@yO=`wLpBbHXSQlAbAXr# zeu&$1A9I+@`2#Erq-Q!#y$KSjOoV(3@32LKc*48UnMYXBwOe3pgAT$X*jqA&`<4XP z^wb-fy}x4Xx5uRz26beeva)B4(fu*%u%-;}X&$ScmPk4R_%y$riT{KpZ{qPN`~)!%0;FOI z*BBI|-#9(|_%CgZRnM$Vi8kzLoW?0su|vH1_XK;K@vZK#GrDKkt;*#-fh-a55)8LK z{RDS}zGX}{N;rGpKv+=n)yhi_swlt^?kq$0{g@>A%8ieN>sA>`))e$G0GB$X=WT+_ z^`rQsn51E<#DIjk;ke+`Peik`k;{Y5rz{bS*^S9k!K-W6N6`G>We-nJUq62EUrcwS z4?o=f{_p?(<{RJmhLv$|KE)ciR{%c6%KW6H=s?47eQPlGod4O`a5kEa&goqo>)Sry zUhxx-@wv%)#2Wu_jr8Y^|7hdhzU9=LU-|QcDe{W+YI|aOE?)bx{jEPZHG&qrQ8g;l zPDDC&z#3q42PcUD1}k-_CSig~=_s1cO32J&HIh;YW})k?JT*Ha!8}6G(O~@gbho^R zFXHY@X+`S%umhu(lr^rI)FqQ7B?6nvy+i8pu z2<-Uy$wN_*z`TC1zi47|s~TBSKpE ziqAztysRNpgSMwE{~6uNV4Bs=$nhPMAbZRcIIK5G^3Jz@puy~sGXwkuRyry~tzdp2 z$}b3t;s#}pB|CfQwa^!NsirN(64F!#h9C#;X7oZc>y$$7$(@ zuHl&QIOBOT_>wk8f9LapS`3Zu`qA+TOb~&8#!ekHGL=SUK(W$`864{@a4`Iov9sNl zVdBi9Pr(6?BW`~x@XjXary0|E_Ak@TezYd}!RF5|#%0NXuqmTUg;LT>dsRT8xbo7{ zCK6Xu-Ijn`u99bQ@{afHzGt{{*(S}>$kI+5T(1?=+Y7LtSTO5Pfc8SWG9m%162TRd zmq8+}Q;asUP0M9I0c5u^ksZKufHh#_g+$zvj`zv%>bVxQ?V3~gzq`V?u?*ZoU* z#FrL7TTafDi(h604bHPuu(s+Rar>h0jK`b$9KrWKwZC!D^tnFL83T<%KJVG$E5Lbw z`lr(w`vF)E;JtY6-~7l!`~u+o=9+BQ+MgT!@=DD*BYZzHWSTpU=Wcl>(A%(2>2k>O z1o>IP)SM-_KAI6XIpH%x|9pOYvtfDQ{ZsBdeelA`gi8-UoIN`G&MU7B-Xx1x$>{yh zr&a^^3c#mUm7ko@#kZLSdxM+&ZwI60gwg(Dd%~353BBlq&Nky*JTn03<@Bno#lZyJ zG?hz#41-JZ%X7#0>3jCikC^9$g=+{I#_|lxL71I-|H#wgX|_yTSf22>POpU}GrFH^ z$W=I2<*o~u0+spT>W_iVt@j6&>z2?6|vw$Uxkr|5Oh)96ll-=PxzuJsoEMTg{qIbG zn*=jEAvmL$mDB&M37J!ric>%Q1xHiEIj3=*gUI2A$!t7&EmUU!Ucp>6qj;9hb;wx>hLf3H|1`~bhf7d65g11Gp=sYmT?X{I=hR;sDF4Hwr6EmBom9^$fox(V~f zlRU=8O|(p0BON#Ag&tv(yzC0ExJoBXKXqTUI=)FYQlVGPqUC^$$?gqvTa)IK|2;87vz*bBETV)KPJ#*Zcy3Fu zUWmDAIHvD^W->f!kVar+5v!Wl-5*E@Zz6_qCqR5D7@^I!WaajK-~+Ud8-hZVrIveG z;pMLK<*SXrPPs@Z;Ut#p!7V#~HoB@0X}F=&H_E$b_GiM~13BbC9@IIcZMu1<&Q9Q7 zdpborJS)=o+Y-6Ji8`9Cxjcw;)-W5c2+Tf$>DW&s4qkkGd-LneN<1OC_a4m7AOGbK z|MKzlc)I$d|MibPygwB{b$$xwUIF+ND)Ez$`2FACjoy57H~rhc9S$G7di&yRH2O^z zQD5V#%tzO^>;IZg^cwg1pIxoS7ay#qY_jGm%_Ynco!DuArw13ih21+HGxOffIdy%{ z{z`_@VZ+4m5O2S~Gl~a|Oj&oR1<%lc)1kA$2fw?3y>q~s2qQOM>>(f+`{`JGooXjk z42WC_00wIDao#qv0v8_ysmho2QY&(_nj?xs{no zgU6+UZd{YpWHTX*ub&(UYy+zC2{0z2AySu5M!JS!`UO4W%K3`v2?qm=6lpkjHFcMs z_@Nl_kn)EP;ay<&G%csbaL!K0HfRi^-NWd|lW@|$qhUQb;%hefOuD|;$f(v9QUR(Y z)gIjK4nOBnxNtm-7==BTQ{^6bdB5U=MWr+~n(Sasy234icSvV@(tniSR1reoSuEWA zxH(XFrkbI>QGXvGDWpdM#xm-w!xs^2iiH5^5<}4{h$=xyK8R5?| z#e6fEttPoTc*17^xG0E&0kf0k_To!?sqhtU4!kDp1c>9oaQ4pPYV+h*zWUYw3-Y%- z_vYuUfqMnu=d8d_ivIPl4+ekuhr^K@|Je9)%*6l8^v>7Os~>H)%U|29cAr_Y?!Tt< z%{el&rnB=+pWN@CzRvL=^k@3DEQLP>o0;IpPMoLv!vyqT4&40Jco7>ZfyLREuf@mT zNBzJkN4Lw{K0$9XfAY1nGveeunsC6g@4nVQZET_I8NpE%yh-Tbm~n) zsTI=@ooN>h#kAQ178n&A6+p*>!kDtc`q>!(;W|v>$VaDZT;W!I+3x&vw!qnG;Y_nD z0Q#N((j<43H+DfL+!!I;pr&!e4p<;8xOG)Vn}qg$-Q57y>pU18$Ek382CyM&!>l-` z{mUsGN@4>rD$^(BF|2SaF1z(RX@a^jm;S}19ozv0#|U2(^G}Sot&(X zU*yY@FYea6+buT+lJxi8BLu0*gXxn4t&I%bo1eJ`?iGNax#~VXBAvy4ljmFC`rh>6 z!!Mk#R@dXvdiY?ySw2DlKH~2F2kX^(!utMj!9ECG6kyLu)>B@j#9JzAfx7ONK)+tC7)&BkA5Sx4<0?i=O@t->~}DI3BWizaB%r?N>+EJ z&H8LBgZ{fTYm{gC6o1`Td`+C=PY9w2sqVBTRtc(jO>h;c5Hv~c6At_asPWZqjK;TyiV zfl*ON_VAgeDMmvow{OC4NoY!;HlK}_OAw~%!>mCrB8;2-wmOqueKf1UD$UBY^DNQI ztWnaeXEhz;^W*b(E-o&v|MXA)^lkET-7<71H|Oi^`c-zlzQ(lIXEO3%ucjyfency5 zRc0w~Q23VufJcUqr*&(3ZWki^JPD#FKV{j$HGws}@r00)?+_GThOOJLfQ7{a5XP7= zelRuaQfntph7n>V@S3v#h26XE92qGX*Mz-!O1`?p>~SKuxK8KC*>}{RT<-*@P-yLmfq$s8#Oo7K!I2A73KCa^!h*9|V`l(D;xWAoT zudchadW{Q}B_Ug%mguRDc2l&1_c7NFNl8 zSvDPaw@bymiJnC>i1$C?9E~dEYf9a`ls`mp3mc^)pOF?|1K$Axbr(oB%TzP^gzV%hKlMf2fLG~wN_nARBxttt0VI_H z%H+o8%yi&qM9+)DPuLPAV9Do%IZ8QNPgf7uo7EYEhzF$Z_L6hzAxN+e&}FG7XmsJFxD4($flqdsC+b|Wy7C*WgD&NvXvQiNQ2y-(lqKVQO;trgz|2Ed*%xpN2gotT|aNwIHi?z z5C95RL$b4*in9$d50rLd3}TttA-~{gd(n?F5FB&F=CZ;L6L(7Ayt`3QgnYaz<)}Q; ztg~dGZLsyCZEkY=ob4WW_5-wX8Mi+zK$v+eYS3u-p@CZkByI{3CBt+GFgs+dJiZ(Q zW4dj07r;bkWL^-W1s6+b+twjgkE}cenjj#20a!`u4&iQ}fOKUUmJ^^MN>-H6z)}wL zT$#oyxFjrX{##1PwI~s)DkCOK`32Ji&Y$KtKRr+G#!nuNhcV>cCf~eu`las{pS}1jd#2tQ4u1V+z4$dIY|&%~FJ51cze0yTUo1`@T`h(eYZ|@^ zz%|X-smc|50G63fla1J(=^%S)PUmAqAz^fz2-oV?bSMS0os`(>%jM=bLc-o?$7@~0 zu$o>i_}+8Gtvd#K^MH}t(L-9HkAr{HmI12CYaAIikfhVHQp*JD>4nyo4e)~;Txsi7 zg8J{nh#(^<+!+m56rM__(+ba&H1Ykmvf%S0lhT;S3d6>4Ry1ZiNw&Hj^I7G>5WdPJ zTsAS9SArBNPJ0U{&71;gNZCY=J8|NPj4}WFG7ve%(@^$65mmgH*umm$lw`+;I})IT z1G7iGUj;wRGncQBG5x}T|K@f%cH_jWfPkj1dEJG`{&(g3#Vdde(T4!P;YQUyYTFKH`zql z-~x|ZXxGcDh8^Ad(p$|$k!kqW<-xnO&zW{AfldFxCqublsL3*CGqyQ6K)97<+bkqs zLi3ZH72OJuy0*arXL?mDQa3EQHBr#5tuo0=e1ww+kLE#{ zZc$>N+{`BPWpyCFnURjCfL_uI*cU)|lqD)Ciy6sHT|d*s;rF}nYVt!?y6Ynv{_92znfVE03oJz0Kbu^nYhz}8NH3?SA5~3t- zaro}L4&R3yvzB47bG$2!gWxS~!?jfeBEol#yN}#gx(WA1XWA9ci_4s0x^1~z9(k|c z@+iMdk}%)21=|BOYHgh|_)aVkMv6HRf?J$Eou5c6vBiL{nFoH=2+i+k-p_8EHgjc{Aas~@=-4! zZj(}p329(gC;)sR7_{L4zLvT+cD=4k7q}(oIQB>-*1L| zBz-*iv*l)U%`(nxyI$SCdUo~!SvNHces4ZT4csdLAES~!Dag0zn_q+j(tf9~{uv{a zQ`+wZt@45naY3Iwp%Wa-2sU2l+XXZLG8YGOb|}U3-r&FMo%_&_I0QR<1khg^c+s_Y z6n$*d69{8|x}ndN(T>N+7APa{!7bDfx_^hj^^6G}!mtaw>>eEG?m4uLfN-+V5I^FU z?6Wu_`1DT)F|?72V18b>3c)^HH=geAb}z5?v!=%xF1zbEu|wPX)UZb$Qe zdKDM%2mylIsT;#LQ2-p(vQ3k9(|#6y!ClgRT`-CRjVz;9$w{*)w|pq#G%9wU(3dVb ziJ~E_G!PG_i75r3Fr?+kP*YhhT>W*YL&4{C0D?aCrosc0R}htqETzKHKmX*#AL0~X ztuiC8B$~WieFx>fl7*~RrrQeV=>!k# zCT0La!F?llE&e(9x zN?NICw2hup%5nem(Hgi{06tnLKX=F%7}qYIEM8<9`}1sOy;#h5|2fU~H<(I%nd`4l z`2NMzDfSg3{}q>DtQoS{WWCp4+s<$(jDKu~Xx=j6bS(QIgG4$-!XN>i>d4Zn(L;fT zwqYIdnFM4B8hMWNwCu)v)1Ag0Jo|WHWr7u{1yFU^FrNs4hL`De+>+0U0C8nd;DmwW z)HX~q;X08Q7MyUyf@~7qUqQw^&qSaRA@9tnCNy|y*j@04Hf1m>>(M2nOp#dwGvhR` zBNX##D#QSzomviqG>;mDxdOS-+j7Jy^@E@9o)Iq`&&CEom=Du5?w*#HDVQUiVZsXg z`Rz6D)?-tv!Tfq2TmLQRsFMz9xiqlx_9$p&I{ogYhyR94;#Tj!E zOl=5*7+L5_fHMwRk}zACjLx-rT=p^8eFA=t*gP6u?X3jX5Wz8WCwj_86w$$fu3xR| zcU&xh;p)xAWy(T2m`r{1l1pL0Zzsjrr%X#brdhhpMfR%bPQ&nx(`Lv+?#?G%mB!!L z;Oa`%V_WztiDd$eQMkcA&aMa>e}4gq08j$_=aq)OIQ3o%q4Gv3^+*cRvkhYBCmDdc zWva}i+}VgsM(oCGW~zK&J5L6H<C6en{&DndTHa> zyb-Rxo}3M4D3>e=ymUOC{0=h?{}~y1|6($Gd$@iw{r=I> zHA@9^-WOx9s)?4Zd+V7RxK{w4sgO^@d;QKklcVwQ!EQVL9DVHJX1hia*uBALdtY30 z_1TIee_rXkS~6w1VhW7yw)Qvpe037s)un!VZp5<_+E=?RmTUVy;RpMUoIbZ_+Ms~r zZy7t?p-D`BWM)O*MAEUFUmX71)5icH5s{@h<6!_{HV43QCN7}q@UWNI5_1Z_1gr7V zD!2@PWL)u^6LIihWKP{wNcTN$N1-4{`dzUQ{_v4h6c7THD}lupMn0L@txOy1K;8 zdg*dCxV^lJt>C}By@UiS=D@9&C;-Hprh+}Q39TK}E9YIo_=gOg`jTnEq-e=^GKs6| zzshWL$Y8pG`35w?jM1PnBE{`-uMz9FzWd|TlflvP3GsfHp5tB@Cwadr1t-IElmqer z+ze7_D|w%KDiQYvC?k>|;wqXnczOA<9k#mfEDBgD1JT9^SLyQQxxb_@0c3U_cohOF zrxYaX39kxSlx5=WTjs%c>U3V#-+{xYCjwp)G>G!680gm|zb&s7(t_E8fXnydEr6;o zJ)P8lQ9ttIi0*eBWcO^L#prk7*f3{8hb{o(C zM)Bl5|^$wnUJN$mZ8lkW9e0li<1z&cS407gIH)9BwCpPk(D zVWAPH1I+ZCr)R#3aR2*M4csdLPgTdy=lSE^ZhB1Lm|R{S-@ZG)fFHiN-i#i?j~_Cn z;*KF{F<1!k z;!y&zih*vIb84sDo2+N^c6{M#fHVY0NL&q_V^K0(E{h_{v(%*k%x`aS6V5TIoB3_* z%mCcn09#838!u8}`Y8$kc*g6F!L2C;Kqa93rqw~n2<{5cev0-Iq~JH+yXkcw1pv;T zx}!oe+Ryrat2}Bi3IIxgGX=(5A71;et>8C*+20Z(;F%1M^=P`uf`UZ41|WDNa)?k# z)9pUGh(6#*q{>YBJc`rzE?CH*TPG?it!~zo%P$)kYn}uWE}1Ydnq>Q0rAXCV4Kg;qQb|?H+Tqi8$^}>qD_Hg`dWmm9H?dKHjUW{*TSoK zu|aIYCN!6hEblSGJa-m=XsM16a4OhKI*L2X?E~(r=+pe(K&Q~k_YHtk`1}0Oq`qgA95t`3<`qFZ+`pkB`oqUK^ zaP-A5KK}N%zdd~Gt+%X9r{nI;Gc|Cp06bG6KdbjQzcV-)y*T&^%LwZ5Ze zYmNa&1wbQyc4T)q3HP-&8J~|vtC1?Yj*?oK>GZg6i<7=9|1r={6HZ~UHR1W#dY^s}0^6LIN;uh%tcZuYjV(S%xf-{-(G;YkD&<%6 zLKAz8ppFtiB}`!chiS=Hg2Rtf{kk22xYiuO6nt>7H3}t-EN``QcRmjznVaj!Oxr)< zViR^Y&*y`;fABYLgufVEGM&G4^PU1IQ}!(nByQPERPrr}(C?x=m)HQH9fNHSQoU|y zV03NWR$k4sP%=>+P*M0rNvV3@A2lVhqUdj9 zr@g=SE|@;yPwO1d%V>wy_Sp;h79dScv&xoH*u$`8u6r9e1xL*D((GZ_Iz>%Tej47P z6(9k5ARKty0Y8ZA;YGCI19M2Lk$EPlXi>Ol;q_aVo{a8ZwjLHcxYUDnA@Szd6aFQQ zxMgGw1}5nRCkp-0Xi9_V5(pjMY8V^Jg$KAfqQoejv560i?3O8fkL)cLi@`MtfVNC~ z-4p;Rxt{J*^=$h7nV%-W@#yc@|LW^?Da_qNSzwqdFu@4EFT^DIq0z){w6U78GI%i6 zj!G;g0KAHc@9w8nx+&F&&!|Sjr7s96C>64XtFW#anrqv3S9G8*WDBhXWY&ohHq!}0 z(LTfo(0AsQ#C9gvw7qH1FWXI8*)uz%;3Os2+P#U;k-k^oE_rw}6>cF@`a0Z{35aaZF=s2iJ|RT9T{l9?M3OxO1k_Z@DJiY-)+0$jSb z3yrg+8UF*|48W=ll$O{In&RI^MLH)T?|*WN?DU$;gLX$pqla{cuS4e>61z7~)qvx& zd-F5az#sgjuJ4~qijenl0 z@Cdsf(}9ymP(#j+pLCkPO^RpPfdE0f@>Cez z-I8g6z`#^`zOtdO&@%(UAS2my#$rEH0b$o9EvBRUb*AGHOoXqPl6QpW^gmaZM&LW; zea-0q=Ejk}LZ4Xx1it6@JNVSxmO_E@B(I34*aAwOac!EN=WZ;ASeeJQ*3{Y?!f) zA5kyv=61$GWw^ekXZc)*^ZFwNyjrL$T?*;)DeTQ;Pz*Rv|uv?+-A8@*V>YWJK3Ne>zT&mNrV!#!2 zwIo0$jZgllUf&;GJb6{ENI2yJEp5TFM0OhRjDak%$wFe>8({B4d zHnMZrD^Y1CK)djy7bB|AQvnrq5ZD8rwxz4C(8T}%AOJ~3K~zP(S~r^HGcyncM(N_{ zzsUw3H&Avwv*+?$4B73*`nfZ@4>F0jVyR*JP0g&8dyC+=)opnvT>;RxPdPKdUXBq< zV-Hb?Sy39VSq^yhd^&y|LRa(I>_^?-o;L8+um0ZbFW!Iu<XOYDkm{hYMzsjbuNvO90r3$}e%7QC zTVRNGfKE}rl&WTmR;8~%m~vDp!jcH~s&C7-foZ#NW?xmvZFPZY_1Wkx?eDZ9NTDpm;a7<5Hp= zr~9^;f=5{zHKl6vPMsQedA`M+$c8(L8L{C}!B+b1-(znS7eI!q=M&?Q1@BKXPNfs1%O##Lj!B z&a&)Aq#w^xK{VXvX8(yBf9`a!U=p#yV>fqeXI200Wk|yp*^K22lC*qB4S$4Upp($D z+xts@w?9@SDTym07h=E(q*02;yN@!7Af=j+X6 zGJfUs_-KAQom_2p9LU}-{=@Ni-t~h)DgpQApa$+|01hhSlRA%%-Wjhp$0vN>>il>% zK7Vq%-!xl0k&lVz2fOv6V%`;hn70n5 zEmH|&5rBYnD8u4!kkZqs-5E)20=g}HPdM&7e@D|UkhlWX^JsY7g5XZdf(2B8BtY{g z?7-dphaiWrJsgamXsRAtnFCpAim%xxtD|KwwE59iSU3ydh;WMVMV=;Hg+-PbWy`5# znC1on;ne-*lgq*N^;HBsGeNBLv)M09RpzhwFd=NLthv(&7OaOm!g1s;taey(6aex< z9#lB8?|_VXbk3&>{iCVp=*geL4I!r;1t4qw3PAH+%8aEp&ho__E1rZ;auZ|sJF_5w zh730WlDv}N-p+cPvuwF|NSgw#ox&H6KkKL8D=->)zFvW7nHBW3ePG6P6dC+Tn?Kc{ zI;)Qmj$A6bsrS?sS)#ra(w52d@Y4ya_^sZiED3Z4V1_a!!uGJlQ`qhRQFBHucWv-}mE1QES3g zC_Npp;@vs};0z5fX5@uHlgy4x&<&=q0`xjHdd7?TtCJJh^u75=4csdLAE}a`96$pe z=JURQz5UMh*=%$4aIxDwT5Q&j=9}FaU-vs^_x};Q{UElmOhwHXf z64C5KzY^`)xDG~mNOZf0cYJJkVP(I;1ir!P?vK5?$2ox2SSIm14L1WC6+e}j&4Ddm zVSfh~b{-lEtBseKW7aZR>e$iFifNpsn~xAgE9~WJo}KuP@Yn3fca1+Izes?>H$M@= zE$s;!g?Y6NOxWld1puH4cbnD>gh%F>DFH5mozZGWJY-a1cg#k(QUI6%hyu{y5M~)2 zLd&_N68vRbNV1X2YOcg**#LOsq}k)r*bWo`NAF?t9KT`Gam_fmxF-sFHkBD}n2(8CrbxhEzTW-==Q06p9XgIh^Fb}r z&TWY(W{!^);v1G>c!8PL&(Km0&os>k<;-3Gjy|E0H;CJKMXjS!kL%(sw_&Ck+S>a4 z#RkoKO_=#^Cc-Ul;GJpNLv?6^MB3xTQy7LJ5PeUksiN24OW*+w6@Pp*qI8e=WZo#t zkmHv&#F%bZ@^`)#jrF{+yVxJ&5~(9%cjsNMk{iOcHMXlexYRXa)~lse84w_|HM}!7 z3tA5M1bF%c?9uIJeSE!IK4jKtIGRi!9i8u9_>&L5JG@@cH~-uJ_&;BN;~U?|tF82Y zd4GG2|MJ$rCn!10X!NH)9GpCy41SfR`|0)d`qyq3qyGxc_19SUKY8!HjlO7`Hq6^}}EH@lyqs zc+z&tkw=&j!XTeR?KFhZ&ELf3f&A6lHqHp^(3XyVp41F>{4EW=3}6jP5hq-b0w0K< zpuO=67!KL5kA_)xv|kvT!gp+oH`K#rI;|=J2vs2da(VgL@m9r2s`*W5-ALE z;JQ2@Ek}7vmQHl%7$w(fMu7;NGbqp|Zp}Rid?D6sQICAXo++Hn06e%DoSdIg*L9NM z!K0V*jeEaIFlISLTLChDaGs?BDvW#hS(anTfL4>Q(s@guU+Q#u;6s!=PcM*bHWaED zRGt^44TvoWp;Us2X!^WU*oZIOK3PJEyXwTUYpf^hD?qe6zX4GQEeyESVP;H35IhM6 z&bFFSG;l5}IcpHw*iM}%mHrdE>NA9-P5Q|SG<{FbXv7E$z9#hOC&KC8r(tAk80pWn zJ7-v`LG>P5 zXI^?CGh)Zn+4{lx`PD0rUUq)3g8mw_bbg3evyy z=6(j?lQn_ish__(9-fRgJB}F-AJPXt%c$U25#mSO>;DKQamJ+;+*i#N8*4-g{WH5* zGYcRypmV}997$+oUUY-FtGQQrLV9qEjYU{Gp>cM=Fo6cx;q6iBL`6%yjGPW^!vr_H zqZ!sQB$3BJWrW5^I(co*!I=&Yb_a}<4rJjp+0yaKZs2?RFbCbD0iPtwxldLyhopX- zdm5ozG^$#wWR3|O)oDv#!CyAQn6t~psEA8t%A926bEfmJBZzLUKO9_r_#_H|l{IvJ z8hehwncrRw7T7Z1C0Fz+tavLC?h)WHt{Duhz79<}U|cRQDCAkI5UlxDy^o#75Z%nKtV>q`YeJpHNnFz8&1i2Z@Qc^HeWS$OpM6Q zRI9N5st%*1rc#C52Y2uUtT>ll3IH{T@aQuD5frvlXt^w~P`Fap<>rJ$6I6BeG5rGp zkqut$9t4^Hgj+LJ6g+CTULv4?iBMJiDx9FPbw34yIw_4&9=HP7_5CBXJZFZz4wz|{ z!D`YJE$eHAFwy3j9kWjWQYyaH_?UsfnYjcUUcl!mf<;2wOaKt-y2%24y3`Ne889_7 zKpgIU)mFtzw5rx=f>y_@li+NA5IRn=3d2RiuKyCAOp7Z-Ljo%k3c`Wh-zyD*_`HDN zCXJp<$Ay5i5fw}-mMJNI^oOXu(S7Le5y`v6GysHW>1@Ey!!a&x1q4xh`W}~*Ug0wG z=UvNsM$}g}f(lFI4Smnq0o>!`_3=3$3OeRvUyGyN?z6km=3U+hT?s|?=H_1RQD>U_ zil}<{Sij%;)?oP38&4RxowBYpJf>k@z?2@q9KG#t3PU=AnPDURGujVdf;Z9h=*94? zFbex(8hh(H3JiOuwjDg3qxX<>io4EWaE%mjRFBx_z4y0$z)y2@gl?C-2$QhG<>@rK zU)!*5H=&;ne+b*%f(qMcAHwbF=-?3~&O{V}7F7`yRJ>VOB0Icxn1W$E;diYL6@Bu+ zZYRsYs1R6(*|>#!%_cAf)0*RYo?G|0o}>P2cAa0b&hO}6#^~C$=?P;J`Dfk&!C; zByArcggaqe3pAO+yRA#>f{n8%0N!-yaoQ*VxR)vhY!ZXP4|dFqcm_nDS*NiTq8;7jzW-t~2XajWj>*cDIf;C{Ws}jjOg) zWE>R^42b-vF75kx4C;c-%Bws6&84u~8u$QdRqnBAPo}Upi7O##Sg-}-@9td|FELJf zlD6$08X+hOc@m1J%|LWI**r>y{Yx0OU@_X#GcS9(<>F>NcDm%+F`3ko+o_hUA=e%NM=vs48CUu%l<&NDu>RnbcM2MC!<$K17xW?|fXFyuh8;(E@$^Hi z)2XRwBMB!?rW3m(#;&lS3nOeW_<71xfyO0RSRP4OmnvC}T$B#sa#YR{G;ZVia)iR( zTg8}r1kaD`xI3SzUZ%1yFFzPOe*9kCAHMhg;K>KI!(Vz;6)xu>jF=5j$oo8c;SsSg zOWf|rcY~gz@e@XtM@(BP&{Y6VPYo}j#I?8?KK5~u2r zZPbeOVwoH!^Q4&vUmLO9T4I_!Qd6I}T-ScT=24#32U}hiDqU z8vNV{JmPDZJ2uyIl7G5k>7DI#NFi*$m4C8PTi{+q$+NnM4k zLf{t=wN(ff%%=Der3ifdIXXyXSUlgUw<|7Kj=Q1b+7R5EFhV)g_7=h|%G(qrY{Co+ zLSk@)0!OJz`N=X2?+jUS!d1p}L?J{u^;w$uXVwbL>ogh|vGl)pGY)uK9hhM$JrIzVgh={!bv6NW+$N)QnW9y_Q3 zL2b5-?yfU|tv{CGDOAp>0qX=m{5|IK%fahUstlaUd%* ziQH%O%JAp1eQ!;PH>}uLd8y`z{K=2XP}ZI38%8k(>zm6K<)~c0K=`Tj^YP zc1r&U!IHH~$~|?B(p~4k_?(>7W3|05nw*{MS?i{_14!vWW4Z@x?l zuMsv@_)I*-H`i>!RKVlDZ5uM7F<6|QMgj1>deXxC0t|Qmz=ATV7>IXsQ+o(( zh}<7bmJs~BhaC#oF?$7$u({D|Fm!)_N*M3V_oL=#+dl8XnPSqfDb$c=K2#>D$5+$$xcw9U(;C;RR|V z8uM6Em;5r|u)%8tZ_apvY&b-y?yMU~Pne|VOb6bE&$}VYutXALB>#9BVaHCG1DuwQ z`06d9sOY2~3eA=qd~pORQ!u4;l#)_&R;_qR%z9LrW3P_sQ1@tz%(mE9_-Y`1&ttJ0 z;-}0&@ufg-4ji-p=JmyV^(w0LCG~Q;osD1H?M~S;a^o$5^rd?RpuL29tJeUHzBT29 zjNg2dQO$RU$8g*e*w!hb=fIq>_B%t!kJaQI;ir?+%j384`v4rf`Fk24u?0LZ;_OX# zcY7c4^ErAUyWOeDN@9rUe540uG^pnpq`)=!@FL^;vq!$8C%pZ7PhZ#r(@_tDpA9pg zJxtW@e%daEVwf+&L95lMKycT7?&){r&rb0byZSxK=SH4oDJ*M4BRckXfd4*A0wp}l!|*;CRZpJ$o0`0xxeHFT$bA>d`D=t zI2q0s+u3A$G@5SS-EJ2M#@*_6IllOn^C{(qjUO2Dipp>hP|lg7We9^Cd<1P20G2|w zXJ=>Q^P?k<&u`FoCVtp&g#zUC>gaa0nqn*LO$RbFNBT@qxL0Q5Yye9K+?lb3HIsR4 z(1(|7w7cYG9WkB2;CbR@} z81pkNP5;N`^~4HI+f3SxvKBa!#8Es!I+dpc8DmtLy`#da6(2-4p;(fba61DP6oudb z*Zfs=S$lxA1j&j~g%bzvZp@f0FhD)#IdVYor6KHb<18thYGhIJYp_M^7% zPNPzxTibeFQ!ejpP%(6nRC@(fo;Z`T-MBAnMW;GJSvb92ug_T<7%U$=Q29(QY(^hV z?H4=beg@z<@7D&w%~6xV-~HY645qT)zWnO!#ogY4qmz#e{vw8s@@$hCj zX0(!7sKG6plbC6w;i;vv1hC0EC=F5uA!}&OyEn7ggf@S!WNjV7KH3>WY_)6g`C;f6 zO7>a%Rct~WH3(y8KTxs>dDh(Zy6)2MLR)%yOs2a7(8AeB6R&XywLQH3r@_mNLeHdI z*dER>jO1mJEty=lB3(_wp{Kv#LMDuN1|ez5qd4&!Tfdv_WZt?R)u4_QJ_3KZmK7r; z==5_wo2$T>vuV#!zqXtBK6wA_CJ)?C5fxGoHVn@K7 zpRv0fTLs|3!;3Z>;Pn3#1)w(Txg4O9K)#%kXJ&Hz9dbBFz%Rz5*%hOBK99o;0Lp`P zMjA-E-I6I>g+Eu)@4n9$^!|=j|68K?*cmobk-^&EOMh&;gfO9c|mqf>Ru8?sagJ;`03u z2J?4*gwlpW=PZwx4Edf}D!6#%MfMD^v5?H2l5=hQ_8E$T=SbN9%hU^(f4TBOtDCdp zGAGxijNk{@a6fZw%}1?KfH7*Y|Ju^CmJc2MnH#}jEOiqe5U|5II=qu8=`qj^ov3xV z9F1t2l@6j?obVMp42IVb%rKeiZz82XwLWjW{i;%+@4?$j0iky}8_|=JX(bIT=(QA@ z-GNM$n0R@sDQH3FSAK$#L)eq`8LqI`Xt&R25zpQZrc;)bl?j)@h$Fd^#SK$R^t~JU zA@7tvNjDt7czk^JuUDJJV|3HM9bbR&$8U|tf6Kceo4afJr?2yu#@s6a&w15fr)~WY z-~Yah=!oIeY_wf{j!C6or^9f`&*-y@_3)oCEzfnogYoTZc(S6!AwLESo+Zskf$xWY zmh3`KKcJ(PZD%OWwUV_VA85n)4Do>{!7y-OR8aY^2xH?()ZUp6Lr}MM9d^6BaPjVZ zB`gm^imo6L;MzQLXieM-<`%B9(cNyLCb+?rEslbE4`aZA@l1Z=2@|(inDl*0fWtde zOXh*p7-<>jG&vi@nyvEVt#0NyY|JBl%${3mj`)W#hn&x6D*5*EJp{m-5A<9P-h2D+ zVs{0=GvgkCO4==b7PFZe_UMtP1}1|GjvhJXsNg?8JKrk+7Z3TiJ%Yi}zPHmkVm6uy*1>D>;ZX6vEKMc|7h@s zfB1*PU-^|^8E{$B(AffUUwrY!!C(E=UyWXP;RP>MvcJ8^XOL$I`=gWT$@lr-&;L!> zWHZ~Gqs=($He*D7bhBE0j+e=&V7Cvq>*eR@ix24%)A?|9jv!#BVKbcFu+yKt7MbNy z0id($RtY$n)z=seyS=(joE>xd!4m``ZHxCk=Z3jk6gXE$PA@!0$a_W!UO>6G zb2LJme}%u|KQR>A+xTh#ZKN)5Pv=#PyS}Jy?(-n zqBd{7`MyhA?v?3D?eqDI>bO?`p7UnEPS_8bT4X_WvRJd4IUZgNcbk{#60e{qz6@XG zLsRVhw*#s5BlI)vhtN07$7#d0+BHon?Y-G?@0-2N*xpyo`OdCJ-&3q0trMbPl)J%@xF?vG3Okf!dCYqn&R3JD8MOHdu%F!Sak z9&)~H{9{Mw!WqsZ50sfYb*sR)^0!-$2O$8Q7DT`=+KAsHe7CF<-?GDB1z^FIzOFUP z5j}fc`v%HpOFFX+uJyZfAK?-Oz;BJrn0e-G=y<$4na#FbH|r)l@F!bN0Wgq5QDNBX z=061coSB0UIk)^SgWt!zt1G^A_je%VOmgpx;IG)OetCO4dh%cj3!99V>kk&ld&I|L zvH4fO{jV;eYs2~fn^EKXnGB3~jN*V?r4kYD$D`xLdb_?s8Dmf0(fM>YIs?YI$eU+RfNRb)5AGpIKjuA;I4PjQpse&ub*|X}Ndu)7 z;`Sx69z**h6kON8r$aAIF0%s0c5T1)OOuhgxMSnj)Nga`JuMBev%FxqT8M-+ov41@ zMX=%{-^3TGKLYA*b`K`Ra0PH$xmc9|03ZNKL_t)=xlCp5zC$Y%5l{Msbnlq;OfM0h#Sl0I$SctOw{g@As>*4WLA`vzV{ zO{Aj_-=k@q4=1zZ6ON|*;CM9ppF-&_xt{DVs@-|cFKYRI)^ZJRj@UNJXk)aU94{8D z7uaU<8iN1T-Fo*DQ+2FiI;E$ehk2wC(nzEAViT@bdT8|a=P>-xt>DVPs11G}Zx|-s zqxBK}c1KNxm%!9>0FwCU{&XPi{yYaTVEm*{V#`muNnH16H#mVB|Dq`5tCdHA_+w3? zDqg1Q?0`N;-kB-DM=%{+zx2`cvjhM$I>2@0k&9$l&-b0IyrC}}{WzP>RIo?-jAs*2 zc9DC0Naa8p`MMs&y&~5sd4;(nc`x6I(C3c%6E=zIW@Nu1ZqC4hn;$r8H_0e{!`wD& zj2r*>M>L&xsE$kc(KVy=Cnzx9Q|>E|AwNi=@$K#H&bwtcFCR_U zkC&I*i?cHhJ%#&r(1!7!zV*#-esl2EuYQ#Y07h=J*@hVbq_+Fw+4JaF5-pB8o?^3G z&t(8)@@~hzFJ8iSd&wpM2H?9hSo|3{d>;l?ZNgpy_eu`f_{G{UjGqk`>=-|CHo(~> zPG~R-pdz3G;CTjbf3)vbd!m}{-v|=#C;*IUK0X_KdByZHnO`OpR@FxZ+b z8fH2UT1o@Z=fdy>uW5JvSRq#+-Cp6SWm#Z^e) z8&c|qgd{=Nr}kVR(C>@`@0M`u-*@Ixp30p^b$x&C99hvo7OUk68Dc-}@N795Js7UW z4>=9H{NmBkyI=n1H$R$?0z@0?(LkVV>O>+3WEzq;q3V%oW$80eZR5uIFvBmQ885x43_{%4cS>Jy*9sdb# zr~BRT=SctN`Z4duo9fu{&y9cD(wkB)uhw-}{wW`iIlbUqK8c>P2JDXhtmPw)UO0Q0 zDSZU^&GhK_uaQAdP*@hs47>%rQ}s6}0q-&8_zv&R&GvH=ULw3W0Kc6xU+}l<+vSS+ z#^prTp{&cck zd`_m#^8wk^KM&h57k|2nu#q&}O*nfh7W# zSvXz5JrF2}PT60vXW)iWzZ(Gkh|rHNA0vI3A&~OFCL>xHj_Tonr zR#Z*x(|m$Pe{r|JjD>)`V5e^FFU}4aGjtjg81qnn`uR_=5E?p4oZ2c%GKY#&QSVSm zLoHnltZ)V+dEB&@yclg*fvM^5org_4b;2GVjVb6C(Upv?XG~PF2x3oG(s5bF?@RC@ z7Wf9sZ^Ug;0yqsYI+>lEe0Y5Rt4F*qmh(2pqv1d1^v5wTmYHJHV{1ky zO?b8QwAbgPa~Nck?)|~=*2eEZdM7bNH%||W&5s)K(}4niI>t2m^HV7T7+8NWv6{O= zKKgqYEyIu%WRx#HOa@`wM*aH3E20o&cA)VfUqR|!<^Usi; zU+*2CoV=~jrwDV7na#mj^W{MFkj0bP@$s7J{nc9^d~o}fuY4uv>OWd5zg!{I+acpU zbU(Ku*!Qryy1F{1UYAUj%xBxpV}$;Y^ZR^_o0)gt5mUPewiQBvNuCmx%o?P9@ydCx z?AOQPWkk#v5W?%CAF23h?CUu-Vs0Y15aJ$?P+8PEW;wtU1jL=d3>0qpmH=^SmNYD9 zkzA%yNM)JJFz`L6-)+FE<&`pQk!@ z{AA4dZn|4rW?0Nh23c-}w;TDig>h|PW*ml04LWsd8eYEQ41gnkFY(czwC&iA`t8g- zd=DHyYkv#{uGr{zb;+)76#%aCUEDAOz)N(RaJONozzg9>JtH>~MIe}mQ!bM6Sf3;P zGj4@*hd+u3v%E)FM{LAnN_x$xLBVpx6hG7a!zbW)Bwu_@b)bq+O|uq*vGZ~8-(p+ zjy@c7Ng~(!4bEsON2NfHZvi^v9Wq#-9gWyk$-WFPDtf_X1(ky(?a43AP1qyg`SyH+ z9_c_=kHT;NjQS6Zo9Z@f^JlMO3ryT6vL*|(TQ7&SQRi$VG)Oro z09fc@)(J(mjlh?rBYh?gIr@RZKtl=-p@n2W0iFnNXeHb>>|g_1f6Wq*micV7@TH^X zdSk;ZL{lzm4jsQ8lK;|JuuTukr<A)tJ#aQ_upLp;0HgrdGEdVR=@RIzm=J}p6V~6eXjsK@2$>H*xGaaGHvKL zxV-1#{AT#_oLl}B?0(ee`WB&Y16z#-HyokXR=ZzvI@=wnx}(V}r7+bQpXmT;)W+7d zIbJ%R-M^XifbNmbU2n>!9{~w+jjKoXPAXQW&>D&(2G`B+;OG{533qg&9nNKl)?3!EZ3aSwbJ(KGRk<1-kOLZ|Xat1IKMG&_TJ}J<0&z z^&T9z#hmcpp|9~m~rD1gNCm(W?-+S-D(3$dgv!55nK-0Xl z%{+A7oj*N>Sz|lmdwAh4{yM^UjskFjpETD@JAZ#RJ^mi`dkNh2>B-Sw@^K!ngdg5A z;(wQLwi%C?qxJf4*vfu;xmc{(DZk`cTrSMv*xZg&dfOKtJlJvZ$o4zm`OcbIh@DrF zz8ZE*rtZxru7N-Lqd!`5+TdMI7L1t*7(ZD(`Cxi}FWD2l;}ZHbcuv=Nkv(cpp|40Zu- z9T;3Asr-6`Q}I`{YFa&^q@j=pPF|9mmFcpX;j)%!s}dk8{XpCNEO$^6!59?cJvXt@ z+p!9eZiU0X4Nb0(3}N$=K1ZKaM@UCedSc=?zL`D0kc8S%%ZsxXNfW<9xHb+}pSOhF z6eqwZzRJh}?ZB9R57SL4{ofnQk5Sn3`E}BP@5mddTX~Op@N|Ob{Js5mh5 zV({+&$KIPYOOjj%dYKuK`(0}71r36QW;cdJLqoy<8yTdLS@U5`kMkfKzRV-v^e52x zH8LZAflMPqKS)MtBn$xo%^?A@(dezJYrkvmk@~*l;d%4kx>a2b^m3~@{oY95Jt92( zEPnj>@q>MK1e9*)z0&D-|63k)#1h^o(_Z8M+ZpuA{bqBob?MR`efj{{K6&uf13W*0 z4Fhn_x12J6E4$4hJj8GhpmLCN zIi4e~Uk@qMoE+%u(sr5@PQN*4J$c`#t97TZWSdEd27Dq%r|}gGEs1gyeL!? zc+2?*1CSXHiSo(YoYb*yW&q?DWLhGPAB41NW=|NsyTEPHjzCQ>TFz7Y)B{*C2le*8 z3WT?qBHD-GJG(zRyoXX>Le___`zrx}F&JahU#Wmn0<`b3`o{J=nE={mb|1(<@%|Qv z@wm0mJ^ar8cZS;{mL*#L2mRjQ7R=IpX7T%2ihGAL$4&O%H3x$|2>%2L#cY1(&dqQ9 z#b1n{ccekLT?nV%01pxpfck+#hXBOg`uO9IdwY9({Vqo@wz|V31i%ki+gCxb-x3}@+e$BgF{6LjqU3te0EI+q({Xx(nM zU`%28h_eY0Fa$%unCe1^g4VhVK-m_xxu>l040!fMB%f|^^FoyfTLPp5FG>W2gMeA@ zWy}PesK6jit>9K>!1TO3AhF+;P7oU0ub@i%ft&<{h}XUi)>_eS$Roca)qu+0*{HUosUB6pTK-~b{H zXbKl@UsVsh#W(otzy9kb_6FEBIboT1iPi4;7_))Rio-e>atF)^-JyXM#nxS@ zOP??_yx8TLLqT9ZbLU4FOSv3N%!z@{{?kk!R)TS-@*P7lG5q*4l(G9nVgrLPgW!bD z3CIDF1hWUmtPF%>S!Kd{@Ou&^2Rp>rj2Wy<0hwus0O|ZWR#l1>$H+Ph0|~mw4Pe$k zfuo$4ZbH!jDQ}pZWg~5c2+XtPT*kZNa@4SHJ)WwpZj}n;-}Qp)Y0+v<_}qQr+jsNn zQ@D=Prx6E<%P^?$i6%hc2$y`667s}sAFX=hqsfrdUQyIt9w1!;>{BQ4O}^NN|J-}y z=^ETH0Chh}{EMC5cMj)^tw!hgrP0Cof8aRZf5+I_!jSqFyOR1d*89WC9|!%!NmR`I zwfoPSV=&UdzEmR(LKy`vo$wk1^PC|Rz*s-Sx|qyiw*sS8AQcScK~O(m=ByVBk(dcl zMi#%$#gp+j7MG%Z<6bo}HY13y1#!+?qK;AEAVmEJEk`90)=3-n?4GvreH7ih$2r=i zo`!XdI-_^ZU0+v;B$@IU@AMGSA)dDl3BFr;W~|MMRQDd-0eB()tiL|Eb31$i`Qbjc z`^k5GFU}Tc_D}T&hIbmn?LkuLV}^G1O48z&0f0yZBfvY1huz-Kkp%3qf$SJ9;d_HY z?+&vBRleTG*!{<>B_3hkchukB{s<;x*4^H&?)~Byi#9&{$(M~E{b<_!$xp=28}}9V zz)$!F?4X#w`s%B57Z#C zLNs=+U1lPYm5>nJ^0z;wBlGnNLB z$iQ_4e)6fn%zQiWH+bYgsftV_o);{u#JqXEHswGj0_B^*??5@x$`q zz|A=-@+zcx`~#BtEV>N?P=7UN{t|ywM0Z%=zXa=l1u@@sU~UK@$BuNI0vil~#(#nd z7YN}f9h_;*L-gY>kCe5M`n02CeTdZ~T`yYl$NcP{+h&xkbJiPME1Zh}-v*<70;~Jf zMeEzALgIp`7d29e9IulS15wW-P8E|u03neU_wc^vy4_`jYKV_Q_BBtZE7@?Pyry3^O1 z0pO!H*>7ArOL)i;q{Xazi6L|uWN-!9FQWej=aDx$D(=Ku9E4v*Kxh9U0gy29p@MD^ z00#s-85ed|utnT*zqhlpW3ToFx<>&1yvC^$NW(nq*Vk$s=lt4_{Ig}PW<6c_A|4*V zI`NYTcFwklmrxUsW4S1s(#>;*A7vul_KXW)b(x$lK+}n8SuZf;qej0*eYD*lB7Ycw z2|56t-lu}!65s~HqdhI}xrI-}txbMU-NUqO5PJ>zsO-muf?qk-XPE(;{N?>-$vS_l zwT~j#9rVTS@F)|~A98N|fnm(TPG7!pWA^QDe_NumaTl})K-NM@cKKU1fBMs(E^pkp zF@|w$_4|Wz(V9Fgm|mk0$QJ+hO^81m1Di|+(du9mIg+$yd%?+u75IVGAqh5&LShUU z;az6>QW~}(g#L)!Yz>}_TPKd>F~gjVCV>yw8G^+@3{+_wz&{3kz!T$Ky@B9}!8e{X zc(MydqUO#4ZpCXkF&DoOjcKWVzNh!ot;cg%KPs*Jbwad}8?u;Tf2b5;>*yk$@Lo!= zpl|_vCo(C#ER1F7Azs2ArfN(Lc--9XC;G zLi!XbqE-SuEfS``HD7YVD_910kIh`|-A-d?tF^<~pWE9W8SEH3?`h6F#j_RtS~CFe zywhmE^!n@D^GVTR%g+wye|!h^n-|#DcYQWpyf}izcg=r-cKc>a>7-hjo3$v&R+Whq<)CZB{8^b@%!pG)92i68Y2>IlF}m@I!x7A9>2^2sxsVe2?x z%Dj{r1tK|>i3TE`+9K%fP9BE1H$SOoP#4K5NOnvwQhH?O4CNkmg`9+MN=&-&eLW9a zQj+46&g@G%Np{RPIOcnC3{~i23_b9R^5BWi!D#$#CVDR*d3>RZRjKW*-3PsH`;ddJ zK4OXZ6RNr#@LajCH3M+VM*UssFvE5P<;B!n-Grc0LRI2Z(i zKT+EsfYZ{S=d4l&uAfWGfB24XHG`D`Bq#8!57hN=F2cxj)C;+~0J0QxM6Z|tt6C(| zOi$N3fJLnuXbmT6v=xYG3Tvg6SEofA{8*=^`~pstH?1bbCap69K7<0u@NL#6qDct8 z?@L=$4^65tlwy4@nSYs+!j9M;(LP(R2191~MPrM@T!w~4z~pUV{VhbVjpjaw zgZu+){_oQVBaHR@J&&~yaX7?G@O{JupWMB7Z}FeseYdJb%4GZ8r%+X-ZGN7O9^l)l z6qwICp8nz&zZkO&RLlqS_LZ$G`>eBf5efGf{l(?(R{t7QVvA)T#m1Z=4+EgMSVYI()zNg5H2aqsg;Gu5J9+re4Xcy`f#iky)A zx=7Bmd=BS&=lEJ*)dd{U!~s-1btxtgK%UL`kWLvqpvaW)jgU{{}tN5 zOU|UekD}A!YT4L>K<+J@*ydg=9?$Y_O%Qt;eTF%qw%F$d^KhoASaya|BO!iH$ zubRJDwVSgN698Hv!7@q~(#!%x0-#pTfIze=SvmX1oKA>S-KwP$U>MmGAz2S#mZ&TC zm&mh*&(lU#I`}oH%zWCzw z+u#1Sm~Z2rqaMf%9N)R(NMsE)Er$!v8hUYgxZs>v#s`L)Due+XN-zWRn=Qx4!D3l; zn;hZC7;*~kIPyqi5QAg}0>d7_WytX%%B9i)mkymjb2(#ROgrvm7$ih8tuAk2Aeu2; z6IJj?z(c@-AjEi$u^OYCQjTA4$1P1a=tc2po1_yWyTVB(p13HJqw8fX#uZH@Zh-Kr zz$)Z3MQUy~Dx|v6>`9e;m-GGgde+J9ovoka*%xu-@OdqR3zVjct8J4QVZK3J#3A^~ z22>Q*By;QvK%#;!hX)1Gwap$Mh@D`IAv6H?fw_s_`8jl7YY9N}-n2X3-n)h|3r_fJ zU71Zyu{rbQ#F$2I?1E z`6NY~5?|XV4nm8%2C1CW6Y5IM1u{bkAR(?~;V;elX&)6{`$_TY2+Ms(i1%IhKX`bT zJ^c?M{%B&W2f&q-dN|AlB|hr|ppDCycVd>WQr{Lw_HJ{8D@Ybi@a~7qw%EsCjzsR{ zdkUCLz$eW9e-4KK1m!K{JdKZ*lgSv3@hN$(`{kD=xN*;65BPnZKugFM{mcEmqy3{| zR?OPVhuChKFIn$tVpXv9e=q@O-3fExoC?+L_O3Bbi+M4{Fl5Lx5(*Iz4ek{Klhobz02C%w6Mw5{5Q8Iai9wljN|4T9+N?g zD5WEv^&cN1>c4XjVn5-KmV3lu01)-dy{oiC0HoRCXkLu>sPG2@-Q8uY6{_9A0KgdR z1Org8MZbM((C@v&)a?Ox=a~1wdpy__&EE&d)9Je)^9Lw`E?5&jMjSlH45)|kEHE^a zU+}z_-=sa`J-}Gy)4Opjxd5&}QNOp}etQA)*F^eL9JP)n5a(m00F182s8ja-W!9Pv zASQ#(rAs%c*G0(>1^>w|(khsM`4-0pzszjXaN#&~E3I1x7l-H(ahr;@Lg$_yG9*cJ zsi28CehPEo_P`dF2#aNxw02{OS!xfa(t;g^DPl)K;38n7yI>gs#G{xV&2@-2I0?83 z9UQ2=Vc~;kOrV)xum!-?)Slgy} zO6Hfys))@*_4uj7b=yAgInLCamBQzgqq(}pXkk@H!Ha*stbT=_g=Sot@q0|OONrY1 z^{8mi3vpXzx9pVtvba2%EpC(*2UNA1m(g!{E@|a+`BE_g9`gfGG=Jt2HEw+CVGq`P zn{}T{D5~uMGJOUbIv?1Cf~rg4o#h7yNnApILi7P)fGv2_%r-m5zACpi`XlI1PQzcN z`FuJ33D0Vg^=lP5RY&lbIS4e6h(DFYDb~#{+NskFJ7bSBM6z2@CW zI{mvv8-qU0;s_mqk2zm(qP9Or^E_}{A8z@hAN@$Hu z4FMrc&340X9hAf-TN_j$Pk71$*%Z?(^LRg(NkwcSyQt4k?x|HPrg^Wr>0ltNPJv(o z>WWbw#Cp2LlUTmbX+J8jC4E}?wW6zFs^erVm?F~k6N4mf=NoV-rE#(ny8-NfP!y*^ z_K@c6gop+s(QULseqH^#J8e#v-)lZUX9Rq$7=TRtw?1y{!q&G&$DM1;_OT1zcm*il zh9if2U$*8Db`{WN6rJhM5E*&UPt5w205G8P8dDM~CD3x*oeWz;DV6|ZeE`>`B7^i? z37=Yk_H&|F??bATF!gQwmyvkd8m)a^v>Ac7we^@#YD|n zKlL@$E8cddk71thA@rWicSzm>u`KKT#kee5i2OOJ4-r3yw>$uOa*pc~h3o}e`kE8A zXB~sMXV~PgUg5aix88b-2imwz4>Xonr&p_Lw_22~ayi}~PdQSkg^{X7r=>yA;vU1E zNkTP5-=xI^fwkfa)BVMr1$L{U#{-y!?e?s_gJ{3mpLQE#X8Wuy#CE|MW1UJ6Y@h{y ziIjlf#+-8noQWc{LVAEyizNhS`HK04WgC`JKyDC{0f`nu$pBx8eXM2RgSOTPF!{ht zfVt^4g307K!!=n?=fo93-63gtKpoI>c`qg!WSgPZss1r3&`KU>WS;^5I@+Q+~%s{o73Wvl(d^d?%$ z(;>%tEbqXyS^b%JUn>THuT{MCQDbLPHong==>ye&I+}F;J$9r2l&iHr>1@qstnC3+ z$K#4af8-F66i~qv0AszM!Ee5UE3VoKpd*2#l}p4sk5<7uhsnu7YeYow)iI{(Mik$p zvKFb%5FE<68h20Wb^Y8P!oo;TKa`Fs1TiVCO{Hd7@T^OarU+gUpLvSpZOdX3TGd_* zJ1I_=akuN4=eJ`?xKNjTHKOt|l zabL$CP%3~^<2z1+d;9O-KDd7UI-?wmP%8Kv$@^gxpdsve-+|z2_qtX4jJNP zAdJb(=T~~&?svDgcm4wApwq|N9vPGcI0FFu38WISlqSSdw2nj-Vm#nlQ6@(T6QuN1!7AREf3tDf7mbC$ zO>{lBS zUn>Rx(SP%j2fso58FNFdzi%M`y3EkH662^|Kl31oQ1D~GB#}P=Mqca+AWcAY*rh9m z_ZL1A9NKAkKjv0vGJ}wl_Nn#On>(+tZLGBuMf|}DuFk{-F3UvRH1qh$41|&bkNx$s zyl5uPHIE6}uE@1qG-)pXK@SB91FBK@VNO7n^0!$_Lz~&N+OIf3~HCN1#HegmjhH%b4KrhHSTx` zq9`Teq)X|j=C6?@07u*!qUDx5XZ%6|M_I)KN!VBb;Bs=1>~7?TJ_U=E6Lx$X8HaFZ z{gj%1qwsnHVbY>Ud^V%{Jha^uOhgp7qIb5t4A=KEc%EA;ayt9GjPue?*Gex*T7AF# ziacQSuTK-x4FW!){6St4@LRYez!L;xxdvqcmTZ`_xJXcG7;2kI*)hc9xPw9IGk1Pw zqhBiqp!ZIrIo;y>wmA8z+1NsBzmHkZBKFim>G}4oGec?^9cfR$!08~tXRx1K4*qog zVPBq+8iTbSu|yKyNdUHL`y@M+Uk$%U>#u-rRXMdKzy@sMi7fn2Yw2k&U~!8)&gJS+ zhQ^Xl8w70I8BFqXJf-=lbT|_m*EA`}tj819=vDfgllt6a9bEVnt^dQA4N3epxT5&e z`t6ea(ir7*CyodC4Elp0{_N`?Bl3U9Qh+D$9dn<50Z1QG?w~B2x47L+_6EDH3*$bE zqh_-|0l23>_`wf^|Bc)9z#r5D*ov56>uw*Yl@F!IbpP@uv-$zRam<3j7R=`my_Oz2 zC)+Rq;2adm;7o@M;5my4Dgk;xQ20EFP$7Yqz?-ueT)>gUj(f>SBix!tX4O?PW~xKeC2 z2I;G4sosSqo`cZ>>u4TW15Z`KlJI zK{Mvd>wL)F>126jI$Lyt@B=F{;7I1u#UN1<0Bi$H0+dn!T6a*%A?O}E@>2Gv|4Mw* zU`}O)pyW)=aA$bsxaSI9Y5)qRfkKMkby3e?T`?ZhAM36)W|DD{ie)2C@DXPr+pf#S zasUydBBIO$gm%A(i3}~9Ipa6az4#s04Xh{djS+jg=08H@dH>%1@YV3Y^T{V1+l!f0 zI=x_NB24*$;0ArI`nk7XQ@&;6>h;U91OPFAh?xKHQSy`6AC+bA?_dHR@I8+~z7MLZ zyoW#Mn(ptvJG*>&yBduS_;@eYAMlYk-6U*!;Hi7y)~#EkYu~%}@$C4xywZQ6`1sy? z_e#tMR2UZP_qX0#RI`6{*8rmR*EnYMkJ+ZT%`!kAf^&mOMQ4JdYk8oNF^;MRryQXB zq7}gk$=CkDUSkA7a(m!lXQ$C)M~hYjT@vWB>?KhLNKiI_380SkIM*0OHV_tJ%oT*D zvoqX~vH`6T1}5E&pcCG0CTP020RowwI}2p$!&xA%I=V_XP8l1rek@rU!CAFNRM1+o zO{nwCH=Ls)Aj3Hx`S~Z8vpz1`&4CnMrAHwB1ba~5-w9k3yb~A;Y09ir2P5Gxmu1A~ zDq6bl7wgZBd3(yTB|D7h>oy$LR;PR8czk%Rs%97iTK+>hD|&0UdZvS4D+YkmK%4C; z=6#!u0Rwsq?!3=zw3timgU~DHPxM`75jO~hrhiBPYAFC~MGmek_6p$neTP5>Bz?wt z;-Jrkf|G&20swDH#CZBKfP1vQ&Bv{xc&d}r1cPXTn%R$L)_zRSm&q~P#}|3%a$Cl8 z*a}%HphA;dqLetq>1D3#bE)aiT095(9EP%={*=A`zC&L*Du#SC<*T9}Cv#xlH!%QO z`I9+7#d^Fw9Nrpi4d3Iwhegr;7nTL?^94u4VR3Nz@~iti!=oJ%Prdos^uQn11AqCK zf9aBYtOYa6Y`riZ&WjKXW-F$9d#4ipI*6MA>euiy~ogh(={pNe`-Dn)aFjqxu zu$Wal41^s-|Cs-(!rs4dptV1Efwb<=KzID4%U$d-d;$n#C^0Al795=QK=ZDDkPCna zuwfvc5M}|u+dSfOV$OeD+=})@&d&|y_*p;aKgJItZ*$bM>LV8kaXa(Pgp(4{sPv?k z|BQouk||Zq`KCjDWC-kGw=&5%xUH{bonHn3sp$-Yk2WrRyN3l}B$Yy1XuD6Vd)$Dl zc}%ql?EuBq;_h8ebwayi<2F6;rS!mCZ#C}z$N#wO4mmjj#g$?-Qh{Y8mkeGyV7xwb zz~avt(+?TMeP){s^PnFcZ8Q&n)Qo~+Zd!w(Q2ACdcM!sWNrAFJD+IFMx> zkHj3#eqAm+o|NR%cd?xBkZ?=FlD?5WmfOfaNvB*qK$MC;pdF@jTy~Fm$sUC5;->n+ zkhxT;Bn8Q@2A1Y%csO&!wVLD3;O9U8`H&@`>RI19{M;}AfAcqgQ#AHI*gYK2+FRRY zv3Iz`QO~VD5Yl>Z$SE$(`|bHrvAtY$zcrc`|06(oeKIY&qfvKv%K1;u{6`}{HcJ46 zCmioSfg2DQXK;rCmfs(Es@Lk$2=k=C>}vX*qG`<4Sw>>;)WA@O9fWAe2P9KM&OM6h zalZuZhSQ)o&v0(a#)IRD@)kG1MO8s1z{R>?hY%S?eeyG#6b;vvZ7e{7xQ?>%YEdVN z^+hsPaTfspL9E*#8O3L&xAC2oh){10w^9Bcd@<@@0F zCvl{&^)ylTU*86wQIfEaybLnzK#?$zDT^z=4aCbUt|w7fAW*+r*FP_tRbPDot@*s)vIj( zpL~o>i*^So6{ku5s_J&P+*{sjcK)2P+nF{2~yym z7Q=b(z##e}41NEl$l*vk1-jsuH9Q-I>vDnP0vqu@t1`T!O^C2?;QF{l3bVDt89!OR zb-|79IMXW!MI%vTkJMl4ES&uc5L^KkIG)F+1^I@c7@M;oiX-+BicdMTaKxl_DARLnc3^B-vFfdIGPMq>FQ1x|=>+IITH7SmqC|MUz zMJ-rg^&oWghRCiicec){>iK^9W<2gXRW3bTVi*|sHL&?!k8Qio3BaN)Vau`tPWmHk zJYQtyGK&2d3lP}cLscdqx#JM|PbX=wzx(|Kyl#m}RD8AkUF*l{s*-?Zuh$t5`-45= z+GIcOb;_H-q6?=VQ}8CZ^Oo$!wQH}d+3)ftU3dzEki2o59{6H=;LSJRgtDxhV{xoh z5+czn27~2dGT8$14v&`Y7lFF|5xZlXU5*bzpyf4Y{JR~^HS-CO)N%qMZs35Ss-@Hn z@ici$#M2Tr;jcj9AmEQd-)4WWL{CCnvej0YG7?*J1CtR4EJ6v8atX&+JK+N*ZzoPX zcb>~-&iZ08h-TEXrul0vtZ)pebyaVdOP;-xMe1UKVslg05ht(m)+(B|cE^)Kk(g^F z+-^1JTQVy8_9h_$yahMVR>KTwrT3dMaZxeZDPY?UZ?$&8Q*IGuRcH20n+Sex7yv&g z#6Do5+r#V&)_eGzT?jflf7PY3sCM|kJFR&O<39@y`)OkFPXP0jEd~QuKJ-Th(3yYa z;|ox6aS#1)XZwYmFAAS3K-+S&(>9Pwq?%C>rGfbx#f_VhG<}3wx!X%ahD~$SZ zUJxd!5hSxaV21;Q_Mtu+rfOn#xQ`DN$0h)~V>p2Z;snvBC5iuB+cVhH@K7w;B%LjW z><-YFkT4`1%8a+R#TrmI*PScbMAwp|$es{Z? zaeQZN^=o`*Hl4r5Ail&`zPy;1JwYaB`>=odN`<_VGN3+dLbZ_}x7cX^KMgQPB$p%1 zAOqxNkP@%mYTOcE51W0$TPH5Qu5#L2AHjxmJp2?%x2H9trlhHFdL$bQj^2zjS8z#@ zY(-}T2@!}og5A#LZA2Gwf09$7&)1F}01|#L;xYkoQOr1cN+IqGv%V~5qbCQ1+39_Y5(vfqm(02KihLbfmv2&mLexnWO+CSR zxpeXa_vC7)vz{uJtHv|>?BTqdISEe%vdTF=nuYxb_r-Q^^6VMUTu6N&El^j9UZk9g znj_4TSM5a;J&iUKmn-wOHW(XdZAMq)J)7?NV*vOLT^5q32g@!BoLdm?o%y`_*NpW4 zj=kX5BvH&nd#>H9E+zvf(GT%Ivi}NeTAnMNb_M`63;_4krn9D#cr89P>8JcUddkaX z))P&WrPnDIGnhwlB%b`_hD4;*>@h zfMt8$;agp2`(T&%JEAQuJUz4SxncnLoQ$xF6J4>%Rd$N|u*I&u z{v*+$2dJT{K`LYc^U<_i9%B6O zz_aFBjBU@aU|Sle0hj77VFMU~-Qut-2tGyzX$IV-Bp_z5+6-WMjY|eVB|ix|z=tdk zq7R@-u=PVSKwHl2W9~|taDK|=xPD}&1Rp*Ke_c4^8U0*kJ(P;2=kQrh@^b0*w7St# zUY(zdM;oUKoD-pr)v~jzd<0#h_bKde-EI^IPw`ozsx-kYaeksC;xHt{)DKI6Xj)VJ^pN~0*QQ(N6qWi1muYw79qwxlOSA6S}dtLC*bHf0<)p%?9R^xw09NZqGHM=+% zSJx1y^|80npUjH(1h7-tZ-%&k6o>nup+k7WjDMnSf0*kD`6>XaIg2Veewy*4BN~Gu z4HySdgz8W0=vp69DLKSsHH59ku~|eyRG*45@RB$#q6}Ee#-21R`@Q~xw+2<_b#}Xsj%!nvC@oamikKYw>&9=7s zAFx&`65Qj^^Fi_3mgg zDF6jd!l>H7>1`$_TSd3C(_AjDl&$4XIqQ9+x7_)oHs=w|knuM$gcU~%vm|hEj5yk9 zEAYFx_pmWLVz(hX0a(`rt~g2p1(J!B8v!Xe9%} zvs(L2c2aW;a)hyaAJou%|9IZo)e-^jzrr!xFVq*J5Ml6X%&fVOrNugl%-Z&h9azXFGe-=PvllA|ts@Qrs8NI=>3`|^Wd#k(kN&Cb0c5~jG{^Qm^{`21~ z|K|9wnt$b;p9%NeFaSSk{-lBha80Ihd-|}soWiKhmns4FMvGR-**-9AD0E2l<5Vt> ze?-}ak31pWK`>`<2qca{_-g*rKzC53VN4)g9>C;idSHDN<|6ocphZ#|b7??IcyYl9 z*hIw0VgT1!apw#G0YFxXYIUoGjk@!tPL4X!niWZ0I%BY$mz3KkEbM7OQAXnLy9f&Y z`bQQQbY0eD6?R*naZe*d7#8I`?5EGNK|59~VUt>Hcc$m&3y$v{KWH31xQhbKv~l?G z6RvwmFQ+j>>8rTJgY?+)w}l83YksuMcE6q8$1nqX=oZ{(+^X79Z7PTn>)8Fsr!0Z zyHLZY(brG6ToWgTNDeTP71{BWip zjq-@z0!2xm!Mbyv**}(!X$Q$9FRYn(6gU(CG`g)t!Q^syw3xq+U~}17w!6c|@_m@5 z8d)5W001BWNkl{J9s^5n2#~0cBJgYsx7#3LKl)?Ake;+}>;;6qEIRWpQhXo~B&M+}ag%G+8ZHUj_9CeX%}NB_yH0+AQVyssi2kfT0u*p3 z9k8cr2nAO}kE*0e2xSR^DIlD%f!NQmkM6JcCrpwv>c}!9h`s^SY8n{XlwaW)>-hkQ zsjxR}B7S^;xCP}+(^0@z3#K8u&LUAL%0_qAQJ*?j%SBQnXMW?oQkqqxIzK*ft}l}i z+rhc#j6R=osUih!D6ku_rIdgzggZRwW$d?H!Mb4C@3cDYVh3K%X&E``Tc;ac(BJ1q z0&uE-uK*{9aBHa2z`?cQqFEk;+YDlla0tK&6Te6UVlXq>9Ap74c|@473ZH8I;zaUj z)pt-w+#er2`%nM4`IW7an+xBjxP;rdNOY-jl}EPgXcGG+#DB_Zeroqjin|7-Xrc$z`qE9D^cq@>r?PdZtF8Z zZm!}DG7})(&*|$;<+QVY{cxY|(OBR-`Xz1vjgWMQjuLE8_>?)Z45%`=r8uskOwE5kG&=%dxMNVL{K0r26918wNl*=UpFY68;1WKbF8TZl z2|!DN)0CQlBMP4LqfPOAh*0cA?GU z??L#n=g(UI!T$Zm-u-(pU0T^%HrUb^?Lp)lm(li@5r6@BxVyXi%dM@g+uZROWcZ&= zg8@Lzx$z20Ryr|PUE2;9R4>U%^@e`<~(w;M@oZ5ezTC&Q%kIS>uU zyw7)QvR^V@5uzbMSKz3m5-a3Pe2{^X?!ik32m1}|l5;>=Z|kVveZ76$x#I4mVSD(i zOT%xLgR(rBy*Atb@$$!eE=|YHF6hs5$p91w`^|Z;3*Bj!Fvff{=BE(1USM24vFeGC6S+nYpb`-RuHe*}irMOqHW%pztNCZ@i!js|}` z#&^bAzY0t4^Y`G57B>CeN(f<(OD3QWFBI`Z^baBMM@Rt&WgN~W#H*NUg$c?C#|?yBqNBH4eWji*S+&rti~JYQ5Q5Wgh2T{1}mWO|bHI8KM9 zH~AJQa%xbH1D_hraf2;_)neS9b-U&0XjHtl`&Q#8{9R=C+%NzDP>VIST~6C7$CKsN z`J{M}@AndLu*2kZ3;P*GNXXz=T@Ek|ysUg+DQ5i6`W*qwahs*-b9{ z#0;)Q>jF>q*2`xge{zk}xd3#I4lBU;tTz!c9f4wRtP-A zGwX-bKWQwb=Dd><_1#xBQqJRlZ82KqCtAz5iT5HBzJ#8FNQUFxWu_n!X6dt|ERI-q zr+{ZQsDA-IE2s16mP?(?aH>vKUPnuCx}0*v{4ACLI6nAdyXS@hz}|Le(YpL9+cwas ztzKBp=YI|j`aTo?AzC%pINYb>k()C#|0f)jp~%`@0CNh>SprbobDp<^_|aJ|P)|S? zrXo`?22dufF{qiPOXC1E~>ZDse-$KHV9TE=0AWfeMhgV_(XqAo2wq9W$dpv#^ z{tP?+`wt$%1gQ9@5+Jt!eFARcn*Y{tfXKf+XJ)^L*#Eyk{5id^`4Qs({~LzjBcug0 z@;aY{OlShk!;Cm)6o;AjjfIBdXDeo^*gl3Ep#O#;HjShW2UoLyYqoZ#?A3WjlO1oSB zS*KZE!r=KG_77I<_PELOfNd|Xdu|wjyL%7cDhWS#$IX8QxI{Poc2 zEIjEiX8nFx#)?dUx&Ve9$1(e_!;Vo0esV-sp%_Z5SQ<$u`3Y2V>DsrM+|-$@Cq`Fw zR)HrLUuDX0#DA(61Dy91CUuTwou1E|e8L0-S@Tyu;XM1-PZ9vfG{B3#Qb+(mJlgl4 zu=YQ}WTwV`Dz4~I8sf;)U;%RrjKLAO%@Bag={>5p2G_9WsfbFB^l2cI>nkqT-_>DJl5 zQh+!w0G$9g{>?j1mVU6CO|PabD^;HkpWLEw~pyV65;)r){nVO zu0+zOI~%fs4d9}I6imM-jinNGId6tLNn4MnnveTkwMZc9`(tAGCZ-aJry`8vz#wr-tbL3v6YyEdQNdd>U6hdLxrSZ^JQUnjwHV$R-m$ALw0mwCfEVfJ zL-))sFxbdxm@si}WpBZq@ey4A7hnhlfv>}nKt)s;Jv&~ zyog5zeZg5`ehf=4aCbn;u9#@X9N%VA*ss0nF5m;jo~h z&r;b#jRyc*@YZDyrn+3|*u?DBIX3XA4i3#U>!kr<+T~AyJK&kwZR`?omP@inhJ!FA zW#kLLi2$Z2vECQx#5w8FPWxZrEuXq+a9xm!>_hNNfCBRmn}`N0A&qryVPr zotugxJfnzabY&lc!?xO;>Uz;a*4e0bIF#!p zh|c$S=esYtF0J)a53TV8!71CFM)&Vy^NUl5uv*gtURx;N3e;^328DPY{Qx#Xg(kd* zpB;k?o7lc@;=2eiXXfj~Njn3{LQm4^S>d(li1TsGsXhXSoT{^icUIUdH&go|fvC&H z^1*tYT=vvWh&(R+Ou68JNE7>|<%m?#cMZqAme2_ayi&}$eZrD}6Cbvc#nOpO1LDym z4mqPBIu=Fe(q!vTIC=2}rZUi>#@$)DcxU`zeAL=&O=x>^5pmmd!vO3Td#Gb}+N>Kn zx-m4nWY-JEC6#&cHT*p(tg!*)kf-XnO`%N>d~rR%vf1<) zTGX7)jyXvXBR}oplV-C#Mt=#znVqT1fJy*lq6^miJ=8`9-JLiR{DP%`5(!;8#ZyCE zOin1Tsc%mebZJkt7F&XWVe>+AM>!!{xHta`b>bFXjbm1RO8p#v&%Vjoc)pdherbe0 zy%3jlLIZNFCp+nyf)TV-LzfE>z3edI?=RWFF~T0rxHE3uXxzwemTzz& ze96Z%?f6I`W*JPntjF*H=FM$p_nZRQ>}h_6Y__$!Q{BPL2bAZ72~aWNiMPSoQ)lxoD4)5rks#TS7P3@f$r@+i?3 z8vjkSh1D3IFIZokqv*%wdVgB;M=_AHD8sN#iCG57sExrAFcin2w@0MyfvD7qec0>u zu=rOTvVJ)_I5?O~_&3D=$=`01`}OPriS>gA59Y(+aD4sx^QB_gKaQN55RCJhWKBfe*l8EPTYF_xn!|;DC<^a~KJYdbp2T+XyYXDQwteE-V zZMVDs%y-h@&t0zDAg2Rn|5K2c3Qrri>48lToZkbO%kIo?-n2e2LG0r=@uLUD@|gp-GMl1x&4k>NTeZnbNw&WgnRlgsCL z_DSbf$wisiq}yH+;27ezq|3~4RxQw=EM3UeAVG+ngO8mTAZ%c zMVwq7O)hWGn(c4%jb4W{yF6bMyGUmW*Y;%qW)gqsSS;(X_7##E_rO-1o~4g-{5s%u zG5AvOWFSTk1NzK>sgpmmmk4woZoG``a@F1nfCRl0Cz)T1`!H4rhn{bpWf^DWI%~N1 zKL|fBseYbWYVBaX8_K#D6AGTmcA(L4x|K)!vfXbqx)Zbh$r$24J3uIK z#1a9T{^ge9$uL#AH5kAEY}0mg0hYSO4CWsOgWksw{)g;czWK{v{_@yoW2+xuW#cwI zu<3!v^}r8*_(PWWmvgq2Em{B*2m9Pb)O^elS`*+7kg9fosh!F6_!}tG_SlzB;-YbQ zxE~tqY$b$g*Qy;89dvUFw&WHr7lgEq2@t-N2{xXCf{@+?esOD{%z^kLVMa9rL^pGh z1qjkvu@Z_zsKqiFb0xg9LQLSEU;-lf1e;~FrEgaD2}b;+h8daOq8DDL6%kQFtu}AJ`c5mwBXMFZv zUv>PMW&nQnv!68)C$sI9Q<@u#HbBDKf3wS{N;&|1TnDFv%PS4z)aAx?rse!dc+5{O z|CsD&D%l}__L4bRPm~!6Mj_xs(U+z}8ThOvR;>Lm^i2~*$W3s$#;EuoqVM58b1#sN zB1=#7I|5N1^Nq&r{hx0Lza74Dn;v+&9&m{aSAFoo2aDuSYj+N3mm&VU%=EXRQQi4;*^E>Fn83Q{pUEyC z3(mGa0x~|F-yKz@+({ail#xs-7)XaD%C z3(TRUzhXzKi2%G?D^OJxJUag5q~-JSL0u6e}g{8epFH_ z%Q0ym_50oT(26|(J^j2W+W$m9-(wAPeEat83nvQ8-L&yHJ#c}0paOQr*LQY)2kvSE zvX@{2KoiYTr`MYghJzUdXW5x`8WZ$%j*+bP_qwtyb%gU-;I z(Xw>s37@Dnico z;=cMZgU}oH73c$Lg?Sv&%oZ|2%=(+q@tM{YVFr*ghgCpzCv1}4n*2g%7VQ=X0}j}k zam6F)NE%=nFy>ekxKQqyW&pnRt?hDeZ@i0{?A>a6`_;Pu)`~jaa;Klm1wchljP3D_D zxm2!a>0BeHg->F@Dt?~n$20Jgn2eO6D9i1?UM=_!Wno{dQ|>XU`MRe(-}IY#4x3 zA7~TV^uVY00B{7lT8`0s`iL2Rb2c6yvi3Zr=7OzztsxA6n5~?)8za{Lw?@Y-eM0yT z4r8ZI%v#<03_?`LNnDY1g}_6^)!mWsi2ztT#iAexQ~jVI>fi#=km=}31>W?=N)v2o zg?WrvF#G0xaV}7_x6{RQq~Mc(a|NFCBp8pKqoMQ{<_DSeg9DS^13VL0PoqE;eKf}y zkj&JSu(V>L>1Cb)=Bxl0_GZ<5*Jr{+KyQC^$Jx|{bI&vbaCnGvd9j1If6*_>78;n# z4j+OY87)OHi5X=KOZdHlamAOzTV45+tiF7mP{(uf`q(mUJ)WERT>X55?9=%Av*c&U z{*&%)6YG~So-Z>TwECtgs7iTC1z?N-0+t_R&EJE5yz(+YY!!lOD7ixU4=ZILk!2eL zKNu+4gZSTO$#M@J1NJaiN8kF^w^W4LxJ?gidf@l$0hs`*##%CGpB!^y<}paH$83Ip z;@L4%nq$tVZ)qWj9Reu#E78*)hk8%0cmv=ogb0kA2D0?ZVt=G|$@-_rfcgpv0CB20 zVaN||9`$SwUrNfP7-zWlbI~u`_c`Jj)^2=W+FJHW1uIfm0%A|&EwPfshH@gFmS8`y z@JXlGpL2AhLP7vj3Cn@fR6)vz{hk2%?_9luxl=1Ku{ ze(v+#*_$2;wOC5 z$#6uf=oda5t-v3Oe++ev`?&7UR(%HcabqzXLHx(mr;{1OLG%$sVxH7`3ziHXf_OM7 zvvm)I!PUHnMX`Mth!G}!FU+8~Pd0AT1J8O7u=J^QO_gcYf8vOM*2my8z_Qrk=Mv2K zWni#{slWz@q%osk#JK?)vx!a7o*bx=8xW5SfU~B+K_Cbfg54Tup1$}YFb#Q%@0=t+ z2Uos*S)&_+;c{Gjlihka+9M;E%Vj=q|Li@_Z{Je+i3kN&Un`Nqq1tgGOb#dCyRC;OMi&|TZq6)EL&j|gYPkBJHP}{jm&_D z{LJQU2CvM4`T&%{_an!5l}pHOedOdc2FXe{fI90%YThB0T+cAPipw=~6F)y`0^Uhp zT_`*Yq=zvSWs?Wg*+!iv#MaeJ(i!yxKsEo(7I?$5AHHGifyGjQ41R~Lex5X{=Km1m z$%dij+!}&EIii1NjmgnRjnTpF&;wBMk7a!j#G=u|qS)n~VWW=@05goy)l0+wr`PM> zM>hjQC9R(^+W*g1>&R37j$eE2wF{>KOE+xXrU#zc9zX(cgsI@)vIC$*|7L5}8nYZw zT}2}I;?8jEhwY;Lx4>(!+vMzeF05_`a#(^inV5!oPniN_|G;#!F~EL+O8{C2WQC!@ zLN+s8s(>q(;8ZOQQX2ZkT?hx-fsTn|#8DDM2_*lCGl&AzM?|hJLCPTCaP4F;vLh!4 zqfIA1y7hyERt26Epc(7tmc`4_@+#rvJ_!@%#@6sHF9AJ|{?amHv`6324UJ=VE*CTk zDM3%dgO%Jqxoq^&x#%!y>Q)QPGxffCI2yeOgLa5s)Q7vn*3EWNZSSUkB^TEjLb;Sx{MM|`*T}6ABF)Nm)rf^=kIE@gIGfE z1sRMuX)!YLc*y=2*(G@pp7GB_0{~(Kn^9-_c=-{}`sY1zuDb49Zapsit*+Elu5tnN zG65io3S~chlv$JLpRjo{G0p6v+q~`w-(isPPe&q8W zb(6N~flUv5#vXX{%{Qx$KKf{iUd@=(Ge_-q>mjrLd#!fsLAxxDVE|Z;Q$I)B94yrd zr!_HF^xtxJ1TjvAftUzD^qe4Q3nVQCDh-761&+|qmpCJ(r%Wn|R4$4;?^(`Ed*pd7 z#3jdopYd(@{KyX|NtVG0eF}WE0hEPO#-zz?z zAF=LCKWd#-r=0d;D5UYQ8srz|>z#ho$H&&6e?3eMx9+92d@>TG(;slU%nYVzwR)w) zei-O>9uDR!PWLN03=95CJN~!vKjFLFDVyb;*0OaEKVZH3VY6D?$2ia}i2m=`3iv+oDPyouNmna@N-*6K;C8{4 zPgwXDPJ`&Ph8L!RVH$wznh{V**fQ=M!k1V|K{8Is9aV$6Anz3}`?-vMPSelVDv&eR zEBxW%e4EsIyusSdAAe6#UtDyPYyWZ4kD5g{tR6sIu|*o?KzvOxGx=fDL>y3u zolF;7=m%aHy8BEm0YK237{e%T+_=$a%SCZCp1%Zf`XQs|`|JQ<32Sz#TC~cAhgoo# z18K}&`Ec>UT+>b;kuQ~~KGJ}Y;}CtE3zEmrYG@EJZ54l9#WU&afKb!XGbKLXXaFBg zuKfBPTzQI%JON$cMn?#rH9%+cDz~8ZKbejjhevxb0@KDwt$(%=fnZ`5F&u1#h_Kt~ z9JGiH`--h8@K6!O%HrdJ@AvC{G@^j zh#fWT3}_VH;zOG5&x>l$-MDN=ZxxGbYdDzx88iRaIH_(po{Sp@1jAn*001BWNkl;JU_w*|z>~$?IuxCW#6SsvB1P7= zW^l6)xNge;kbY;_9>M^0_L0mSzVg~D+WCJ1dv#GeZ1Y^39(Xo-Aj20pZUz%@SaquD zqG&G5e)Cp)-u)e?1@25H?Qblr-dsW+GgpX}Gyh7MuR1Y7l8`F?*O$W_PFKO@EuWimWx)`Hc zvioJ82?l68Yx)6VFQI*BXSt!AG;}bY=Jc+OdUeTHt5d@<$o%~*P&PF^`ZL)1X=d{} z3db}yq$}R<+JRq4a&BAcFU)h#a{^7>!uK5K@mLfk$=k+jXPUZhBCLS-Mz9u^aalN$ z$U5<^lFrl(fP)nhIQh*?1d%MIfEqHVgO4+3PD}aD#%L5D^4tKC%lJ>LzW}K{l02nq zIP$g$AxPHrRtjUd2PkZ-QZyzd%$$(U_QI zqkzlqcW=^37r~x-$Y0?)zHz2r?ppX7I?i~J2>AVrZfbIMc5G6<^UXeSnj(cU`>p!? zjXPs9d|ylbUO_HcPane<0IHgfY)4x^`zRmt&uVNu?#I!A;SDeqIzAgcGBX80o2R(? zleJqm&hjkJ0RTVid)y**=@miu!`pr94wlBfHH+|hJb&h?Gnzq>6uJA2LcdH*?^eDgDxjNGLDq>DOSQ_z&-mv#}bcFDQ?;7=aPVz@TDl~vz6vN&Gp!+((I-niP+ z=k&C!+2mc4f}i)9 zF}#7l{*HJ>?EERegw1p!FKX$3dFFUJs*(5k#-wqobrQV z#$ae8Qt$}wr_kymxiwbMEL}C3W4;&ScuJo_PT#pxiOQ;1qhciUalw1u%J0hO^|@AhlJqN(!UMo-?d-_}l*3Si625ZIk86y2?EOn>-u z8RW6!a@g?I7SbVf=XLHtdzVo6I>(y@Vv-x9>TQWU^(mF>YlldGyVGg`e!P2hI!D}` zr5yd?$U{Hw$$Nd}s(9z6%6j)_Z*Bx^yU>-w7Qn3EX4Qv}%Nn#p35{@K zTtfzm>>?ldqfz>kbxev8tZQchn%kVOc$%ti(lU8Y-rg;)`{Nl&;pv9?*Ce7iioa0k zh8+jr)9JI|+tihM(U-UyInSB}99*k3Zfz5e+nb36o@>M?jz~GNUcG3Sv?J@OU%ZiJ?1G(n`aK31Sjh1qhDh|Xpb3K}csAuxO;que zT3k#s5)a}sqn_W9@))DT``JXCBPbMfD;U;D`P5dv|I=Ls9&MY7>yCKczTsRm zc=QFQT{$Oe0FkX|V^5qhdmgK=w^Jh*$u!?0Kh9@gUxb!DZfb`!-Ka_33aWAyg zEpx|xndk4pfT4yfFSwErin6<=dOMH#Yq z`H{1^5vmDLOZT%go6{3L@-uRSH}e;|j>j?k6&ejTcC4qkRjw|8>`R?naFVAtCy{sn zyItSV+|AdtA4H!(8=XbGws50X9xnss6nxGk*?JG1C4a%n!2!B7zG{ z7mo!~ou6gT!@}@o@)Vt~`YBeiga3oe-uP~KxBkt1r7RR}m;EFI>x-+Ww}$O6Qx20Y z(P3WjQxr8)QBnVlBrkx4MW6vvlb)IaBIY7E6fsTI1e4Dv{8RXs9cVz~e8_v@1I+*i zV>$ym+@&NZJ@r@zyZ{;few8NKI|`P^rhNE<8*Ima@Y_!otA`1-wZ!KpAtQrZ3T*Cb z$`p&p9ym~jrxMAEF9lzo8R)fn_qjgKIp0}yavO8FHmsiMo!va=?b^6;dq8g`zTOaa zRrHGBpaHmGv|2gx5pBCP;kY7*W?AIh0Ln7h=My#9qwf~U<#uCarImnPw}r#4wg8W$ z(~^bd9uw11P5<>Cz)$|=QsN6m=Ll8-&gy=}zpVBxNI#z$GbXkFfzmQ840vkQsM#B3 zNf0k!m(-WSOShb7e0#Ti1ZVfZsA=4ZX)-_GRzO79%=13amiEhssvrCYjdZd;fk$^F zT4M;;8?iFd2jj3}SznOIDH*F~H2v}6KK`5=h8)3be`u0qkNW)-+7!>LwDc(Qb$l$H zJMPoI$j0`+BVNv)Z2Kt}2K3mUCLf3!)qRfeYLWcH*_1uG9r6*qke0diZQ$kKn0DO? zKCWloTmbMZ2Qg&XyQjLee*b#+ZqxFrY{=zGB4k~Iuv9L6tn~+l1-JD!M5XSRb5~Rb z9m>X!O50e+)8vF73gehXBYxp!m=?}XtbXM3PkxuAe6bO`(&JCSZ$8)bbd!%zoX&6l zHQ@>$a^!$vF2vt^eGk-WuHFL*O;DJRo1M~dlNLe;vlu#R{8HRm{j(f!PG9HZdVDFL z(4+4&BJZkR+$AaA2I$tKsU5qO*wMTf0H;~Z&@&*Qi?$3)DB}Kn44hcO7H7~ zjh{W_=}l{Q&u&-zKdKjRn|X}hfHZ~!I5cwHkt3*Q32BvI^9knWh2!|%bDL;m+;0(n z^KtBM=buR@0YWGu>*N^z(;`wIs&tN%7-~f@ODy%%zj&M=8TD`{cX<$Xmplz8ODYDogIK?;*JE56hiT=OSZth ze+C#xs$I4}#)+nIJ@!6b_0qIg)`s(@E#V= zpHsIlujj$o(oRn2J#BKFiROFay{%tt_fEOC*Xe#K`>vhgiaa@ZDf z8eZr38$psK1@rXY+Xi}Uxp0&{fBd3~Zi?DIaqj_J>$<_gFq{^J!^(v{<-W zYUJ4Uz6g_4XL<8g%ta!A2bxx!JLuREmF?b@9Z{VdN zaPe$V2D8!2N?=pB#r-u)X%ji`Yol&CxZ8_=e&Y6sHWKl46X_q$KR_RhQ=7bv*|`od z&@4hG{kJcv3#gskHmA{HWRVtgrO{Mzv!`_Z&%OidR+FL-Ys9i7_3julf?)C3@Ac)# z66xe9_u4t7LXMsFd|M_QQv>P0Yzgw~yCZ3$X9)DENl4%A z>JYW3bHD2I;6ezqpWKw2a6E$<;ZRA{r)?*jF^Ut2*s?zSWzb*qs{0C@^4VP0opJlA^#YMQNCEw-@3u=O&fy) zggcR9ou{I-#amSj6)7?w2>AQ2ZUT~7pBe6Y*Ng1%KmiptU%uQDEqQFAi<+AEcRy{( z>ZJA3mpxVjew8Z~_Nj(>@E8{9SHn-LTV>or+$uDVfC+e-hx8R@ofSd&h8`%LcXw$% zcRxfWF5mminX$UOjVDCAjIEr1pW<(hm{r-~nLwB|HJoo)(pNaQObwYHG4^QVfP3{E zNiW&`TRmIgA%P906}6dc5AwmnznR=u#X0iJ&O*mZmE@x5R$INSbEoj6Vf@L008m1& zK^0hJaH(VIRj&SZ3|nict(m7t`I@2(wyBJmUEBtP5Wh)G`rpBPmljD*>en~vLrgth zLr?FF&ikYIBqH$o@~+8u)z+QWjQ*QdO}qf0uQ|vb4zRs31_vMix!23N?E4OX-Fmu+ zyAr}bkM2LZHZ)G>l(gS}Yr(T#BzMT;###YfeKIRD$4J4A-HP@H37ZHOh@~usYiTfk zQysDXLk7RG?m+{&PFzy~dD1eN&2$75mL(DwO%*mW;W=1z5AS{|3A%>slAY0Ugk497 zq^8^FOL30hrCRT=@wtmvUhXi2Qp-*(yIZ`WADWY_v{?JbV(r z)mLe~i8m9!tgb`#?{||84XT|@Qxo|pd}G;YcbPT?MUP8Hb-D5t5o%_fer8DVFlsbk zNIX%g78b^sq?f84u1#<@4?Hs5o4}qNa_rE}0PU$SL?m46~!NqEffuJ``k~Ynp5g|}dp0KpYS zrFDj=zZ>u<#Mj;egXE8~J0cl!EQLan5w&R^jQ+C)e^H(vcYFJQS0w=gFTy98)`utHFM0b zkTvOrdn%z{Ud-5()HeE|99Rh1F2USlhYaVvqa|Hc-2rhk{`N>~s`^-ZKm!v*fB|qn z_RJAL^|^O~{+O9j=&&H&c@E5QDHYNSeN&?j;=)YNVnp4gz;Q-wukB%i9|_5a)^2~C zX%8>+e_(_&sZ?BAH{S1j*W2(b{iMNr*G{0ZHkh!bdky^vh}F*K$myy=R6 zx8{1On%G77%uXYVix3d-D^hjrZKKE?Q2Vw#5$QinKTGBN<2(nPhqNi=^qlz(c#~%{ zwP3!yKJE}9NJ_Y5x6YEb3(jM?nW{dbd@^iBl87)l?%K*ve)Z7Qm&Fvnn9C)RuCEz>L-VRv zYCM9@pBEg+v05!vj~#f8JMkS5>Wn1zDZKYK&H&q%E*JT`&gKeUCY~JItO<^2HzeDyGYIwi*#5B=NAt?jg?#!C;Gq;0FQ!( z=zRG5toiAAZg{1xq}i)nCb6&;eG^*}USpJZ0cjwGf1urPp zEb>!C{W8XPO@y6TyzVUS=+H(en_tX9I_j$+YNsTwv-hA49^fRu@YV4!h8mH}Zl^%@%Djf6lshP4{L=z4LnCe&6<5oh3n$gL%ww5u%cPLvLJ? zHb$4_v-Q>ATONgbMXiUm^Ub6VDXMu)+|XjY`d?da^2Aq4!lLcp(EyK?zszA($BkAq z7(cau-WijOUR124(q2H^7mZi71Ec1ig8q}`&>S|A8P3!?guZeoD#ldt&>H4G4L`|d z6N`#0O2^AyWB`V(J4noY2R{^mj<~Kq3wTwVGh3B*TitN!s|j_d%d=m%8gk=5m&3Sy zZvVCSrjCUk0qySz)%#PqrkBU!1-HS{KIe*`9#k8eqH^bBk3vD=zyy_0EAt1Alqg(x z*R3n*KnbY-f`fdYw{VvUr)OMvYMym^UnjuL)du}LXvkJBE`d=w zm;+JLMhkpjgm|O~D1_hI>AUjW>TyA)O@z|Vo=J-I93uaqUerN1~m>i(q`Td}D-eOway zzSE>?D~@!G)&MWQnOaUa;Es>Uw=VuHNR(MFYOd8E5NQo$UH7BL0}vl>#4C|kF2Ock z&VXXaUA94!LRdGtwY;fDrMaVsn=<#?!Mk*q_UnX10By^nL$vID8!oaSlNfX5?i=vx z3|q53g`&o##xr|D&hx|Hj-(0W=2iChDw~tC5;}pJ66qah3*CdJxi`Lo7dDz4V9##xf=6kI~JyTr?CBHv>gFvirW9R-WemVpaE%9MJY<1QSM1^4SV zjlUghjAW#__SVvD?JCx93lxEr5&XF(KEh@ti@2%^M5+RTwFa+Tu593ND=fV+_0;(R z%v>R2wEA2|QB}boxriMp#SC|+1U*-rlaCTh3Z6y5lG+I7Yv7Ee2I2AP%>sc>l(jB0 zuRgdhmy*tsE&IGoN>E9aU8Gkm1b++Y&iK?6>3v)=Q3Lc8zv%uJ_kiI-NCKD$pg7ZX zK3yX$Dy{L7Xz08nGpX*~u@ozh{qqI(|{VxqhxVNPdWb@-{Sq zH}3+e{hN@(bYX<%;^v+g6X9^lT4)?)gs<*GsJieH49Zl>0x!d=qu37sz^q)AA z76`Q4cz48bGm+`5c+Sjjiq9I>wp=)JI36$H!oQ?ovgiAH6pFe3U8jNsJ1?QWEa`+= z=G<%#m^;LTN-#Dex^RDpCi&ElgHe$pFMLjcU{p))?I_FjYVt4gOf4jSz_N&YoF*cS zd8a-bznD)1!3*_eT*XK#!g`%Vm(cvU|o{X5ts1C zQY0=d^;iyUK^}p?$XR9Ns|1xlYB79gjWQZ35i$lrB(shT>xAOk9`29UGR_1eTaVlr z6}Wr)Ou=s zw(<91>p;()>C!rL=f15cubM`h!VrvfirN%*sUHX1y=j8>&z}$=92 z#jK|}FP_#h7P*0Ue_F3U*Z0ioZDvix|Nm&TKs5iv8BU zpT~_Wm09a{3~$;0TXR+Qs-dwGrevCbOH+ZMl`%T}IxucXX}p&wh4AAiP^PYAA1xs&FH?n~WJ?H||cM zhcZicMHQ6qv6JqdX69`Y&449-r*e_8Q;@|ZmFS-69sVyWM5vYgwm&W`pgKKP{@ah~ z-fh@Mg~ApF=L)3n<^<&6`!@|Bc7zjUeZe^J$FWhJp8}!1C9#E#%NH24Xx=ryf6|l| z9GB;}XUi6f9sYkV0HSm<-44!~I#xCHQnY*vA|-Ei*#dCVVV{wYo3}?T8j%z;%Rza& z&f0)vWvmf@@82ok7vMun$FmT|Ym-1LSC&$8&*7z$vsVQ9D~o#ZqQj7iaHNoU%VgtB z^UVNu#F8?fRfXu!b#Qz4ox}r2oL|4)nDw9Ql5ASrm&)C}Y?fG$U3QI4bF^dj2mL7A$FJtjY#p7<+ zH)ok35d=RAhRz}Xk#oQM7R<)DBpj>ck)dx+1ZDu84x)_lLM3x~hZ2CWfZ43z%SVAh zR*KJl)UZQQ4Cu1wDSM8COfriRO)52VS22_6T6H}^wiRDY@}~=srw;XXiKi+>sL-dS zix5qWr_h(Kwj3n9nQk|k&hTsP73>WtPvk7;7Dw|CjXR|I>sW`x(L_PsB}}x<^Li+Q zgzq4RXJw%=M?y0+!LTT00{w-S0U!}wLhAeaxIbpXk%mThO!Q8U{8U!}DjA`tCha`NYwJRdII48*lT0Eu^NPB0qvt3$w_^owuCu zYJd#1?m@u~+`gkg$>UW1*_)fyWh1=3(}B{XmM2j1`eu-8vIgPb3Y)y_7(*H>?73Zx zuem(fnSfPf-Jk-vPv8Er3RHwO2nrf3CQMx-cvI>iBDrN?DGU`PdUICX`PA6 z_8(bT@+D<=OBh@qeoJ3j0Y1wYYdUqJ{P$Sz%5-d^UiOfU_Fy83i3)L!Oq6~X6};QW z^yuqmQU2G^j-ByDq3O#9*2cvg-~Mm+{%TqcxW%3Z1<-za04go<_GG`uaNx|4lNUEJ zrahJqTyyginDw`lpCJt;E|7k6Dk8@*crV!hUAUDyc$y~G<{9e#OW%n%p!G-1*q!o% z-RIo?z*AA9u#jTuvr3d}btxrR?TPifAc8ZV3Dfew9)pb=5~=$`HmzQqJB^*kbaW$K zp%nE}izD^-cmKU>Wr0D_9aacq8-}mDhQf>2kvaQn9+!tY&du5$w&+|x#`Rm4*4ONJ zSvbF%eG`C>&2+dV*<z-5KkfMmG=x={>^A_yAR;3&A3Wc7Haxi z-fb1Z{MSz_ArWbFH&DFwyXO2DR^Kr;e#;uac))ZDySUW0L7}H9uD>0P7G~1+P}DJ> zN@{x#`;_gVbNnW5bdjv_c{t=}YoOmh_RNF#5?uHQ?>niXM@a9zp|P{PqaILw{vM8v zkxjDnEsP&zj9KdnEWh{=rtBPDjIFC>y}L?uNmujvncIvn(K0(#sC%new2UOTar-&7 zO)2E|LvLd-;;DPPMmE7D_&_IIhiwzQqJ3r@blc9y^*=I1DcmKFTsx89=N{2gXKe)a zImGLlCx(o(tFYP*879>>m}G(TaO?;U!+|1#K#2*&eHXDN6k!DNO_ZE9hFnTM^~Zb` z84Qwk`iaY`=Uqp!a7o`IqQ?zteY^Ph44Z9Wgv!`Xg(T4=N7*P|IgVexBIkJL~>la@NQCVVspRk?&s-rMBK&ls&lx`X^H~6$hiED(ICDk zpXc_YjH~OQ{1+dEv5_3jjgTRNM#ea`dl-FD@2Zs3_vqynV;XS+o&5y9p^M5(CD8-u ziV&S^^10k>&&En;(7*$LDNhpgXh?+WV?)ZU=aV7sKjxT&aHpvm^9Q$RfGmsG6E7uv zhcmNlRd92H0>?KNqe~8i`zHDy1Spsv^YKooxfd$mLqAx;)o+!!Nnv@|p0r>;m6m-q zPsCtQYKLA+kH^@dUf(&>gwTR8G=YWk&9T4UUUr$DYiG00+_ox&uZ2N!~ z$;)5K3p~Yf=-#Q36Z)+gvaE|ALp7_oZWrDgZ`^GV&LN>tH=TU`*VUzGDe|*2>Iwa2 zQ#6Iux71)3)8*#@%pVveyL98VD7t;fvIqMd!yUHQ(hPu67++ywVYx-s}}eyFF6R?9}qwh`12=_VQ%BY5Kvs`f-6+|Gtqx0V^~H` z^#~4yz636|C68vDoL!c)guVL6nL+?6+!t|jdantZu;m4F_v3&kVlEhEx{r! zjm7-iL;14;@*JWS{T_NKq_OLp4BL;RQ~;t+2YH+8FJtQtFXtXN$86)$g@xAAUey&O zL0b{(WnaKB@y@bx1V_^P`*(>IALU)an1?nMv+&{Jg@~d(X9efkUebSirc`dO;NE}o z!N#vpW$`{sQ3e5w;2GQyC?_6)|4QR!rv}N;FT|oBR1kSnNx(ZpDbujVyJYyA7bH@Y zVU=ujnSf|%3#C^fq`&|7Lu7zfE}EWTqR<;TR()?YQ+*0UL)f&FXvYw8XW7uV7mMx7 zo0v^jK3;h;MOq6-T7NW#^2PuKp{yblpaHl@@b7-hEGZ2d_zVKu+V zu=|MK)Zx`@%To6AIWMb?kTvaEzcQ~23N*pqwA7@TJq)wjug8V5DCigS5!*hac5ZCP zfbAAu**_7GRY`$#f(sbLZL$1To^apy-t+zq3Wcc-#SiAPBp_ZkU-4_76cKXl%9vsw zofp-(yM4wKfIIifF9E=@gjkbYY+aN z<)mW%{NiJOvo zi%;fcZ=Pxc^h1A<<())^Rw`32&EUz^3H{96y)k{ZygNk`d5F#hQbZwyFUz|aK7{3O zfx|AvC@*M#QslbU?IsE?503GirkCK1KMF!dUzH!lnuElTMyMiv90H&WLOqE@DX%&V?Q>_;FV7S(o_$mB_P~p)ll2*osxVzx z(!>r-$0;}`beTiFTwDAbfsG$pe4e9-fDfSh3UlCs=v5`^pg6VRYtq5m_ZSzcp)0V2*%W2^99Bb!@w&*?Kh2&<~Xzo7j066 zytxtuBxeua9MKemO*#5_gJV=@iCDyqGwUWa%tm z=;XzJZKxDg!H+qUaV+El?{rf%$a=hq=raG&!R%jw=M>3AFPM+%ZAm2^I~(txR(i}? z&oCeXlcM>}lrc2^-@*F@`j1D%9EHzCm+6FYj^FW{BhaxHa)GH`am!eF4%k!yK!?!H z(i9quW1V+xqO>j~^G)k2@23yCXyMmNe};_kp96V8=fo1BN$g|Vy`vg5U7VjdhUm=Vo2SjzK75|&)1vTlDI3v#>Nw16UO{=onE3fM z+Mj;sbANR_{;O5)Jc+stXhI7+UHE6!rFy*k9b0JmAjlDac>|+JNdT)+rxZV{24f@l z-|UIuqMs=)pmu!Ii#wexDvf%n7u$Q5?p^e&npo&wP+3u_5>)ibRjUK{v-^?WA<_>D zE2LpU1wOM7VU|WX6=LGUV~eA5(lTR&-^*4(TzsLu^-XVH@^;(QP>eX<=3}f}Ux%QJ zLef2z;=~Bomz<_2ungejcHjr^rl?`8axc$E3!X`HVaUsai|g|p5)$hCSeyI(e~MX+ z9001AEW20c+RI^pSWNO*+&gg+x4Wts^m3k04@m3&#`K61m2_I~#Lvr_*$|_#fsZO? zia`$)Y_(1cxtL@2*!>a0r5t4kNdNn8-I_YGX#O`dRNg_1Sx93L}gTz$fLQE50LSI)@ z;jHJUD2EH&-y3s5lbDKemVJ2SPjYg>!1u7sY)2^DxnzRMe^THD{XKRZn> zv4y-A21}7vOa@lw^_XRwlIr-MG~)ApVn%59LumgLr2=YYgttNX)@eGn=O0BKe?qTa$-&AkFrxu2jCw3wqAbZ>Lm*;UK#w<(l%SY}q$2fkl zWWh7bq!V8R{d+d~+C)X0NJLzipB_8~GMapTDhuNVJo;vN8m-!DoFuX2=~Sh2aC!Gy z8RQJQzr(7T6@3VGzWd{1N@B#7rD#zo&yub{drWi8xm4;U^WVM;CTe}>_o-+SAvDZ0t~{^ZR4(S}p|VX2Fp+KeGMdCFCb2>)O(nRp zlz1S>{Z?36d&et`4IvsV|1@58OK{fB$6XC;3)Ik40$L&Xjma&jOS1%grN#VXO{jc1 zBVqTjGq^7NVj)cAjOHL}ne+Yj`qsy@o>=MOQYo3@ZXb5;`)h4kZKFgfU_$iSX6!4# z8+Jdhhr<3l(yt5ccA1pn_0gy41u=95N_U3#+e<($lKlI~W*`az66)r;y14ugQz4qe zm8M}adX%7{3Ri3E9qq$@`xU&{r^KsTV*FL7X@ON`trGJLw-6}a)0}RLFn0H?|0FfJ zv{9;(A9RQbZ{Kt9UYE$+BQvlSN9-W-IK&u>Hl06NtGf<8_eXC*m2cdJ_zlMBd98B? z=TqYD^QI;z#9pcgD_huXkik#3z8ga?6L1hSwJY#yH+A_KTrG*^WItzNp$Pk3^uG&v zmHut#n!*O_)jAW;8jw_(AkenR<$-1KfT5>DT5~rfv_r8=mD^+PwlX2pg8}o(gin-k zJw5WvykUDcjbCFvhv@GJKJlTgp7X(a{srpbHHEEw80>aGH;eyUv95QB43H#rg4-BO zEObB59E~ntZ;Uo~`N0LNcpElT17hKaOAnylTeT#MGd#}5*>-H8;mX*U;nF{rRkubq z<=9%a!%4M*GXY;Zqera+dL0({drdOqwc`RqI{724fVngi=P;yYgo2wF{d>M$ z+|HlW0D4dkJ$HJiaAp;H<))7EaLi!-|3Z{O-x|nDg}+r$l^Wn)_}D-$0SM)f{w&m+ggjn)zDDKN%f5d%BjfG1YNqaA5~u zfx0KSfkmc==hI5aR{mrC*F~N^zKJhxSo{%G#PcXt zlqaHJ7{)O!I1CbCUh`bK`K9>}_u<_sYF^X@oj!r#{A%AWZ_3j*fs7wYdOB=3oWr|6 zUoH~_!;~Jdx4*9$<1S$*6iGmf(@V0w7yETY;S^6lSy3>(OcBz)aME$H;%@C_qhJ<# z=b}ZbU}&$7Os)Uf7TDCW7kxrj{~zA4mI5}wx8JXMJLCGxWcfWEQa>Tnt*AjhS!{Ax zhmG^|+r(RkZje>#HxaGAXQeWYpadyxt`FvIEW4>z=Ycxnq^|#I7KPekkunRavVAeT z7=)%P-7Cpo#@f3*3A#lsHz@4Y*rWc1Zop$+DkcPGq+<$)g!)(TPP&cg>3n7bzsc2U z5;=@e#zX)LmCgDuz|XLdY47_pg@mYe=$ShnM$*B*sIZFBYyg@l?B#lIsm^-LN7NC# zgsn?i?npSsW686-sF>_7s>+*e!%KkQ?ziy$9P%&+p$Zv^D%uru2ra&4aDz7Mw$yjl zT49|UvZR116_&maQ(8mN%)dF0r~n~2{l(Otk1@&K?Bl(HBeytTFQ`{ywEdemb})k> zZvg0T{zu3Ej=CP2l>xw0DI+G2Yp20ISM!3Vh&r8W;nh%yfDM@&8Qd4DQ5tzO9q~WI z`9hnCNQn=XMprFvm(m_lOvd^z%{*@zbEMyhB?$;TaZI%ofzf1Rz_{unH4j zQzn<)pLFYaOB4_{y-}lO17vnpUN3V0i;AAf*;ZAqR@Y-27oXAW_YMD8dEGMuTj2WzC-fy{7kiSXm%%z&uO_;9rtIxN$WznX} z*>cUS<2A7^3=OP_5;nS}{zBGFMO$2}c|;EK*@brg!d^(C

6xvL;Uw=ZAqJu5s!S-&i_ctU1kVD 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 Date: Wed, 28 Oct 2015 19:48:54 -0700 Subject: [PATCH 207/572] Correctly translate mouse events to the right and middle buttons #2975 --- src/lib/platform/OSXScreen.cpp | 21 ++++++++++++++++++--- src/lib/platform/OSXScreen.h | 3 +++ src/lib/synergy/mouse_types.h | 3 +++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index 58a323370..98c80e42e 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -466,9 +466,9 @@ OSXScreen::constructMouseButtonEventMap() { const CGEventType source[NumButtonIDs][3] = { {kCGEventLeftMouseUp, kCGEventLeftMouseDragged, kCGEventLeftMouseDown}, - {kCGEventOtherMouseUp, kCGEventOtherMouseDragged, kCGEventOtherMouseDown}, {kCGEventRightMouseUp, kCGEventRightMouseDragged, kCGEventRightMouseDown}, {kCGEventOtherMouseUp, kCGEventOtherMouseDragged, kCGEventOtherMouseDown}, + {kCGEventOtherMouseUp, kCGEventOtherMouseDragged, kCGEventOtherMouseDown}, {kCGEventOtherMouseUp, kCGEventOtherMouseDragged, kCGEventOtherMouseDown} }; @@ -541,7 +541,7 @@ void OSXScreen::fakeMouseButton(ButtonID id, bool press) { // Buttons are indexed from one, but the button down array is indexed from zero - UInt32 index = id - kButtonLeft; + UInt32 index = mapSynergyButtonToMac(id) - kButtonLeft; if (index >= NumButtonIDs) { return; } @@ -594,7 +594,7 @@ OSXScreen::fakeMouseButton(ButtonID id, bool press) MouseButtonEventMapType thisButtonMap = MouseButtonEventMap[index]; CGEventType type = thisButtonMap[state]; - + CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, index); CGEventSetIntegerValueField(event, kCGMouseEventClickState, m_clickState); @@ -1444,6 +1444,21 @@ OSXScreen::onHotKey(EventRef event) const return true; } +ButtonID +OSXScreen::mapSynergyButtonToMac(UInt16 button) const +{ + switch (button) { + case 1: + return kButtonLeft; + case 2: + return kMacButtonMiddle; + case 3: + return kMacButtonRight; + } + + return static_cast(button); +} + ButtonID OSXScreen::mapMacButtonToSynergy(UInt16 macButton) const { diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h index cd0dc18df..7193dd511 100644 --- a/src/lib/platform/OSXScreen.h +++ b/src/lib/platform/OSXScreen.h @@ -138,6 +138,9 @@ class OSXScreen : public PlatformScreen { void showCursor(); void hideCursor(); + // map mac mouse button to synergy buttons + ButtonID mapSynergyButtonToMac(UInt16) const; + // map mac mouse button to synergy buttons ButtonID mapMacButtonToSynergy(UInt16) const; diff --git a/src/lib/synergy/mouse_types.h b/src/lib/synergy/mouse_types.h index d3d9e1291..2ad79d0fb 100644 --- a/src/lib/synergy/mouse_types.h +++ b/src/lib/synergy/mouse_types.h @@ -33,6 +33,9 @@ static const ButtonID kButtonLeft = 1; static const ButtonID kButtonMiddle = 2; static const ButtonID kButtonRight = 3; static const ButtonID kButtonExtra0 = 4; + +static const ButtonID kMacButtonRight = 2; +static const ButtonID kMacButtonMiddle = 3; //@} static const UInt8 NumButtonIDs = 5; From d93ff2f53fca228f73be3024950bd01a16dd0fb7 Mon Sep 17 00:00:00 2001 From: Asbjorn Kjaer Date: Wed, 28 Oct 2015 19:58:54 -0700 Subject: [PATCH 208/572] Fix comment #2975 --- src/lib/platform/OSXScreen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h index 7193dd511..1a2eb563c 100644 --- a/src/lib/platform/OSXScreen.h +++ b/src/lib/platform/OSXScreen.h @@ -138,7 +138,7 @@ class OSXScreen : public PlatformScreen { void showCursor(); void hideCursor(); - // map mac mouse button to synergy buttons + // map synergy mouse button to mac buttons ButtonID mapSynergyButtonToMac(UInt16) const; // map mac mouse button to synergy buttons From c7e723030b76a2fb845cc7f96d0f480da3e24728 Mon Sep 17 00:00:00 2001 From: Asbjorn Kjaer Date: Wed, 28 Oct 2015 20:05:17 -0700 Subject: [PATCH 209/572] Fix log string to use the re-mapped button instead of the input ID #2975 --- src/lib/platform/OSXScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index 98c80e42e..80ace5858 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -590,7 +590,7 @@ OSXScreen::fakeMouseButton(ButtonID id, bool press) EMouseButtonState state = press ? kMouseButtonDown : kMouseButtonUp; - LOG((CLOG_DEBUG1 "faking mouse button id: %d press: %s", id, press ? "pressed" : "released")); + LOG((CLOG_DEBUG1 "faking mouse button id: %d press: %s", index, press ? "pressed" : "released")); MouseButtonEventMapType thisButtonMap = MouseButtonEventMap[index]; CGEventType type = thisButtonMap[state]; From 48d24d72e6f0d9797cd69c37c8cb8e1a9fdda7d0 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 28 Apr 2016 07:16:40 -0700 Subject: [PATCH 210/572] Proofread internal information after retranslating UI #4836 --- src/gui/src/MainWindow.cpp | 11 ++++++++++- src/gui/src/MainWindow.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index a05210d9a..af72437e2 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -491,6 +491,15 @@ void MainWindow::restartSynergy() startSynergy(); } +void MainWindow::proofreadInfo() +{ + setEdition(m_AppConfig.edition()); + + int oldState = m_SynergyState; + m_SynergyState = synergyDisconnected; + setSynergyState((qSynergyState)oldState); +} + void MainWindow::clearLog() { m_pLogOutput->clear(); @@ -958,7 +967,7 @@ void MainWindow::changeEvent(QEvent* event) retranslateUi(this); retranslateMenuBar(); - setEdition(m_AppConfig.edition()); + proofreadInfo(); break; } diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 9c7aeebc4..da06c2e73 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -178,6 +178,7 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase bool autoHide(); QString getTimeStamp(); void restartSynergy(); + void proofreadInfo(); private: QSettings& m_Settings; From 60b56f9e58bd76b6aed95d449644e2badbbe465d Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 28 Apr 2016 07:25:00 -0700 Subject: [PATCH 211/572] Switch translator back only when it's been changed #4836 --- src/gui/src/SettingsDialog.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index bcaa2263e..fbe81f2af 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -89,7 +89,10 @@ void SettingsDialog::accept() void SettingsDialog::reject() { - QSynergyApplication::getInstance()->switchTranslator(appConfig().language()); + if (appConfig().language() != m_pComboLanguage->itemData(m_pComboLanguage->currentIndex()).toString()) { + QSynergyApplication::getInstance()->switchTranslator(appConfig().language()); + } + QDialog::reject(); } From 6e7adeac852356217f2ad498128f05e5de84a6ac Mon Sep 17 00:00:00 2001 From: Nigel Armstrong Date: Tue, 4 Aug 2015 22:24:04 -0400 Subject: [PATCH 212/572] Added "--enable-crypto" to help #4922 --- src/lib/synergy/App.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/synergy/App.h b/src/lib/synergy/App.h index 2c11a7c52..2ce85d2fa 100644 --- a/src/lib/synergy/App.h +++ b/src/lib/synergy/App.h @@ -163,7 +163,8 @@ class MinimalApp : public App { "* --restart restart the server automatically if it fails.\n" \ " -l --log write log messages to file.\n" \ " --no-tray disable the system tray icon.\n" \ - " --enable-drag-drop enable file drag & drop.\n" + " --enable-drag-drop enable file drag & drop.\n" \ + " --enable-crypto enable the crypto (ssl) plugin.\n" #define HELP_COMMON_INFO_2 \ " -h, --help display this help and exit.\n" \ From 929431f5fc5d16f6c92aa5e1a2108d4280732662 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 19 Apr 2016 15:13:15 +0100 Subject: [PATCH 213/572] Remove support for OSX 10.4 and below #2765 --- src/lib/platform/OSXKeyState.cpp | 113 ++------------------------------------- src/lib/platform/OSXKeyState.h | 6 +-- src/lib/platform/OSXScreen.cpp | 87 +----------------------------- src/lib/platform/OSXScreen.h | 15 +----- 4 files changed, 9 insertions(+), 212 deletions(-) diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 06cc0fda0..47cec76ca 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -20,31 +20,18 @@ #include "arch/Arch.h" #include "base/Log.h" -#if defined(MAC_OS_X_VERSION_10_5) #include -#endif // Note that some virtual keys codes appear more than once. The // first instance of a virtual key code maps to the KeyID that we // want to generate for that code. The others are for mapping // different KeyIDs to a single key code. - -#if defined(MAC_OS_X_VERSION_10_5) static const UInt32 s_shiftVK = kVK_Shift; static const UInt32 s_controlVK = kVK_Control; static const UInt32 s_altVK = kVK_Option; static const UInt32 s_superVK = kVK_Command; static const UInt32 s_capsLockVK = kVK_CapsLock; static const UInt32 s_numLockVK = kVK_ANSI_KeypadClear; // 71 -#else -// Hardcoded virtual key table on 10.4 and below. -static const UInt32 s_shiftVK = 56; -static const UInt32 s_controlVK = 59; -static const UInt32 s_altVK = 58; -static const UInt32 s_superVK = 55; -static const UInt32 s_capsLockVK = 57; -static const UInt32 s_numLockVK = 71; -#endif static const UInt32 s_osxNumLock = 1 << 16; @@ -54,7 +41,6 @@ struct KeyEntry { UInt32 m_virtualKey; }; static const KeyEntry s_controlKeys[] = { -#if defined(MAC_OS_X_VERSION_10_5) // cursor keys. if we don't do this we'll may still get these from // the keyboard resource but they may not correspond to the arrow // keys. @@ -103,56 +89,7 @@ static const KeyEntry s_controlKeys[] = { { kKeyKP_Divide, kVK_ANSI_KeypadDivide }, { kKeyKP_Subtract, kVK_ANSI_KeypadMinus }, { kKeyKP_Enter, kVK_ANSI_KeypadEnter }, -#else - // Hardcoded virtual key table on 10.4 and below. - // cursor keys. - { kKeyLeft, 123 }, - { kKeyRight, 124 }, - { kKeyUp, 126 }, - { kKeyDown, 125 }, - { kKeyHome, 115 }, - { kKeyEnd, 119 }, - { kKeyPageUp, 116 }, - { kKeyPageDown, 121 }, - { kKeyInsert, 114 }, - - // function keys - { kKeyF1, 122 }, - { kKeyF2, 120 }, - { kKeyF3, 99 }, - { kKeyF4, 118 }, - { kKeyF5, 96 }, - { kKeyF6, 97 }, - { kKeyF7, 98 }, - { kKeyF8, 100 }, - { kKeyF9, 101 }, - { kKeyF10, 109 }, - { kKeyF11, 103 }, - { kKeyF12, 111 }, - { kKeyF13, 105 }, - { kKeyF14, 107 }, - { kKeyF15, 113 }, - { kKeyF16, 106 }, - - { kKeyKP_0, 82 }, - { kKeyKP_1, 83 }, - { kKeyKP_2, 84 }, - { kKeyKP_3, 85 }, - { kKeyKP_4, 86 }, - { kKeyKP_5, 87 }, - { kKeyKP_6, 88 }, - { kKeyKP_7, 89 }, - { kKeyKP_8, 91 }, - { kKeyKP_9, 92 }, - { kKeyKP_Decimal, 65 }, - { kKeyKP_Equal, 81 }, - { kKeyKP_Multiply, 67 }, - { kKeyKP_Add, 69 }, - { kKeyKP_Divide, 75 }, - { kKeyKP_Subtract, 78 }, - { kKeyKP_Enter, 76 }, -#endif - + // virtual key 110 is fn+enter and i have no idea what that's supposed // to map to. also the enter key with numlock on is a modifier but i // don't know which. @@ -303,13 +240,8 @@ OSXKeyState::mapKeyFromEvent(KeyIDs& ids, } // get keyboard info - -#if defined(MAC_OS_X_VERSION_10_5) TISInputSourceRef currentKeyboardLayout = TISCopyCurrentKeyboardLayoutInputSource(); -#else - KeyboardLayoutRef currentKeyboardLayout; - OSStatus status = KLGetCurrentKeyboardLayout(¤tKeyboardLayout); -#endif + if (currentKeyboardLayout == NULL) { return kKeyNone; } @@ -343,17 +275,10 @@ OSXKeyState::mapKeyFromEvent(KeyIDs& ids, } // translate via uchr resource -#if defined(MAC_OS_X_VERSION_10_5) CFDataRef ref = (CFDataRef) TISGetInputSourceProperty(currentKeyboardLayout, kTISPropertyUnicodeKeyLayoutData); const UCKeyboardLayout* layout = (const UCKeyboardLayout*) CFDataGetBytePtr(ref); const bool layoutValid = (layout != NULL); -#else - const void* resource; - int err = KLGetKeyboardLayoutProperty(currentKeyboardLayout, kKLuchrData, &resource); - const bool layoutValid = (err == noErr); - const UCKeyboardLayout* layout = (const UCKeyboardLayout*)resource; -#endif if (layoutValid) { // translate key @@ -454,13 +379,7 @@ SInt32 OSXKeyState::pollActiveGroup() const { bool layoutValid = true; -#if defined(MAC_OS_X_VERSION_10_5) TISInputSourceRef keyboardLayout = TISCopyCurrentKeyboardLayoutInputSource(); -#else - KeyboardLayoutRef keyboardLayout; - OSStatus status = KLGetCurrentKeyboardLayout(&keyboardLayout); - layoutValid = (status == noErr); -#endif if (layoutValid) { GroupMap::const_iterator i = m_groupMap.find(keyboardLayout); @@ -508,16 +427,11 @@ OSXKeyState::getKeyMap(synergy::KeyMap& keyMap) // add regular keys // try uchr resource first - #if defined(MAC_OS_X_VERSION_10_5) CFDataRef resourceRef = (CFDataRef)TISGetInputSourceProperty( m_groups[g], kTISPropertyUnicodeKeyLayoutData); layoutValid = resourceRef != NULL; if (layoutValid) resource = CFDataGetBytePtr(resourceRef); - #else - layoutValid = KLGetKeyboardLayoutProperty( - m_groups[g], kKLuchrData, &resource); - #endif if (layoutValid) { CUCHRKeyResource uchr(resource, keyboardType); @@ -862,7 +776,6 @@ OSXKeyState::getGroups(GroupList& groups) const CFIndex n; bool gotLayouts = false; -#if defined(MAC_OS_X_VERSION_10_5) // get number of layouts CFStringRef keys[] = { kTISPropertyInputSourceCategory }; CFStringRef values[] = { kTISCategoryKeyboardInputSource }; @@ -870,10 +783,6 @@ OSXKeyState::getGroups(GroupList& groups) const CFArrayRef kbds = TISCreateInputSourceList(dict, false); n = CFArrayGetCount(kbds); gotLayouts = (n != 0); -#else - OSStatus status = KLGetKeyboardLayoutCount(&n); - gotLayouts = (status == noErr); -#endif if (!gotLayouts) { LOG((CLOG_DEBUG1 "can't get keyboard layouts")); @@ -884,14 +793,9 @@ OSXKeyState::getGroups(GroupList& groups) const groups.clear(); for (CFIndex i = 0; i < n; ++i) { bool addToGroups = true; -#if defined(MAC_OS_X_VERSION_10_5) TISInputSourceRef keyboardLayout = (TISInputSourceRef)CFArrayGetValueAtIndex(kbds, i); -#else - KeyboardLayoutRef keyboardLayout; - status = KLGetKeyboardLayoutAtIndex(i, &keyboardLayout); - addToGroups == (status == noErr); -#endif + if (addToGroups) groups.push_back(keyboardLayout); } @@ -901,11 +805,7 @@ OSXKeyState::getGroups(GroupList& groups) const void OSXKeyState::setGroup(SInt32 group) { -#if defined(MAC_OS_X_VERSION_10_5) TISSetInputMethodKeyboardLayoutOverride(m_groups[group]); -#else - KLSetCurrentKeyboardLayout(m_groups[group]); -#endif } void @@ -1050,14 +950,11 @@ OSXKeyState::KeyResource::getKeyID(UInt8 c) str[0] = static_cast(c); str[1] = 0; -#if defined(MAC_OS_X_VERSION_10_5) // get current keyboard script TISInputSourceRef isref = TISCopyCurrentKeyboardInputSource(); CFArrayRef langs = (CFArrayRef) TISGetInputSourceProperty(isref, kTISPropertyInputSourceLanguages); - CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)CFArrayGetValueAtIndex(langs, 0)); -#else - CFStringEncoding encoding = GetScriptManagerVariable(smKeyScript); -#endif + CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding( + (CFStringRef)CFArrayGetValueAtIndex(langs, 0)); // convert to unicode CFStringRef cfString = CFStringCreateWithCStringNoCopy( diff --git a/src/lib/platform/OSXKeyState.h b/src/lib/platform/OSXKeyState.h index 5385ba3c7..75f16c8bf 100644 --- a/src/lib/platform/OSXKeyState.h +++ b/src/lib/platform/OSXKeyState.h @@ -25,11 +25,7 @@ #include -#if defined(MAC_OS_X_VERSION_10_5) - typedef TISInputSourceRef KeyLayout; -#else - typedef KeyboardLayoutRef KeyLayout; -#endif +typedef TISInputSourceRef KeyLayout; //! OS X key state /*! diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index 80ace5858..a216dc1dc 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -136,15 +136,7 @@ OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCurso } // install display manager notification handler -#if defined(MAC_OS_X_VERSION_10_5) CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallback, this); -#else - m_displayManagerNotificationUPP = - NewDMExtendedNotificationUPP(displayManagerCallback); - OSStatus err = GetCurrentProcess(&m_PSN); - err = DMRegisterExtendedNotifyProc(m_displayManagerNotificationUPP, - this, 0, &m_PSN); -#endif // install fast user switching event handler EventTypeSpec switchEventTypes[2]; @@ -182,24 +174,8 @@ OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCurso if (m_switchEventHandlerRef != 0) { RemoveEventHandler(m_switchEventHandlerRef); } -#if defined(MAC_OS_X_VERSION_10_5) + CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this); -#else - if (m_displayManagerNotificationUPP != NULL) { - DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP, - NULL, &m_PSN, 0); - } - - if (m_hiddenWindow) { - ReleaseWindow(m_hiddenWindow); - m_hiddenWindow = NULL; - } - - if (m_userInputWindow) { - ReleaseWindow(m_userInputWindow); - m_userInputWindow = NULL; - } -#endif delete m_keyState; delete m_screensaver; @@ -245,22 +221,7 @@ OSXScreen::~OSXScreen() RemoveEventHandler(m_switchEventHandlerRef); -#if defined(MAC_OS_X_VERSION_10_5) CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this); -#else - DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP, - NULL, &m_PSN, 0); - - if (m_hiddenWindow) { - ReleaseWindow(m_hiddenWindow); - m_hiddenWindow = NULL; - } - - if (m_userInputWindow) { - ReleaseWindow(m_userInputWindow); - m_userInputWindow = NULL; - } -#endif delete m_keyState; delete m_screensaver; @@ -705,7 +666,6 @@ void OSXScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const { if (xDelta != 0 || yDelta != 0) { -#if defined(MAC_OS_X_VERSION_10_5) // create a scroll event, post it and release it. not sure if kCGScrollEventUnitLine // is the right choice here over kCGScrollEventUnitPixel CGEventRef scrollEvent = CGEventCreateScrollWheelEvent( @@ -719,12 +679,6 @@ OSXScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const CGEventPost(kCGHIDEventTap, scrollEvent); CFRelease(scrollEvent); -#else - - CGPostScrollWheelEvent( - 2, mapScrollWheelFromSynergy(yDelta), - -mapScrollWheelFromSynergy(xDelta)); -#endif } } @@ -1236,43 +1190,7 @@ OSXScreen::handleClipboardCheck(const Event&, void*) checkClipboards(); } -#if !defined(MAC_OS_X_VERSION_10_5) -pascal void -OSXScreen::displayManagerCallback(void* inUserData, SInt16 inMessage, void*) -{ - OSXScreen* screen = (OSXScreen*)inUserData; - - if (inMessage == kDMNotifyEvent) { - screen->onDisplayChange(); - } -} - -bool -OSXScreen::onDisplayChange() -{ - // screen resolution may have changed. save old shape. - SInt32 xOld = m_x, yOld = m_y, wOld = m_w, hOld = m_h; - - // update shape - updateScreenShape(); - - // do nothing if resolution hasn't changed - if (xOld != m_x || yOld != m_y || wOld != m_w || hOld != m_h) { - if (m_isPrimary) { - // warp mouse to center if off screen - if (!m_isOnScreen) { - warpCursor(m_xCenter, m_yCenter); - } - } - - // send new screen info - sendEvent(m_events->forIPrimaryScreen().shapeChanged()); - } - - return true; -} -#else -void +void OSXScreen::displayReconfigurationCallback(CGDirectDisplayID displayID, CGDisplayChangeSummaryFlags flags, void* inUserData) { OSXScreen* screen = (OSXScreen*)inUserData; @@ -1293,7 +1211,6 @@ OSXScreen::displayReconfigurationCallback(CGDirectDisplayID displayID, CGDisplay screen->updateScreenShape(displayID, flags); } } -#endif bool OSXScreen::onKey(CGEventRef event) diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h index 1a2eb563c..5fb8a4039 100644 --- a/src/lib/platform/OSXScreen.h +++ b/src/lib/platform/OSXScreen.h @@ -125,9 +125,6 @@ class OSXScreen : public PlatformScreen { bool onMouseButton(bool pressed, UInt16 macButton); bool onMouseWheel(SInt32 xDelta, SInt32 yDelta) const; - #if !defined(MAC_OS_X_VERSION_10_5) - bool onDisplayChange(); - #endif void constructMouseButtonEventMap(); bool onKey(CGEventRef event); @@ -165,14 +162,10 @@ class OSXScreen : public PlatformScreen { // clipboard check timer handler void handleClipboardCheck(const Event&, void*); -#if defined(MAC_OS_X_VERSION_10_5) // Resolution switch callback static void displayReconfigurationCallback(CGDirectDisplayID, CGDisplayChangeSummaryFlags, void*); -#else - static pascal void displayManagerCallback(void* inUserData, - SInt16 inMessage, void* inNotifyData); -#endif + // fast user switch callback static pascal OSStatus userSwitchCallback(EventHandlerCallRef nextHandler, @@ -305,12 +298,6 @@ class OSXScreen : public PlatformScreen { // does not have focus. WindowRef m_userInputWindow; -#if !defined(MAC_OS_X_VERSION_10_5) - // display manager stuff (to get screen resolution switches). - DMExtendedNotificationUPP m_displayManagerNotificationUPP; - ProcessSerialNumber m_PSN; -#endif - // fast user switching EventHandlerRef m_switchEventHandlerRef; From ed0888880cdc072435b4df07ea5b0f9f96aca9ee Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 20 Apr 2016 16:01:42 +0100 Subject: [PATCH 214/572] Rename CUCHRKeyResource to UchrKeyResource #2765 --- src/lib/platform/OSXKeyState.cpp | 27 ++++++++++++++------------- src/lib/platform/OSXKeyState.h | 4 ++-- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 47cec76ca..bcf65fe10 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -429,12 +429,13 @@ OSXKeyState::getKeyMap(synergy::KeyMap& keyMap) // try uchr resource first CFDataRef resourceRef = (CFDataRef)TISGetInputSourceProperty( m_groups[g], kTISPropertyUnicodeKeyLayoutData); + layoutValid = resourceRef != NULL; if (layoutValid) resource = CFDataGetBytePtr(resourceRef); if (layoutValid) { - CUCHRKeyResource uchr(resource, keyboardType); + UchrKeyResource uchr(resource, keyboardType); if (uchr.isValid()) { LOG((CLOG_DEBUG1 "using uchr resource for group %d", g)); getKeyMap(keyMap, g, uchr); @@ -795,7 +796,7 @@ OSXKeyState::getGroups(GroupList& groups) const bool addToGroups = true; TISInputSourceRef keyboardLayout = (TISInputSourceRef)CFArrayGetValueAtIndex(kbds, i); - + if (addToGroups) groups.push_back(keyboardLayout); } @@ -1026,10 +1027,10 @@ OSXKeyState::KeyResource::unicharToKeyID(UniChar c) // -// OSXKeyState::CUCHRKeyResource +// OSXKeyState::UchrKeyResource // -OSXKeyState::CUCHRKeyResource::CUCHRKeyResource(const void* resource, +OSXKeyState::UchrKeyResource::UchrKeyResource(const void* resource, UInt32 keyboardType) : m_m(NULL), m_cti(NULL), @@ -1099,32 +1100,32 @@ OSXKeyState::CUCHRKeyResource::CUCHRKeyResource(const void* resource, } bool -OSXKeyState::CUCHRKeyResource::isValid() const +OSXKeyState::UchrKeyResource::isValid() const { return (m_m != NULL); } UInt32 -OSXKeyState::CUCHRKeyResource::getNumModifierCombinations() const +OSXKeyState::UchrKeyResource::getNumModifierCombinations() const { // only 32 (not 256) because the righthanded modifier bits are ignored return 32; } UInt32 -OSXKeyState::CUCHRKeyResource::getNumTables() const +OSXKeyState::UchrKeyResource::getNumTables() const { return m_cti->keyToCharTableCount; } UInt32 -OSXKeyState::CUCHRKeyResource::getNumButtons() const +OSXKeyState::UchrKeyResource::getNumButtons() const { return m_cti->keyToCharTableSize; } UInt32 -OSXKeyState::CUCHRKeyResource::getTableForModifier(UInt32 mask) const +OSXKeyState::UchrKeyResource::getTableForModifier(UInt32 mask) const { if (mask >= m_m->modifiersCount) { return m_m->defaultTableNum; @@ -1135,7 +1136,7 @@ OSXKeyState::CUCHRKeyResource::getTableForModifier(UInt32 mask) const } KeyID -OSXKeyState::CUCHRKeyResource::getKey(UInt32 table, UInt32 button) const +OSXKeyState::UchrKeyResource::getKey(UInt32 table, UInt32 button) const { assert(table < getNumTables()); assert(button < getNumButtons()); @@ -1171,7 +1172,7 @@ OSXKeyState::CUCHRKeyResource::getKey(UInt32 table, UInt32 button) const } bool -OSXKeyState::CUCHRKeyResource::getDeadKey( +OSXKeyState::UchrKeyResource::getDeadKey( KeySequence& keys, UInt16 index) const { if (m_sri == NULL || index >= m_sri->keyStateRecordCount) { @@ -1214,7 +1215,7 @@ OSXKeyState::CUCHRKeyResource::getDeadKey( } bool -OSXKeyState::CUCHRKeyResource::getKeyRecord( +OSXKeyState::UchrKeyResource::getKeyRecord( KeySequence& keys, UInt16 index, UInt16& state) const { const UInt8* base = reinterpret_cast(m_resource); @@ -1278,7 +1279,7 @@ OSXKeyState::CUCHRKeyResource::getKeyRecord( } bool -OSXKeyState::CUCHRKeyResource::addSequence( +OSXKeyState::UchrKeyResource::addSequence( KeySequence& keys, UCKeyCharSeq c) const { if ((c & kUCKeyOutputTestForIndexMask) == kUCKeyOutputSequenceIndexMask) { diff --git a/src/lib/platform/OSXKeyState.h b/src/lib/platform/OSXKeyState.h index 75f16c8bf..9b90e2240 100644 --- a/src/lib/platform/OSXKeyState.h +++ b/src/lib/platform/OSXKeyState.h @@ -166,9 +166,9 @@ class OSXKeyState : public KeyState { }; - class CUCHRKeyResource : public KeyResource { + class UchrKeyResource : public KeyResource { public: - CUCHRKeyResource(const void*, UInt32 keyboardType); + UchrKeyResource(const void*, UInt32 keyboardType); // KeyResource overrides virtual bool isValid() const; From 7f786cc8848bd7ccd3a48c5d76c56924d7f17a42 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 21 Apr 2016 13:59:47 +0100 Subject: [PATCH 215/572] Refactor KeyResource into own files #2765 --- src/gui/src/CommandProcess.h | 1 + src/lib/platform/CMakeLists.txt | 4 +- src/lib/platform/IKeyResource.cpp | 193 +++++++++++++++ src/lib/platform/IKeyResource.h | 36 +++ src/lib/platform/OSXKeyState.cpp | 456 +---------------------------------- src/lib/platform/OSXKeyState.h | 50 +--- src/lib/platform/UchrKeyResource.cpp | 296 +++++++++++++++++++++++ src/lib/platform/UchrKeyResource.h | 55 +++++ 8 files changed, 588 insertions(+), 503 deletions(-) create mode 100644 src/lib/platform/IKeyResource.cpp create mode 100644 src/lib/platform/IKeyResource.h create mode 100644 src/lib/platform/UchrKeyResource.cpp create mode 100644 src/lib/platform/UchrKeyResource.h diff --git a/src/gui/src/CommandProcess.h b/src/gui/src/CommandProcess.h index 508552daf..ff6c16126 100644 --- a/src/gui/src/CommandProcess.h +++ b/src/gui/src/CommandProcess.h @@ -18,6 +18,7 @@ #ifndef COMMANDTHREAD_H #define COMMANDTHREAD_H +#include #include class CommandProcess : public QObject diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index b5c747b72..e5497e8ec 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -18,8 +18,8 @@ if (WIN32) file(GLOB headers "MSWindows*.h") file(GLOB sources "MSWindows*.cpp") elseif (APPLE) - file(GLOB headers "OSX*.h") - file(GLOB sources "OSX*.cpp" "OSX*.m") + file(GLOB headers "OSX*.h" "*KeyResource.h") + file(GLOB sources "OSX*.cpp" "OSX*.m" "*KeyResource.cpp") elseif (UNIX) file(GLOB headers "XWindows*.h") file(GLOB sources "XWindows*.cpp") diff --git a/src/lib/platform/IKeyResource.cpp b/src/lib/platform/IKeyResource.cpp new file mode 100644 index 000000000..81e86a930 --- /dev/null +++ b/src/lib/platform/IKeyResource.cpp @@ -0,0 +1,193 @@ +/* + * 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 "platform/IKeyResource.h" + +#include + +// +// OSXKeyState::KeyResource +// + +KeyID +IKeyResource::getKeyID(UInt8 c) +{ + if (c == 0) { + return kKeyNone; + } + else if (c >= 32 && c < 127) { + // ASCII + return static_cast(c); + } + else { + // handle special keys + switch (c) { + case 0x01: + return kKeyHome; + + case 0x02: + return kKeyKP_Enter; + + case 0x03: + return kKeyKP_Enter; + + case 0x04: + return kKeyEnd; + + case 0x05: + return kKeyHelp; + + case 0x08: + return kKeyBackSpace; + + case 0x09: + return kKeyTab; + + case 0x0b: + return kKeyPageUp; + + case 0x0c: + return kKeyPageDown; + + case 0x0d: + return kKeyReturn; + + case 0x10: + // OS X maps all the function keys (F1, etc) to this one key. + // we can't determine the right key here so we have to do it + // some other way. + return kKeyNone; + + case 0x1b: + return kKeyEscape; + + case 0x1c: + return kKeyLeft; + + case 0x1d: + return kKeyRight; + + case 0x1e: + return kKeyUp; + + case 0x1f: + return kKeyDown; + + case 0x7f: + return kKeyDelete; + + case 0x06: + case 0x07: + case 0x0a: + case 0x0e: + case 0x0f: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + // discard other control characters + return kKeyNone; + + default: + // not special or unknown + break; + } + + // create string with character + char str[2]; + str[0] = static_cast(c); + str[1] = 0; + + // get current keyboard script + TISInputSourceRef isref = TISCopyCurrentKeyboardInputSource(); + CFArrayRef langs = (CFArrayRef) TISGetInputSourceProperty(isref, kTISPropertyInputSourceLanguages); + CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding( + (CFStringRef)CFArrayGetValueAtIndex(langs, 0)); + // convert to unicode + CFStringRef cfString = + CFStringCreateWithCStringNoCopy( + kCFAllocatorDefault, str, encoding, kCFAllocatorNull); + + // sometimes CFStringCreate...() returns NULL (e.g. Apple Korean + // encoding with char value 214). if it did then make no key, + // otherwise CFStringCreateMutableCopy() will crash. + if (cfString == NULL) { + return kKeyNone; + } + + // convert to precomposed + CFMutableStringRef mcfString = + CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString); + CFRelease(cfString); + CFStringNormalize(mcfString, kCFStringNormalizationFormC); + + // check result + int unicodeLength = CFStringGetLength(mcfString); + if (unicodeLength == 0) { + CFRelease(mcfString); + return kKeyNone; + } + if (unicodeLength > 1) { + // FIXME -- more than one character, we should handle this + CFRelease(mcfString); + return kKeyNone; + } + + // get unicode character + UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0); + CFRelease(mcfString); + + // convert to KeyID + return static_cast(uc); + } +} + +KeyID +IKeyResource::unicharToKeyID(UniChar c) +{ + switch (c) { + case 3: + return kKeyKP_Enter; + + case 8: + return kKeyBackSpace; + + case 9: + return kKeyTab; + + case 13: + return kKeyReturn; + + case 27: + return kKeyEscape; + + case 127: + return kKeyDelete; + + default: + if (c < 32) { + return kKeyNone; + } + return static_cast(c); + } +} diff --git a/src/lib/platform/IKeyResource.h b/src/lib/platform/IKeyResource.h new file mode 100644 index 000000000..cc71cf391 --- /dev/null +++ b/src/lib/platform/IKeyResource.h @@ -0,0 +1,36 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "synergy/KeyState.h" + +class IKeyResource : public IInterface { +public: + virtual bool isValid() const = 0; + virtual UInt32 getNumModifierCombinations() const = 0; + virtual UInt32 getNumTables() const = 0; + virtual UInt32 getNumButtons() const = 0; + virtual UInt32 getTableForModifier(UInt32 mask) const = 0; + virtual KeyID getKey(UInt32 table, UInt32 button) const = 0; + + // Convert a character in the current script to the equivalent KeyID + static KeyID getKeyID(UInt8); + + // Convert a unicode character to the equivalent KeyID. + static KeyID unicharToKeyID(UniChar); +}; diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index bcf65fe10..dbba0e09e 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -17,6 +17,7 @@ */ #include "platform/OSXKeyState.h" +#include "platform/UchrKeyResource.h" #include "arch/Arch.h" #include "base/Log.h" @@ -296,7 +297,7 @@ OSXKeyState::mapKeyFromEvent(KeyIDs& ids, if (count != 0 || m_deadKeyState == 0) { m_deadKeyState = 0; for (UniCharCount i = 0; i < count; ++i) { - ids.push_back(KeyResource::unicharToKeyID(chars[i])); + ids.push_back(IKeyResource::unicharToKeyID(chars[i])); } adjustAltGrModifier(ids, maskOut, isCommand); return mapVirtualKeyToKeyButton(vkCode); @@ -576,7 +577,7 @@ OSXKeyState::getKeyMapForSpecialKeys(synergy::KeyMap& keyMap, SInt32 group) cons bool OSXKeyState::getKeyMap(synergy::KeyMap& keyMap, - SInt32 group, const KeyResource& r) const + SInt32 group, const IKeyResource& r) const { if (!r.isValid()) { return false; @@ -850,454 +851,3 @@ OSXKeyState::mapKeyButtonToVirtualKey(KeyButton keyButton) { return static_cast(keyButton - KeyButtonOffset); } - - -// -// OSXKeyState::KeyResource -// - -KeyID -OSXKeyState::KeyResource::getKeyID(UInt8 c) -{ - if (c == 0) { - return kKeyNone; - } - else if (c >= 32 && c < 127) { - // ASCII - return static_cast(c); - } - else { - // handle special keys - switch (c) { - case 0x01: - return kKeyHome; - - case 0x02: - return kKeyKP_Enter; - - case 0x03: - return kKeyKP_Enter; - - case 0x04: - return kKeyEnd; - - case 0x05: - return kKeyHelp; - - case 0x08: - return kKeyBackSpace; - - case 0x09: - return kKeyTab; - - case 0x0b: - return kKeyPageUp; - - case 0x0c: - return kKeyPageDown; - - case 0x0d: - return kKeyReturn; - - case 0x10: - // OS X maps all the function keys (F1, etc) to this one key. - // we can't determine the right key here so we have to do it - // some other way. - return kKeyNone; - - case 0x1b: - return kKeyEscape; - - case 0x1c: - return kKeyLeft; - - case 0x1d: - return kKeyRight; - - case 0x1e: - return kKeyUp; - - case 0x1f: - return kKeyDown; - - case 0x7f: - return kKeyDelete; - - case 0x06: - case 0x07: - case 0x0a: - case 0x0e: - case 0x0f: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - // discard other control characters - return kKeyNone; - - default: - // not special or unknown - break; - } - - // create string with character - char str[2]; - str[0] = static_cast(c); - str[1] = 0; - - // get current keyboard script - TISInputSourceRef isref = TISCopyCurrentKeyboardInputSource(); - CFArrayRef langs = (CFArrayRef) TISGetInputSourceProperty(isref, kTISPropertyInputSourceLanguages); - CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding( - (CFStringRef)CFArrayGetValueAtIndex(langs, 0)); - // convert to unicode - CFStringRef cfString = - CFStringCreateWithCStringNoCopy( - kCFAllocatorDefault, str, encoding, kCFAllocatorNull); - - // sometimes CFStringCreate...() returns NULL (e.g. Apple Korean - // encoding with char value 214). if it did then make no key, - // otherwise CFStringCreateMutableCopy() will crash. - if (cfString == NULL) { - return kKeyNone; - } - - // convert to precomposed - CFMutableStringRef mcfString = - CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString); - CFRelease(cfString); - CFStringNormalize(mcfString, kCFStringNormalizationFormC); - - // check result - int unicodeLength = CFStringGetLength(mcfString); - if (unicodeLength == 0) { - CFRelease(mcfString); - return kKeyNone; - } - if (unicodeLength > 1) { - // FIXME -- more than one character, we should handle this - CFRelease(mcfString); - return kKeyNone; - } - - // get unicode character - UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0); - CFRelease(mcfString); - - // convert to KeyID - return static_cast(uc); - } -} - -KeyID -OSXKeyState::KeyResource::unicharToKeyID(UniChar c) -{ - switch (c) { - case 3: - return kKeyKP_Enter; - - case 8: - return kKeyBackSpace; - - case 9: - return kKeyTab; - - case 13: - return kKeyReturn; - - case 27: - return kKeyEscape; - - case 127: - return kKeyDelete; - - default: - if (c < 32) { - return kKeyNone; - } - return static_cast(c); - } -} - - -// -// OSXKeyState::UchrKeyResource -// - -OSXKeyState::UchrKeyResource::UchrKeyResource(const void* resource, - UInt32 keyboardType) : - m_m(NULL), - m_cti(NULL), - m_sdi(NULL), - m_sri(NULL), - m_st(NULL) -{ - m_resource = reinterpret_cast(resource); - if (m_resource == NULL) { - return; - } - - // find the keyboard info for the current keyboard type - const UCKeyboardTypeHeader* th = NULL; - const UCKeyboardLayout* r = m_resource; - for (ItemCount i = 0; i < r->keyboardTypeCount; ++i) { - if (keyboardType >= r->keyboardTypeList[i].keyboardTypeFirst && - keyboardType <= r->keyboardTypeList[i].keyboardTypeLast) { - th = r->keyboardTypeList + i; - break; - } - if (r->keyboardTypeList[i].keyboardTypeFirst == 0) { - // found the default. use it unless we find a match. - th = r->keyboardTypeList + i; - } - } - if (th == NULL) { - // cannot find a suitable keyboard type - return; - } - - // get tables for keyboard type - const UInt8* base = reinterpret_cast(m_resource); - m_m = reinterpret_cast(base + - th->keyModifiersToTableNumOffset); - m_cti = reinterpret_cast(base + - th->keyToCharTableIndexOffset); - m_sdi = reinterpret_cast(base + - th->keySequenceDataIndexOffset); - if (th->keyStateRecordsIndexOffset != 0) { - m_sri = reinterpret_cast(base + - th->keyStateRecordsIndexOffset); - } - if (th->keyStateTerminatorsOffset != 0) { - m_st = reinterpret_cast(base + - th->keyStateTerminatorsOffset); - } - - // find the space key, but only if it can combine with dead keys. - // a dead key followed by a space yields the non-dead version of - // the dead key. - m_spaceOutput = 0xffffu; - UInt32 table = getTableForModifier(0); - for (UInt32 button = 0, n = getNumButtons(); button < n; ++button) { - KeyID id = getKey(table, button); - if (id == 0x20) { - UCKeyOutput c = - reinterpret_cast(base + - m_cti->keyToCharTableOffsets[table])[button]; - if ((c & kUCKeyOutputTestForIndexMask) == - kUCKeyOutputStateIndexMask) { - m_spaceOutput = (c & kUCKeyOutputGetIndexMask); - break; - } - } - } -} - -bool -OSXKeyState::UchrKeyResource::isValid() const -{ - return (m_m != NULL); -} - -UInt32 -OSXKeyState::UchrKeyResource::getNumModifierCombinations() const -{ - // only 32 (not 256) because the righthanded modifier bits are ignored - return 32; -} - -UInt32 -OSXKeyState::UchrKeyResource::getNumTables() const -{ - return m_cti->keyToCharTableCount; -} - -UInt32 -OSXKeyState::UchrKeyResource::getNumButtons() const -{ - return m_cti->keyToCharTableSize; -} - -UInt32 -OSXKeyState::UchrKeyResource::getTableForModifier(UInt32 mask) const -{ - if (mask >= m_m->modifiersCount) { - return m_m->defaultTableNum; - } - else { - return m_m->tableNum[mask]; - } -} - -KeyID -OSXKeyState::UchrKeyResource::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 + - m_cti->keyToCharTableOffsets[table]); - - const UCKeyOutput c = cPtr[button]; - - KeySequence keys; - switch (c & kUCKeyOutputTestForIndexMask) { - case kUCKeyOutputStateIndexMask: - if (!getDeadKey(keys, c & kUCKeyOutputGetIndexMask)) { - return kKeyNone; - } - break; - - case kUCKeyOutputSequenceIndexMask: - default: - if (!addSequence(keys, c)) { - return kKeyNone; - } - break; - } - - // XXX -- no support for multiple characters - if (keys.size() != 1) { - return kKeyNone; - } - - return keys.front(); -} - -bool -OSXKeyState::UchrKeyResource::getDeadKey( - KeySequence& keys, UInt16 index) const -{ - if (m_sri == NULL || index >= m_sri->keyStateRecordCount) { - // XXX -- should we be using some other fallback? - return false; - } - - UInt16 state = 0; - if (!getKeyRecord(keys, index, state)) { - return false; - } - if (state == 0) { - // not a dead key - return true; - } - - // no dead keys if we couldn't find the space key - if (m_spaceOutput == 0xffffu) { - return false; - } - - // the dead key should not have put anything in the key list - if (!keys.empty()) { - return false; - } - - // get the character generated by pressing the space key after the - // dead key. if we're still in a compose state afterwards then we're - // confused so we bail. - if (!getKeyRecord(keys, m_spaceOutput, state) || state != 0) { - return false; - } - - // convert keys to their dead counterparts - for (KeySequence::iterator i = keys.begin(); i != keys.end(); ++i) { - *i = synergy::KeyMap::getDeadKey(*i); - } - - return true; -} - -bool -OSXKeyState::UchrKeyResource::getKeyRecord( - KeySequence& keys, UInt16 index, UInt16& state) const -{ - const UInt8* base = reinterpret_cast(m_resource); - const UCKeyStateRecord* sr = - reinterpret_cast(base + - m_sri->keyStateRecordOffsets[index]); - const UCKeyStateEntryTerminal* kset = - reinterpret_cast(sr->stateEntryData); - - UInt16 nextState = 0; - bool found = false; - if (state == 0) { - found = true; - nextState = sr->stateZeroNextState; - if (!addSequence(keys, sr->stateZeroCharData)) { - return false; - } - } - else { - // we have a next entry - switch (sr->stateEntryFormat) { - case kUCKeyStateEntryTerminalFormat: - for (UInt16 j = 0; j < sr->stateEntryCount; ++j) { - if (kset[j].curState == state) { - if (!addSequence(keys, kset[j].charData)) { - return false; - } - nextState = 0; - found = true; - break; - } - } - break; - - case kUCKeyStateEntryRangeFormat: - // XXX -- not supported yet - break; - - default: - // XXX -- unknown format - return false; - } - } - if (!found) { - // use a terminator - if (m_st != NULL && state < m_st->keyStateTerminatorCount) { - if (!addSequence(keys, m_st->keyStateTerminators[state - 1])) { - return false; - } - } - nextState = sr->stateZeroNextState; - if (!addSequence(keys, sr->stateZeroCharData)) { - return false; - } - } - - // next - state = nextState; - - return true; -} - -bool -OSXKeyState::UchrKeyResource::addSequence( - KeySequence& keys, UCKeyCharSeq c) const -{ - if ((c & kUCKeyOutputTestForIndexMask) == kUCKeyOutputSequenceIndexMask) { - UInt16 index = (c & kUCKeyOutputGetIndexMask); - if (index < m_sdi->charSequenceCount && - m_sdi->charSequenceOffsets[index] != - m_sdi->charSequenceOffsets[index + 1]) { - // XXX -- sequences not supported yet - return false; - } - } - - if (c != 0xfffe && c != 0xffff) { - KeyID id = unicharToKeyID(c); - if (id != kKeyNone) { - keys.push_back(id); - } - } - - return true; -} diff --git a/src/lib/platform/OSXKeyState.h b/src/lib/platform/OSXKeyState.h index 9b90e2240..8eb591187 100644 --- a/src/lib/platform/OSXKeyState.h +++ b/src/lib/platform/OSXKeyState.h @@ -26,6 +26,7 @@ #include typedef TISInputSourceRef KeyLayout; +class IKeyResource; //! OS X key state /*! @@ -112,7 +113,7 @@ class OSXKeyState : public KeyState { // Convert keyboard resource to a key map bool getKeyMap(synergy::KeyMap& keyMap, - SInt32 group, const KeyResource& r) const; + SInt32 group, const IKeyResource& r) const; // Get the available keyboard groups bool getGroups(GroupList&) const; @@ -149,53 +150,6 @@ class OSXKeyState : public KeyState { void init(); private: - class KeyResource : public IInterface { - public: - virtual bool isValid() const = 0; - virtual UInt32 getNumModifierCombinations() const = 0; - virtual UInt32 getNumTables() const = 0; - virtual UInt32 getNumButtons() const = 0; - virtual UInt32 getTableForModifier(UInt32 mask) const = 0; - virtual KeyID getKey(UInt32 table, UInt32 button) const = 0; - - // Convert a character in the current script to the equivalent KeyID - static KeyID getKeyID(UInt8); - - // Convert a unicode character to the equivalent KeyID. - static KeyID unicharToKeyID(UniChar); - }; - - - class UchrKeyResource : public KeyResource { - public: - UchrKeyResource(const void*, UInt32 keyboardType); - - // KeyResource overrides - virtual bool isValid() const; - virtual UInt32 getNumModifierCombinations() const; - virtual UInt32 getNumTables() const; - virtual UInt32 getNumButtons() const; - virtual UInt32 getTableForModifier(UInt32 mask) const; - virtual KeyID getKey(UInt32 table, UInt32 button) const; - - private: - typedef std::vector KeySequence; - - bool getDeadKey(KeySequence& keys, UInt16 index) const; - bool getKeyRecord(KeySequence& keys, - UInt16 index, UInt16& state) const; - bool addSequence(KeySequence& keys, UCKeyCharSeq c) const; - - private: - const UCKeyboardLayout* m_resource; - const UCKeyModifiersToTableNum* m_m; - const UCKeyToCharTableIndex* m_cti; - const UCKeySequenceDataIndex* m_sdi; - const UCKeyStateRecordsIndex* m_sri; - const UCKeyStateTerminators* m_st; - UInt16 m_spaceOutput; - }; - // OS X uses a physical key if 0 for the 'A' key. synergy reserves // KeyButton 0 so we offset all OS X physical key ids by this much // when used as a KeyButton and by minus this much to map a KeyButton diff --git a/src/lib/platform/UchrKeyResource.cpp b/src/lib/platform/UchrKeyResource.cpp new file mode 100644 index 000000000..21480988a --- /dev/null +++ b/src/lib/platform/UchrKeyResource.cpp @@ -0,0 +1,296 @@ +/* + * 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 "platform/UchrKeyResource.h" + +#include + +// +// OSXKeyState::UchrKeyResource +// + +UchrKeyResource::UchrKeyResource(const void* resource, + UInt32 keyboardType) : + m_m(NULL), + m_cti(NULL), + m_sdi(NULL), + m_sri(NULL), + m_st(NULL) +{ + m_resource = reinterpret_cast(resource); + if (m_resource == NULL) { + return; + } + + // find the keyboard info for the current keyboard type + const UCKeyboardTypeHeader* th = NULL; + const UCKeyboardLayout* r = m_resource; + for (ItemCount i = 0; i < r->keyboardTypeCount; ++i) { + if (keyboardType >= r->keyboardTypeList[i].keyboardTypeFirst && + keyboardType <= r->keyboardTypeList[i].keyboardTypeLast) { + th = r->keyboardTypeList + i; + break; + } + if (r->keyboardTypeList[i].keyboardTypeFirst == 0) { + // found the default. use it unless we find a match. + th = r->keyboardTypeList + i; + } + } + if (th == NULL) { + // cannot find a suitable keyboard type + return; + } + + // get tables for keyboard type + const UInt8* base = reinterpret_cast(m_resource); + m_m = reinterpret_cast(base + + th->keyModifiersToTableNumOffset); + m_cti = reinterpret_cast(base + + th->keyToCharTableIndexOffset); + m_sdi = reinterpret_cast(base + + th->keySequenceDataIndexOffset); + if (th->keyStateRecordsIndexOffset != 0) { + m_sri = reinterpret_cast(base + + th->keyStateRecordsIndexOffset); + } + if (th->keyStateTerminatorsOffset != 0) { + m_st = reinterpret_cast(base + + th->keyStateTerminatorsOffset); + } + + // find the space key, but only if it can combine with dead keys. + // a dead key followed by a space yields the non-dead version of + // the dead key. + m_spaceOutput = 0xffffu; + UInt32 table = getTableForModifier(0); + for (UInt32 button = 0, n = getNumButtons(); button < n; ++button) { + KeyID id = getKey(table, button); + if (id == 0x20) { + UCKeyOutput c = + reinterpret_cast(base + + m_cti->keyToCharTableOffsets[table])[button]; + if ((c & kUCKeyOutputTestForIndexMask) == + kUCKeyOutputStateIndexMask) { + m_spaceOutput = (c & kUCKeyOutputGetIndexMask); + break; + } + } + } +} + +bool +UchrKeyResource::isValid() const +{ + return (m_m != NULL); +} + +UInt32 +UchrKeyResource::getNumModifierCombinations() const +{ + // only 32 (not 256) because the righthanded modifier bits are ignored + return 32; +} + +UInt32 +UchrKeyResource::getNumTables() const +{ + return m_cti->keyToCharTableCount; +} + +UInt32 +UchrKeyResource::getNumButtons() const +{ + return m_cti->keyToCharTableSize; +} + +UInt32 +UchrKeyResource::getTableForModifier(UInt32 mask) const +{ + if (mask >= m_m->modifiersCount) { + return m_m->defaultTableNum; + } + else { + return m_m->tableNum[mask]; + } +} + +KeyID +UchrKeyResource::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 + + m_cti->keyToCharTableOffsets[table]); + + const UCKeyOutput c = cPtr[button]; + + KeySequence keys; + switch (c & kUCKeyOutputTestForIndexMask) { + case kUCKeyOutputStateIndexMask: + if (!getDeadKey(keys, c & kUCKeyOutputGetIndexMask)) { + return kKeyNone; + } + break; + + case kUCKeyOutputSequenceIndexMask: + default: + if (!addSequence(keys, c)) { + return kKeyNone; + } + break; + } + + // XXX -- no support for multiple characters + if (keys.size() != 1) { + return kKeyNone; + } + + return keys.front(); +} + +bool +UchrKeyResource::getDeadKey( + KeySequence& keys, UInt16 index) const +{ + if (m_sri == NULL || index >= m_sri->keyStateRecordCount) { + // XXX -- should we be using some other fallback? + return false; + } + + UInt16 state = 0; + if (!getKeyRecord(keys, index, state)) { + return false; + } + if (state == 0) { + // not a dead key + return true; + } + + // no dead keys if we couldn't find the space key + if (m_spaceOutput == 0xffffu) { + return false; + } + + // the dead key should not have put anything in the key list + if (!keys.empty()) { + return false; + } + + // get the character generated by pressing the space key after the + // dead key. if we're still in a compose state afterwards then we're + // confused so we bail. + if (!getKeyRecord(keys, m_spaceOutput, state) || state != 0) { + return false; + } + + // convert keys to their dead counterparts + for (KeySequence::iterator i = keys.begin(); i != keys.end(); ++i) { + *i = synergy::KeyMap::getDeadKey(*i); + } + + return true; +} + +bool +UchrKeyResource::getKeyRecord( + KeySequence& keys, UInt16 index, UInt16& state) const +{ + const UInt8* base = reinterpret_cast(m_resource); + const UCKeyStateRecord* sr = + reinterpret_cast(base + + m_sri->keyStateRecordOffsets[index]); + const UCKeyStateEntryTerminal* kset = + reinterpret_cast(sr->stateEntryData); + + UInt16 nextState = 0; + bool found = false; + if (state == 0) { + found = true; + nextState = sr->stateZeroNextState; + if (!addSequence(keys, sr->stateZeroCharData)) { + return false; + } + } + else { + // we have a next entry + switch (sr->stateEntryFormat) { + case kUCKeyStateEntryTerminalFormat: + for (UInt16 j = 0; j < sr->stateEntryCount; ++j) { + if (kset[j].curState == state) { + if (!addSequence(keys, kset[j].charData)) { + return false; + } + nextState = 0; + found = true; + break; + } + } + break; + + case kUCKeyStateEntryRangeFormat: + // XXX -- not supported yet + break; + + default: + // XXX -- unknown format + return false; + } + } + if (!found) { + // use a terminator + if (m_st != NULL && state < m_st->keyStateTerminatorCount) { + if (!addSequence(keys, m_st->keyStateTerminators[state - 1])) { + return false; + } + } + nextState = sr->stateZeroNextState; + if (!addSequence(keys, sr->stateZeroCharData)) { + return false; + } + } + + // next + state = nextState; + + return true; +} + +bool +UchrKeyResource::addSequence( + KeySequence& keys, UCKeyCharSeq c) const +{ + if ((c & kUCKeyOutputTestForIndexMask) == kUCKeyOutputSequenceIndexMask) { + UInt16 index = (c & kUCKeyOutputGetIndexMask); + if (index < m_sdi->charSequenceCount && + m_sdi->charSequenceOffsets[index] != + m_sdi->charSequenceOffsets[index + 1]) { + // XXX -- sequences not supported yet + return false; + } + } + + if (c != 0xfffe && c != 0xffff) { + KeyID id = unicharToKeyID(c); + if (id != kKeyNone) { + keys.push_back(id); + } + } + + return true; +} diff --git a/src/lib/platform/UchrKeyResource.h b/src/lib/platform/UchrKeyResource.h new file mode 100644 index 000000000..67107ee59 --- /dev/null +++ b/src/lib/platform/UchrKeyResource.h @@ -0,0 +1,55 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "synergy/KeyState.h" +#include "platform/IKeyResource.h" + +#include + +typedef TISInputSourceRef KeyLayout; + +class UchrKeyResource : public IKeyResource { +public: + UchrKeyResource(const void*, UInt32 keyboardType); + + // KeyResource overrides + virtual bool isValid() const; + virtual UInt32 getNumModifierCombinations() const; + virtual UInt32 getNumTables() const; + virtual UInt32 getNumButtons() const; + virtual UInt32 getTableForModifier(UInt32 mask) const; + virtual KeyID getKey(UInt32 table, UInt32 button) const; + +private: + typedef std::vector KeySequence; + + bool getDeadKey(KeySequence& keys, UInt16 index) const; + bool getKeyRecord(KeySequence& keys, + UInt16 index, UInt16& state) const; + bool addSequence(KeySequence& keys, UCKeyCharSeq c) const; + +private: + const UCKeyboardLayout* m_resource; + const UCKeyModifiersToTableNum* m_m; + const UCKeyToCharTableIndex* m_cti; + const UCKeySequenceDataIndex* m_sdi; + const UCKeyStateRecordsIndex* m_sri; + const UCKeyStateTerminators* m_st; + UInt16 m_spaceOutput; +}; From 7bf9ca44ae577ba728ac17ebd62591577e25bd87 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 21 Apr 2016 14:04:20 +0100 Subject: [PATCH 216/572] Check list in a reverse order #2765 --- src/lib/synergy/KeyMap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/synergy/KeyMap.cpp b/src/lib/synergy/KeyMap.cpp index 1fff2c589..8833c2b58 100644 --- a/src/lib/synergy/KeyMap.cpp +++ b/src/lib/synergy/KeyMap.cpp @@ -692,7 +692,7 @@ KeyMap::findBestKey(const KeyEntryList& entryList, KeyModifierMask desiredState) const { // check for an item that can accommodate the desiredState exactly - for (SInt32 i = 0; i < (SInt32)entryList.size(); ++i) { + for (SInt32 i = (SInt32)entryList.size() - 1; i >= 0; --i) { const KeyItem& item = entryList[i].back(); if ((item.m_required & desiredState) == (item.m_sensitive & desiredState)) { @@ -704,7 +704,7 @@ KeyMap::findBestKey(const KeyEntryList& entryList, // choose the item that requires the fewest modifier changes SInt32 bestCount = 32; SInt32 bestIndex = -1; - for (SInt32 i = 0; i < (SInt32)entryList.size(); ++i) { + for (SInt32 i = (SInt32)entryList.size() - 1; i >= 0; --i) { const KeyItem& item = entryList[i].back(); KeyModifierMask change = ((item.m_required ^ desiredState) & item.m_sensitive); From 583ac1ed2d736aedcf9f80299188f9163839675b Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 5 May 2016 14:02:16 +0100 Subject: [PATCH 217/572] Made index 1 based in log #2765 --- src/lib/synergy/KeyMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/synergy/KeyMap.cpp b/src/lib/synergy/KeyMap.cpp index 8833c2b58..68b5d1c37 100644 --- a/src/lib/synergy/KeyMap.cpp +++ b/src/lib/synergy/KeyMap.cpp @@ -696,7 +696,7 @@ KeyMap::findBestKey(const KeyEntryList& entryList, const KeyItem& item = entryList[i].back(); if ((item.m_required & desiredState) == (item.m_sensitive & desiredState)) { - LOG((CLOG_DEBUG1 "best key index %d of %d (exact)", i, entryList.size())); + LOG((CLOG_DEBUG1 "best key index %d of %d (exact)", i + 1, entryList.size())); return i; } } From 6d93a28c2ebc5fa79a67223e6f7354676cd09b1f Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 4 May 2016 14:25:25 +0100 Subject: [PATCH 218/572] Used input source ID as the key in group map #2765 --- src/lib/platform/OSXKeyState.cpp | 18 +++++++++++------- src/lib/platform/OSXKeyState.h | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index dbba0e09e..8aa8163ac 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -379,15 +379,17 @@ OSXKeyState::pollActiveModifiers() const SInt32 OSXKeyState::pollActiveGroup() const { - bool layoutValid = true; TISInputSourceRef keyboardLayout = TISCopyCurrentKeyboardLayoutInputSource(); + CFDataRef id = (CFDataRef)TISGetInputSourceProperty( + keyboardLayout, kTISPropertyInputSourceID); - if (layoutValid) { - GroupMap::const_iterator i = m_groupMap.find(keyboardLayout); - if (i != m_groupMap.end()) { - return i->second; - } + GroupMap::const_iterator i = m_groupMap.find(id); + if (i != m_groupMap.end()) { + return i->second; } + + LOG((CLOG_DEBUG "can't get the active group, use the first group instead")); + return 0; } @@ -414,7 +416,9 @@ OSXKeyState::getKeyMap(synergy::KeyMap& keyMap) m_groupMap.clear(); SInt32 numGroups = (SInt32)m_groups.size(); for (SInt32 g = 0; g < numGroups; ++g) { - m_groupMap[m_groups[g]] = g; + CFDataRef id = (CFDataRef)TISGetInputSourceProperty( + m_groups[g], kTISPropertyInputSourceID); + m_groupMap[id] = g; } } diff --git a/src/lib/platform/OSXKeyState.h b/src/lib/platform/OSXKeyState.h index 8eb591187..b5cdbc1f6 100644 --- a/src/lib/platform/OSXKeyState.h +++ b/src/lib/platform/OSXKeyState.h @@ -158,7 +158,7 @@ class OSXKeyState : public KeyState { KeyButtonOffset = 1 }; - typedef std::map GroupMap; + typedef std::map GroupMap; typedef std::map VirtualKeyMap; VirtualKeyMap m_virtualKeyMap; From 6d2040b698c406332d72774cb89181dd95eef496 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 5 May 2016 14:22:31 +0100 Subject: [PATCH 219/572] Reorganised KeyResource #2765 --- src/lib/platform/CMakeLists.txt | 4 ++-- src/lib/platform/OSXKeyState.cpp | 4 ++-- ...{UchrKeyResource.cpp => OSXUchrKeyResource.cpp} | 24 +++++++++++----------- .../{UchrKeyResource.h => OSXUchrKeyResource.h} | 6 +++--- src/lib/{platform => synergy}/IKeyResource.cpp | 2 +- src/lib/{platform => synergy}/IKeyResource.h | 0 6 files changed, 20 insertions(+), 20 deletions(-) rename src/lib/platform/{UchrKeyResource.cpp => OSXUchrKeyResource.cpp} (92%) rename src/lib/platform/{UchrKeyResource.h => OSXUchrKeyResource.h} (92%) rename src/lib/{platform => synergy}/IKeyResource.cpp (99%) rename src/lib/{platform => synergy}/IKeyResource.h (100%) diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index e5497e8ec..b5c747b72 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -18,8 +18,8 @@ if (WIN32) file(GLOB headers "MSWindows*.h") file(GLOB sources "MSWindows*.cpp") elseif (APPLE) - file(GLOB headers "OSX*.h" "*KeyResource.h") - file(GLOB sources "OSX*.cpp" "OSX*.m" "*KeyResource.cpp") + file(GLOB headers "OSX*.h") + file(GLOB sources "OSX*.cpp" "OSX*.m") elseif (UNIX) file(GLOB headers "XWindows*.h") file(GLOB sources "XWindows*.cpp") diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 8aa8163ac..10491f5f3 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -17,7 +17,7 @@ */ #include "platform/OSXKeyState.h" -#include "platform/UchrKeyResource.h" +#include "platform/OSXUchrKeyResource.h" #include "arch/Arch.h" #include "base/Log.h" @@ -440,7 +440,7 @@ OSXKeyState::getKeyMap(synergy::KeyMap& keyMap) resource = CFDataGetBytePtr(resourceRef); if (layoutValid) { - UchrKeyResource uchr(resource, keyboardType); + OSXUchrKeyResource uchr(resource, keyboardType); if (uchr.isValid()) { LOG((CLOG_DEBUG1 "using uchr resource for group %d", g)); getKeyMap(keyMap, g, uchr); diff --git a/src/lib/platform/UchrKeyResource.cpp b/src/lib/platform/OSXUchrKeyResource.cpp similarity index 92% rename from src/lib/platform/UchrKeyResource.cpp rename to src/lib/platform/OSXUchrKeyResource.cpp index 21480988a..7c362d709 100644 --- a/src/lib/platform/UchrKeyResource.cpp +++ b/src/lib/platform/OSXUchrKeyResource.cpp @@ -15,15 +15,15 @@ * along with this program. If not, see . */ -#include "platform/UchrKeyResource.h" +#include "platform/OSXUchrKeyResource.h" #include // -// OSXKeyState::UchrKeyResource +// OSXUchrKeyResource // -UchrKeyResource::UchrKeyResource(const void* resource, +OSXUchrKeyResource::OSXUchrKeyResource(const void* resource, UInt32 keyboardType) : m_m(NULL), m_cti(NULL), @@ -93,32 +93,32 @@ UchrKeyResource::UchrKeyResource(const void* resource, } bool -UchrKeyResource::isValid() const +OSXUchrKeyResource::isValid() const { return (m_m != NULL); } UInt32 -UchrKeyResource::getNumModifierCombinations() const +OSXUchrKeyResource::getNumModifierCombinations() const { // only 32 (not 256) because the righthanded modifier bits are ignored return 32; } UInt32 -UchrKeyResource::getNumTables() const +OSXUchrKeyResource::getNumTables() const { return m_cti->keyToCharTableCount; } UInt32 -UchrKeyResource::getNumButtons() const +OSXUchrKeyResource::getNumButtons() const { return m_cti->keyToCharTableSize; } UInt32 -UchrKeyResource::getTableForModifier(UInt32 mask) const +OSXUchrKeyResource::getTableForModifier(UInt32 mask) const { if (mask >= m_m->modifiersCount) { return m_m->defaultTableNum; @@ -129,7 +129,7 @@ UchrKeyResource::getTableForModifier(UInt32 mask) const } KeyID -UchrKeyResource::getKey(UInt32 table, UInt32 button) const +OSXUchrKeyResource::getKey(UInt32 table, UInt32 button) const { assert(table < getNumTables()); assert(button < getNumButtons()); @@ -165,7 +165,7 @@ UchrKeyResource::getKey(UInt32 table, UInt32 button) const } bool -UchrKeyResource::getDeadKey( +OSXUchrKeyResource::getDeadKey( KeySequence& keys, UInt16 index) const { if (m_sri == NULL || index >= m_sri->keyStateRecordCount) { @@ -208,7 +208,7 @@ UchrKeyResource::getDeadKey( } bool -UchrKeyResource::getKeyRecord( +OSXUchrKeyResource::getKeyRecord( KeySequence& keys, UInt16 index, UInt16& state) const { const UInt8* base = reinterpret_cast(m_resource); @@ -272,7 +272,7 @@ UchrKeyResource::getKeyRecord( } bool -UchrKeyResource::addSequence( +OSXUchrKeyResource::addSequence( KeySequence& keys, UCKeyCharSeq c) const { if ((c & kUCKeyOutputTestForIndexMask) == kUCKeyOutputSequenceIndexMask) { diff --git a/src/lib/platform/UchrKeyResource.h b/src/lib/platform/OSXUchrKeyResource.h similarity index 92% rename from src/lib/platform/UchrKeyResource.h rename to src/lib/platform/OSXUchrKeyResource.h index 67107ee59..316bd5740 100644 --- a/src/lib/platform/UchrKeyResource.h +++ b/src/lib/platform/OSXUchrKeyResource.h @@ -18,15 +18,15 @@ #pragma once #include "synergy/KeyState.h" -#include "platform/IKeyResource.h" +#include "synergy/IKeyResource.h" #include typedef TISInputSourceRef KeyLayout; -class UchrKeyResource : public IKeyResource { +class OSXUchrKeyResource : public IKeyResource { public: - UchrKeyResource(const void*, UInt32 keyboardType); + OSXUchrKeyResource(const void*, UInt32 keyboardType); // KeyResource overrides virtual bool isValid() const; diff --git a/src/lib/platform/IKeyResource.cpp b/src/lib/synergy/IKeyResource.cpp similarity index 99% rename from src/lib/platform/IKeyResource.cpp rename to src/lib/synergy/IKeyResource.cpp index 81e86a930..a692c776e 100644 --- a/src/lib/platform/IKeyResource.cpp +++ b/src/lib/synergy/IKeyResource.cpp @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -#include "platform/IKeyResource.h" +#include "synergy/IKeyResource.h" #include diff --git a/src/lib/platform/IKeyResource.h b/src/lib/synergy/IKeyResource.h similarity index 100% rename from src/lib/platform/IKeyResource.h rename to src/lib/synergy/IKeyResource.h From 8003c4a2ad534efd07847b0bff1daf3147b4ab56 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 5 May 2016 14:31:49 +0100 Subject: [PATCH 220/572] Made IKeyResource OSX specific #2765 --- src/lib/{synergy/IKeyResource.cpp => platform/OSXIKeyResource.cpp} | 2 +- src/lib/{synergy/IKeyResource.h => platform/OSXIKeyResource.h} | 0 src/lib/platform/OSXUchrKeyResource.h | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/lib/{synergy/IKeyResource.cpp => platform/OSXIKeyResource.cpp} (99%) rename src/lib/{synergy/IKeyResource.h => platform/OSXIKeyResource.h} (100%) diff --git a/src/lib/synergy/IKeyResource.cpp b/src/lib/platform/OSXIKeyResource.cpp similarity index 99% rename from src/lib/synergy/IKeyResource.cpp rename to src/lib/platform/OSXIKeyResource.cpp index a692c776e..c6b5a954f 100644 --- a/src/lib/synergy/IKeyResource.cpp +++ b/src/lib/platform/OSXIKeyResource.cpp @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -#include "synergy/IKeyResource.h" +#include "platform/OSXIKeyResource.h" #include diff --git a/src/lib/synergy/IKeyResource.h b/src/lib/platform/OSXIKeyResource.h similarity index 100% rename from src/lib/synergy/IKeyResource.h rename to src/lib/platform/OSXIKeyResource.h diff --git a/src/lib/platform/OSXUchrKeyResource.h b/src/lib/platform/OSXUchrKeyResource.h index 316bd5740..bffee6079 100644 --- a/src/lib/platform/OSXUchrKeyResource.h +++ b/src/lib/platform/OSXUchrKeyResource.h @@ -18,7 +18,7 @@ #pragma once #include "synergy/KeyState.h" -#include "synergy/IKeyResource.h" +#include "platform/OSXIKeyResource.h" #include From 47d6d1c5bc92ae2f2eecf5edd69a71f9a1bf7569 Mon Sep 17 00:00:00 2001 From: Will Tinsdeall Date: Fri, 2 Oct 2015 10:34:15 +0100 Subject: [PATCH 221/572] Improved grammar in connection notification dialog #4894 --- src/gui/src/MainWindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index af72437e2..727f2dfb9 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -415,8 +415,8 @@ void MainWindow::checkConnected(const QString& line) if (!appConfig().startedBefore() && isVisible()) { QMessageBox::information( this, "Synergy", - tr("Synergy is now connected, You can close the " - "config window. Synergy will remain connected in " + tr("Synergy is now connected. You can close the " + "config window and Synergy will remain connected in " "the background.")); appConfig().setStartedBefore(true); From 17c35f53f48b4853e64a8cc1abea8e24626dab33 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 5 May 2016 15:24:16 +0100 Subject: [PATCH 222/572] Applied name convention to KeyResource #2765 --- src/lib/platform/CMakeLists.txt | 4 ++-- src/lib/platform/{OSXIKeyResource.cpp => IOSXKeyResource.cpp} | 10 +++------- src/lib/platform/{OSXIKeyResource.h => IOSXKeyResource.h} | 2 +- src/lib/platform/OSXKeyState.cpp | 4 ++-- src/lib/platform/OSXKeyState.h | 4 ++-- src/lib/platform/OSXUchrKeyResource.h | 4 ++-- 6 files changed, 12 insertions(+), 16 deletions(-) rename src/lib/platform/{OSXIKeyResource.cpp => IOSXKeyResource.cpp} (96%) rename src/lib/platform/{OSXIKeyResource.h => IOSXKeyResource.h} (96%) diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index b5c747b72..2070d487b 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -18,8 +18,8 @@ if (WIN32) file(GLOB headers "MSWindows*.h") file(GLOB sources "MSWindows*.cpp") elseif (APPLE) - file(GLOB headers "OSX*.h") - file(GLOB sources "OSX*.cpp" "OSX*.m") + file(GLOB headers "OSX*.h" "IOSX*.h") + file(GLOB sources "OSX*.cpp" "IOSX*.cpp" "OSX*.m") elseif (UNIX) file(GLOB headers "XWindows*.h") file(GLOB sources "XWindows*.cpp") diff --git a/src/lib/platform/OSXIKeyResource.cpp b/src/lib/platform/IOSXKeyResource.cpp similarity index 96% rename from src/lib/platform/OSXIKeyResource.cpp rename to src/lib/platform/IOSXKeyResource.cpp index c6b5a954f..384d4369b 100644 --- a/src/lib/platform/OSXIKeyResource.cpp +++ b/src/lib/platform/IOSXKeyResource.cpp @@ -15,16 +15,12 @@ * along with this program. If not, see . */ -#include "platform/OSXIKeyResource.h" +#include "platform/IOSXKeyResource.h" #include -// -// OSXKeyState::KeyResource -// - KeyID -IKeyResource::getKeyID(UInt8 c) +IOSXKeyResource::getKeyID(UInt8 c) { if (c == 0) { return kKeyNone; @@ -163,7 +159,7 @@ IKeyResource::getKeyID(UInt8 c) } KeyID -IKeyResource::unicharToKeyID(UniChar c) +IOSXKeyResource::unicharToKeyID(UniChar c) { switch (c) { case 3: diff --git a/src/lib/platform/OSXIKeyResource.h b/src/lib/platform/IOSXKeyResource.h similarity index 96% rename from src/lib/platform/OSXIKeyResource.h rename to src/lib/platform/IOSXKeyResource.h index cc71cf391..57d826ea8 100644 --- a/src/lib/platform/OSXIKeyResource.h +++ b/src/lib/platform/IOSXKeyResource.h @@ -19,7 +19,7 @@ #include "synergy/KeyState.h" -class IKeyResource : public IInterface { +class IOSXKeyResource : public IInterface { public: virtual bool isValid() const = 0; virtual UInt32 getNumModifierCombinations() const = 0; diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 10491f5f3..71700d95a 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -297,7 +297,7 @@ OSXKeyState::mapKeyFromEvent(KeyIDs& ids, if (count != 0 || m_deadKeyState == 0) { m_deadKeyState = 0; for (UniCharCount i = 0; i < count; ++i) { - ids.push_back(IKeyResource::unicharToKeyID(chars[i])); + ids.push_back(IOSXKeyResource::unicharToKeyID(chars[i])); } adjustAltGrModifier(ids, maskOut, isCommand); return mapVirtualKeyToKeyButton(vkCode); @@ -581,7 +581,7 @@ OSXKeyState::getKeyMapForSpecialKeys(synergy::KeyMap& keyMap, SInt32 group) cons bool OSXKeyState::getKeyMap(synergy::KeyMap& keyMap, - SInt32 group, const IKeyResource& r) const + SInt32 group, const IOSXKeyResource& r) const { if (!r.isValid()) { return false; diff --git a/src/lib/platform/OSXKeyState.h b/src/lib/platform/OSXKeyState.h index b5cdbc1f6..ae54c2d3e 100644 --- a/src/lib/platform/OSXKeyState.h +++ b/src/lib/platform/OSXKeyState.h @@ -26,7 +26,7 @@ #include typedef TISInputSourceRef KeyLayout; -class IKeyResource; +class IOSXKeyResource; //! OS X key state /*! @@ -113,7 +113,7 @@ class OSXKeyState : public KeyState { // Convert keyboard resource to a key map bool getKeyMap(synergy::KeyMap& keyMap, - SInt32 group, const IKeyResource& r) const; + SInt32 group, const IOSXKeyResource& r) const; // Get the available keyboard groups bool getGroups(GroupList&) const; diff --git a/src/lib/platform/OSXUchrKeyResource.h b/src/lib/platform/OSXUchrKeyResource.h index bffee6079..6d97cc519 100644 --- a/src/lib/platform/OSXUchrKeyResource.h +++ b/src/lib/platform/OSXUchrKeyResource.h @@ -18,13 +18,13 @@ #pragma once #include "synergy/KeyState.h" -#include "platform/OSXIKeyResource.h" +#include "platform/IOSXKeyResource.h" #include typedef TISInputSourceRef KeyLayout; -class OSXUchrKeyResource : public IKeyResource { +class OSXUchrKeyResource : public IOSXKeyResource { public: OSXUchrKeyResource(const void*, UInt32 keyboardType); From fa2a6a2169537925ce309db06427fc83ff14a3f1 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 4 May 2016 08:09:01 -0700 Subject: [PATCH 223/572] Added restart service on failure #5277 --- src/setup/win32/Product.wxs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/setup/win32/Product.wxs b/src/setup/win32/Product.wxs index c47aa0633..552f76d1d 100644 --- a/src/setup/win32/Product.wxs +++ b/src/setup/win32/Product.wxs @@ -84,11 +84,18 @@ - + + Type="ownProcess" Start="auto" ErrorControl="normal"> + + Date: Mon, 9 May 2016 06:31:05 -0700 Subject: [PATCH 224/572] Continue after close old process #5277 --- src/lib/synwinhk/synwinhk.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/synwinhk/synwinhk.cpp b/src/lib/synwinhk/synwinhk.cpp index 8638f2440..5aed9f3f8 100644 --- a/src/lib/synwinhk/synwinhk.cpp +++ b/src/lib/synwinhk/synwinhk.cpp @@ -934,8 +934,10 @@ init(DWORD threadID) // old process (probably) still exists so refuse to // reinitialize this DLL (and thus steal it from the // old process). - CloseHandle(process); - return 0; + int result = CloseHandle(process); + if (result == false) { + return 0; + } } // clean up after old process. the system should've already From 518fc7a676147d39f3069dc99a83fcc214d5515a Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 8 Aug 2016 14:28:35 +0100 Subject: [PATCH 225/572] Versioned to 1.8.2-beta --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 493a1b65f..c3c19ba3f 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 1) -set(VERSION_STAGE stable) +set(VERSION_REV 2) +set(VERSION_STAGE beta) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From 1a76acd4467aaf8da6fa4f83614c161663054aac Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 5 Jul 2016 12:30:08 +0100 Subject: [PATCH 226/572] #4768 Interrupted both clipboards --- src/lib/client/Client.cpp | 47 +++++++++++++++++++++++---------------- src/lib/client/ServerProxy.cpp | 2 -- src/lib/server/ClientProxy1_6.cpp | 2 -- src/lib/server/Server.cpp | 4 +++- src/lib/synergy/StreamChunker.cpp | 41 ++++++++++++++++++++++++++-------- src/lib/synergy/StreamChunker.h | 4 +++- 6 files changed, 66 insertions(+), 34 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 330488077..d50d1737f 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -271,29 +271,38 @@ Client::leave() m_active = false; if (m_sendClipboardThread != NULL) { - StreamChunker::interruptClipboard(); + StreamChunker::setClipboardInterrupt(true); m_sendClipboardThread->wait(); + delete m_sendClipboardThread; m_sendClipboardThread = NULL; + StreamChunker::setClipboardInterrupt(false); + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_ownClipboard[id]) { + m_sentClipboard[id] = false; + } + } } - - m_condData = false; - m_sendClipboardThread = new Thread( - new TMethodJob( - this, - &Client::sendClipboardThread, - NULL)); - // Bug #4735 - we can't leave() until fillClipboard()s all finish - Stopwatch timer(false); - m_mutex->lock(); - while (!m_condData) { - if (!m_condVar->wait(timer, 0.5)) { - LOG((CLOG_WARN "timed out %fs waiting for clipboard fill", - (double) timer.getTime())); - break; + + if (m_sendClipboardThread == NULL) { + m_condData = false; + m_sendClipboardThread = new Thread( + new TMethodJob( + this, + &Client::sendClipboardThread, + NULL)); + // Bug #4735 - we can't leave() until fillClipboard()s all finish + Stopwatch timer(false); + m_mutex->lock(); + while (!m_condData) { + if (!m_condVar->wait(timer, 0.5)) { + LOG((CLOG_WARN "timed out %fs waiting for clipboard fill", + (double) timer.getTime())); + break; + } + LOG((CLOG_DEBUG1 "leave %fs elapsed", (double) timer.getTime())); } - LOG((CLOG_DEBUG1 "leave %fs elapsed", (double) timer.getTime())); + m_mutex->unlock(); } - m_mutex->unlock(); m_screen->leave(); @@ -301,7 +310,7 @@ Client::leave() m_receivedFileData.clear(); LOG((CLOG_DEBUG "file transmission interrupted")); } - + return true; } diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index 9c2b9c75a..16459b439 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -364,8 +364,6 @@ ServerProxy::onClipboardChanged(ClipboardID id, const IClipboard* clipboard) LOG((CLOG_DEBUG "sending clipboard %d seqnum=%d", id, m_seqNum)); StreamChunker::sendClipboard(data, data.size(), id, m_seqNum, m_events, this); - - LOG((CLOG_DEBUG "sent clipboard size=%d", data.size())); } void diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index bdd0f23fb..b4c33e824 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -58,8 +58,6 @@ ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard) LOG((CLOG_DEBUG "sending clipboard %d to \"%s\"", id, getName().c_str())); StreamChunker::sendClipboard(data, size, id, 0, m_events, this); - - LOG((CLOG_DEBUG "sent clipboard size=%d", size)); } } diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 3c9114e4f..1f3bf3b28 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -509,9 +509,11 @@ Server::switchScreen(BaseClientProxy* dst, // if already sending clipboard, we need to interupt it, otherwise // clipboard data could be corrupted on the other side if (m_sendClipboardThread != NULL) { - StreamChunker::interruptClipboard(); + StreamChunker::setClipboardInterrupt(true); m_sendClipboardThread->wait(); + delete m_sendClipboardThread; m_sendClipboardThread = NULL; + StreamChunker::setClipboardInterrupt(false); } // send the clipboard data to new active screen diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index 380a37a38..d54839f33 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -17,6 +17,8 @@ #include "synergy/StreamChunker.h" +#include "mt/Lock.h" +#include "mt/Mutex.h" #include "synergy/FileChunk.h" #include "synergy/ClipboardChunk.h" #include "synergy/protocol_types.h" @@ -43,7 +45,7 @@ bool StreamChunker::s_isChunkingClipboard = false; bool StreamChunker::s_interruptClipboard = false; bool StreamChunker::s_isChunkingFile = false; bool StreamChunker::s_interruptFile = false; - +Mutex* StreamChunker::s_interruptMutex = NULL; void StreamChunker::sendFile( @@ -144,10 +146,15 @@ StreamChunker::sendClipboard( sendStopwatch.start(); while (true) { - if (s_interruptClipboard) { - s_interruptClipboard = false; - LOG((CLOG_DEBUG "clipboard transmission interrupted")); - break; + { + if (s_interruptMutex == NULL) { + s_interruptMutex = new Mutex(); + } + Lock lock(s_interruptMutex); + if (s_interruptClipboard) { + LOG((CLOG_DEBUG "clipboard transmission interrupted")); + break; + } } if (sendStopwatch.getTime() > SEND_THRESHOLD) { @@ -177,6 +184,8 @@ StreamChunker::sendClipboard( events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, end)); + LOG((CLOG_DEBUG "sent clipboard size=%d", sentLength)); + s_isChunkingClipboard = false; } @@ -201,10 +210,24 @@ StreamChunker::interruptFile() } void -StreamChunker::interruptClipboard() +StreamChunker::setClipboardInterrupt(bool interrupt) { - if (s_isChunkingClipboard) { - s_interruptClipboard = true; - LOG((CLOG_INFO "previous clipboard data has become invalid")); + if (s_interruptMutex == NULL) { + s_interruptMutex = new Mutex(); + } + Lock lock(s_interruptMutex); + + if (interrupt) { + if (s_isChunkingClipboard) { + s_interruptClipboard = interrupt; + LOG((CLOG_INFO "previous clipboard data has become invalid")); + } + else { + LOG((CLOG_DEBUG "no clipboard to interrupt")); + } + } + else { + s_interruptClipboard = interrupt; + LOG((CLOG_DEBUG "reset clipboard interrupt")); } } diff --git a/src/lib/synergy/StreamChunker.h b/src/lib/synergy/StreamChunker.h index b0bfb3416..cca9442b5 100644 --- a/src/lib/synergy/StreamChunker.h +++ b/src/lib/synergy/StreamChunker.h @@ -21,6 +21,7 @@ #include "base/String.h" class IEventQueue; +class Mutex; class StreamChunker { public: @@ -37,7 +38,7 @@ class StreamChunker { void* eventTarget); static void updateChunkSize(bool useSecureSocket); static void interruptFile(); - static void interruptClipboard(); + static void setClipboardInterrupt(bool interrupt); private: static size_t s_chunkSize; @@ -45,4 +46,5 @@ class StreamChunker { static bool s_interruptClipboard; static bool s_isChunkingFile; static bool s_interruptFile; + static Mutex* s_interruptMutex; }; From 30fa5223bcf417cfe43a2f0139b54c6e349d3eab Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 8 Jul 2016 02:19:57 -0700 Subject: [PATCH 227/572] #4768 Fixed race condition on active client proxy switch --- src/lib/server/Server.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 1f3bf3b28..7e85e811d 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -496,6 +496,18 @@ Server::switchScreen(BaseClientProxy* dst, } } + // if already sending clipboard, we need to interupt it, otherwise + // clipboard data could be corrupted on the other side + // interrupt before switch active, as send clipboard uses active + // client proxy, which would cause race condition + if (m_sendClipboardThread != NULL) { + StreamChunker::setClipboardInterrupt(true); + m_sendClipboardThread->wait(); + delete m_sendClipboardThread; + m_sendClipboardThread = NULL; + StreamChunker::setClipboardInterrupt(false); + } + // cut over m_active = dst; @@ -506,15 +518,6 @@ Server::switchScreen(BaseClientProxy* dst, m_active->enter(x, y, m_seqNum, m_primaryClient->getToggleMask(), forScreensaver); - // if already sending clipboard, we need to interupt it, otherwise - // clipboard data could be corrupted on the other side - if (m_sendClipboardThread != NULL) { - StreamChunker::setClipboardInterrupt(true); - m_sendClipboardThread->wait(); - delete m_sendClipboardThread; - m_sendClipboardThread = NULL; - StreamChunker::setClipboardInterrupt(false); - } // send the clipboard data to new active screen m_sendClipboardThread = new Thread( From 9caa04237c8e83964c33c8c906fcfeb91a1af53a Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 8 Jul 2016 02:05:54 -0700 Subject: [PATCH 228/572] Refined file transfer log output --- src/lib/synergy/FileChunk.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lib/synergy/FileChunk.cpp b/src/lib/synergy/FileChunk.cpp index ad1392af4..5eae12329 100644 --- a/src/lib/synergy/FileChunk.cpp +++ b/src/lib/synergy/FileChunk.cpp @@ -90,7 +90,7 @@ FileChunk::assemble(synergy::IStream* stream, String& dataReceived, size_t& expe stopwatch.reset(); if (CLOG->getFilter() >= kDEBUG2) { - LOG((CLOG_DEBUG2 "recv file data from client: file size=%s", content.c_str())); + LOG((CLOG_DEBUG2 "recv file size=%s", content.c_str())); stopwatch.start(); } return kStart; @@ -98,13 +98,13 @@ FileChunk::assemble(synergy::IStream* stream, String& dataReceived, size_t& expe case kDataChunk: dataReceived.append(content); if (CLOG->getFilter() >= kDEBUG2) { - LOG((CLOG_DEBUG2 "recv file data from client: chunck size=%i", content.size())); + LOG((CLOG_DEBUG2 "recv file chunck size=%i", content.size())); double interval = stopwatch.getTime(); receivedDataSize += content.size(); - LOG((CLOG_DEBUG2 "recv file data from client: interval=%f s", interval)); + LOG((CLOG_DEBUG2 "recv file interval=%f s", interval)); if (interval >= kIntervalThreshold) { double averageSpeed = receivedDataSize / interval / 1000; - LOG((CLOG_DEBUG2 "recv file data from client: average speed=%f kb/s", averageSpeed)); + LOG((CLOG_DEBUG2 "recv file average speed=%f kb/s", averageSpeed)); receivedDataSize = 0; elapsedTime += interval; @@ -120,12 +120,12 @@ FileChunk::assemble(synergy::IStream* stream, String& dataReceived, size_t& expe } if (CLOG->getFilter() >= kDEBUG2) { - LOG((CLOG_DEBUG2 "file data transfer finished")); + LOG((CLOG_DEBUG2 "file transfer finished")); elapsedTime += stopwatch.getTime(); double averageSpeed = expectedSize / elapsedTime / 1000; - LOG((CLOG_DEBUG2 "file data transfer finished: total time consumed=%f s", elapsedTime)); - LOG((CLOG_DEBUG2 "file data transfer finished: total data received=%i kb", expectedSize / 1000)); - LOG((CLOG_DEBUG2 "file data transfer finished: total average speed=%f kb/s", averageSpeed)); + LOG((CLOG_DEBUG2 "file transfer finished: total time consumed=%f s", elapsedTime)); + LOG((CLOG_DEBUG2 "file transfer finished: total data received=%i kb", expectedSize / 1000)); + LOG((CLOG_DEBUG2 "file transfer finished: total average speed=%f kb/s", averageSpeed)); } return kFinish; } From 18c2c901442ff36ca48cf45e103c3ea52b99cc39 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 11 Jul 2016 03:59:34 -0700 Subject: [PATCH 229/572] #3044 Stopped attach hook thread to foreground --- src/lib/synwinhk/synwinhk.cpp | 53 ------------------------------------------- 1 file changed, 53 deletions(-) diff --git a/src/lib/synwinhk/synwinhk.cpp b/src/lib/synwinhk/synwinhk.cpp index 5aed9f3f8..7412755ed 100644 --- a/src/lib/synwinhk/synwinhk.cpp +++ b/src/lib/synwinhk/synwinhk.cpp @@ -120,7 +120,6 @@ static LPARAM g_deadLParam = 0; static BYTE g_deadKeyState[256] = { 0 }; static BYTE g_keyState[256] = { 0 }; static DWORD g_hookThread = 0; -static DWORD g_attachedThread = 0; static bool g_fakeInput = false; #if defined(_MSC_VER) @@ -136,53 +135,6 @@ int _fltused=0; } #endif - -// -// internal functions -// - -static -void -detachThread() -{ - if (g_attachedThread != 0 && g_hookThread != g_attachedThread) { - AttachThreadInput(g_hookThread, g_attachedThread, FALSE); - g_attachedThread = 0; - } -} - -static -bool -attachThreadToForeground() -{ - // only attach threads if using low level hooks. a low level hook - // runs in the thread that installed the hook but we have to make - // changes that require being attached to the target thread (which - // should be the foreground window). a regular hook runs in the - // thread that just removed the event from its queue so we're - // already in the right thread. - if (g_hookThread != 0) { - HWND window = GetForegroundWindow(); - if (window == NULL) - return false; - - DWORD threadID = GetWindowThreadProcessId(window, NULL); - // skip if no change - if (g_attachedThread != threadID) { - // detach from previous thread - detachThread(); - - // attach to new thread - if (threadID != 0 && threadID != g_hookThread) { - AttachThreadInput(g_hookThread, threadID, TRUE); - g_attachedThread = threadID; - } - return true; - } - } - return false; -} - #if !NO_GRAB_KEYBOARD static WPARAM @@ -497,7 +449,6 @@ static bool keyboardHookHandler(WPARAM wParam, LPARAM lParam) { - attachThreadToForeground(); return doKeyboardHookHandler(wParam, lParam); } #endif @@ -608,7 +559,6 @@ static bool mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data) { -// attachThreadToForeground(); return doMouseHookHandler(wParam, x, y, data); } @@ -1080,9 +1030,6 @@ uninstall(void) g_deadVirtKey = 0; g_deadLParam = 0; - // detach from thread - detachThread(); - // uninstall hooks if (g_keyboardLL != NULL) { UnhookWindowsHookEx(g_keyboardLL); From 83c0dea2e40eec14e770083fc764cf5250c5c439 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 14 Jul 2016 07:55:13 -0700 Subject: [PATCH 230/572] #4792 Calculated log message size rather than using fixed size --- src/lib/base/Log.cpp | 25 ++++++++++++++++++------- src/lib/base/Log.h | 2 -- src/lib/synergy/ProtocolUtil.cpp | 6 +----- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/lib/base/Log.cpp b/src/lib/base/Log.cpp index 7ad8ae1ba..42389bda5 100644 --- a/src/lib/base/Log.cpp +++ b/src/lib/base/Log.cpp @@ -172,22 +172,33 @@ Log::print(const char* file, int line, const char* fmt, ...) // do not prefix time and file for kPRINT (CLOG_PRINT) if (priority != kPRINT) { - char message[kLogMessageLength]; - struct tm *tm; - char tmp[220]; + char timestamp[50]; time_t t; time(&t); tm = localtime(&t); - sprintf(tmp, "%04i-%02i-%02iT%02i:%02i:%02i", tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + sprintf(timestamp, "%04i-%02i-%02iT%02i:%02i:%02i", tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); -#ifndef NDEBUG - sprintf(message, "[%s] %s: %s\n\t%s,%d", tmp, g_priority[priority], buffer, file, line); + // square brackets, spaces, comma and null terminator take about 10 + int size = 10; + size += strlen(timestamp); + size += strlen(g_priority[priority]); + size += strlen(buffer); +#ifndef NDEBUG + size += strlen(file); + // assume there is no file contains over 100k lines of code + size += 6; +#endif + char* message = new char[size]; + +#ifndef NDEBUG + sprintf(message, "[%s] %s: %s\n\t%s,%d", timestamp, g_priority[priority], buffer, file, line); #else - sprintf(message, "[%s] %s: %s", tmp, g_priority[priority], buffer); + sprintf(message, "[%s] %s: %s", timestamp, g_priority[priority], buffer); #endif output(priority, message); + delete[] message; } else { output(priority, buffer); } diff --git a/src/lib/base/Log.h b/src/lib/base/Log.h index eaa2c8a87..ab36ce6ac 100644 --- a/src/lib/base/Log.h +++ b/src/lib/base/Log.h @@ -139,8 +139,6 @@ class Log { int m_maxPriority; }; -const UInt16 kLogMessageLength = 2048; - /*! \def LOG(arg) Write to the log. Because macros cannot accept variable arguments, this diff --git a/src/lib/synergy/ProtocolUtil.cpp b/src/lib/synergy/ProtocolUtil.cpp index ee73bebf8..d5e0aed7d 100644 --- a/src/lib/synergy/ProtocolUtil.cpp +++ b/src/lib/synergy/ProtocolUtil.cpp @@ -230,11 +230,7 @@ ProtocolUtil::vreadf(synergy::IStream* stream, const char* fmt, va_list args) throw; } - // don't cause buffer overrun, using +100 chars in case - // someone modifies this log message in future. - if (len + 100 < kLogMessageLength) { - LOG((CLOG_DEBUG2 "readf: read %d byte string: %.*s", len, len, sBuffer)); - } + LOG((CLOG_DEBUG2 "readf: read %d byte string: %.*s", len, len, sBuffer)); // save the data String* dst = va_arg(args, String*); From fd39c73bcd5f6ef081f0f7f27fc072052c33a0d4 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 15 Jul 2016 14:49:28 +0100 Subject: [PATCH 231/572] #5471 Made serial key form layout align to left --- src/gui/res/SetupWizardBase.ui | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gui/res/SetupWizardBase.ui b/src/gui/res/SetupWizardBase.ui index 8339e54af..7508560b4 100644 --- a/src/gui/res/SetupWizardBase.ui +++ b/src/gui/res/SetupWizardBase.ui @@ -235,6 +235,9 @@ + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + 20 @@ -258,7 +261,7 @@ - 400 + 200 0 From 62c9ca0a7f1c12bc2589ea5f16650cef883ebb2e Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 15 Jul 2016 14:58:30 +0100 Subject: [PATCH 232/572] #5471 Redesigned serial key input --- src/gui/res/SetupWizardBase.ui | 37 ++----------------------------------- src/gui/src/SetupWizard.cpp | 18 +++++++++--------- 2 files changed, 11 insertions(+), 44 deletions(-) diff --git a/src/gui/res/SetupWizardBase.ui b/src/gui/res/SetupWizardBase.ui index 7508560b4..24dea659b 100644 --- a/src/gui/res/SetupWizardBase.ui +++ b/src/gui/res/SetupWizardBase.ui @@ -229,45 +229,12 @@ - Subscription + Serial key - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - 20 - - - 10 - - - - - Serial Key: - - - - - - - - 0 - 0 - - - - - 200 - 0 - - - - - + diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index e6ada683c..f5a40b6c0 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -62,9 +62,9 @@ SetupWizard::SetupWizard(MainWindow& mainWindow, bool startMain) : AppConfig& appConfig = m_MainWindow.appConfig(); m_pLineEditEmail->setText(appConfig.activateEmail()); - m_pLineEditSerialKey->setText(appConfig.serialKey()); + m_pTextEditSerialKey->setText(appConfig.serialKey()); - m_pLineEditSerialKey->setEnabled(false); + m_pTextEditSerialKey->setEnabled(false); } @@ -120,7 +120,7 @@ bool SetupWizard::validateCurrentPage() } } else if (m_pRadioButtonSubscription->isChecked()) { - if (m_pLineEditSerialKey->text().isEmpty()) { + if (m_pTextEditSerialKey->toPlainText().isEmpty()) { message.setText(tr("Please enter your subscription serial key.")); message.exec(); return false; @@ -128,7 +128,7 @@ bool SetupWizard::validateCurrentPage() else { // create subscription file in profile directory SubscriptionManager subscriptionManager(this, m_MainWindow.appConfig(), m_Edition); - if (!subscriptionManager.activateSerial(m_pLineEditSerialKey->text())) { + if (!subscriptionManager.activateSerial(m_pTextEditSerialKey->toPlainText())) { return false; } @@ -206,9 +206,9 @@ void SetupWizard::accept() if (m_pRadioButtonSubscription->isChecked()) { - appConfig.setSerialKey(m_pLineEditSerialKey->text()); + appConfig.setSerialKey(m_pTextEditSerialKey->toPlainText()); - notifyActivation("serial:" + m_pLineEditSerialKey->text()); + notifyActivation("serial:" + m_pTextEditSerialKey->toPlainText()); } if (m_pRadioButtonSkip->isChecked()) @@ -274,7 +274,7 @@ void SetupWizard::on_m_pRadioButtonSkip_toggled(bool checked) if (checked) { m_pLineEditEmail->setEnabled(false); m_pLineEditPassword->setEnabled(false); - m_pLineEditSerialKey->setEnabled(false); + m_pTextEditSerialKey->setEnabled(false); } } @@ -283,7 +283,7 @@ void SetupWizard::on_m_pRadioButtonActivate_toggled(bool checked) if (checked) { m_pLineEditEmail->setEnabled(true); m_pLineEditPassword->setEnabled(true); - m_pLineEditSerialKey->setEnabled(false); + m_pTextEditSerialKey->setEnabled(false); } } @@ -292,6 +292,6 @@ void SetupWizard::on_m_pRadioButtonSubscription_toggled(bool checked) if (checked) { m_pLineEditEmail->setEnabled(false); m_pLineEditPassword->setEnabled(false); - m_pLineEditSerialKey->setEnabled(true); + m_pTextEditSerialKey->setEnabled(true); } } From 2ad4b896f3bd98aeff6021206ce4ba22a45a542c Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 20 Jul 2016 09:38:58 -0700 Subject: [PATCH 233/572] #4768 Disabled sending clipboard on inactive grab --- src/lib/client/Client.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index d50d1737f..500c7d530 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -704,14 +704,6 @@ Client::handleClipboardGrabbed(const Event& event, void*) m_ownClipboard[info->m_id] = true; m_sentClipboard[info->m_id] = false; m_timeClipboard[info->m_id] = 0; - - // if we're not the active screen then send the clipboard now, - // otherwise we'll wait until we leave. - Clipboard clipboard; - if (!m_active) { - fillClipboard(info->m_id, &clipboard); - sendClipboard(info->m_id, &clipboard); - } } void From 2a3d34983f3a8007f330ec911df3aee29379d529 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 20 Jul 2016 10:09:39 -0700 Subject: [PATCH 234/572] #4715 Increased wizard version number due to serial key support --- src/gui/src/AppConfig.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 684c0015b..dfcb477a0 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -34,8 +34,9 @@ // 4: ssl plugin 'ns' v1.0 // 5: ssl plugin 'ns' v1.1 // 6: ssl plugin 'ns' v1.2 +// 7: serial key activation // -const int kWizardVersion = 6; +const int kWizardVersion = 7; class QSettings; class SettingsDialog; From a70cba80ea3c35afcde25997b81377aba504b977 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 20 Jul 2016 10:11:25 -0700 Subject: [PATCH 235/572] Revert "Used input source ID as the key in group map #2765" This reverts commit 4208e89eaec211d332af9ac7b977107d6712e8dd. --- src/lib/platform/OSXKeyState.cpp | 18 +++++++----------- src/lib/platform/OSXKeyState.h | 2 +- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 71700d95a..9f3d923f7 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -379,17 +379,15 @@ OSXKeyState::pollActiveModifiers() const SInt32 OSXKeyState::pollActiveGroup() const { + bool layoutValid = true; TISInputSourceRef keyboardLayout = TISCopyCurrentKeyboardLayoutInputSource(); - CFDataRef id = (CFDataRef)TISGetInputSourceProperty( - keyboardLayout, kTISPropertyInputSourceID); - GroupMap::const_iterator i = m_groupMap.find(id); - if (i != m_groupMap.end()) { - return i->second; + if (layoutValid) { + GroupMap::const_iterator i = m_groupMap.find(keyboardLayout); + if (i != m_groupMap.end()) { + return i->second; + } } - - LOG((CLOG_DEBUG "can't get the active group, use the first group instead")); - return 0; } @@ -416,9 +414,7 @@ OSXKeyState::getKeyMap(synergy::KeyMap& keyMap) m_groupMap.clear(); SInt32 numGroups = (SInt32)m_groups.size(); for (SInt32 g = 0; g < numGroups; ++g) { - CFDataRef id = (CFDataRef)TISGetInputSourceProperty( - m_groups[g], kTISPropertyInputSourceID); - m_groupMap[id] = g; + m_groupMap[m_groups[g]] = g; } } diff --git a/src/lib/platform/OSXKeyState.h b/src/lib/platform/OSXKeyState.h index ae54c2d3e..29cd5d6c6 100644 --- a/src/lib/platform/OSXKeyState.h +++ b/src/lib/platform/OSXKeyState.h @@ -158,7 +158,7 @@ class OSXKeyState : public KeyState { KeyButtonOffset = 1 }; - typedef std::map GroupMap; + typedef std::map GroupMap; typedef std::map VirtualKeyMap; VirtualKeyMap m_virtualKeyMap; From fec53e812f3c3b75e51735288de94eb12b698090 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 20 Jul 2016 10:11:45 -0700 Subject: [PATCH 236/572] Revert "Made index 1 based in log #2765" This reverts commit 177c2764259c097d0f0f0fa6e081c42bcef91ce5. --- src/lib/synergy/KeyMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/synergy/KeyMap.cpp b/src/lib/synergy/KeyMap.cpp index 68b5d1c37..8833c2b58 100644 --- a/src/lib/synergy/KeyMap.cpp +++ b/src/lib/synergy/KeyMap.cpp @@ -696,7 +696,7 @@ KeyMap::findBestKey(const KeyEntryList& entryList, const KeyItem& item = entryList[i].back(); if ((item.m_required & desiredState) == (item.m_sensitive & desiredState)) { - LOG((CLOG_DEBUG1 "best key index %d of %d (exact)", i + 1, entryList.size())); + LOG((CLOG_DEBUG1 "best key index %d of %d (exact)", i, entryList.size())); return i; } } From 683d9b37867546b7f05d5e3b95549ff300628e3b Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 20 Jul 2016 10:12:07 -0700 Subject: [PATCH 237/572] Revert "Check list in a reverse order #2765" This reverts commit 3de98a2f44a20449468496d47f5642b534a80cf6. --- src/lib/synergy/KeyMap.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/synergy/KeyMap.cpp b/src/lib/synergy/KeyMap.cpp index 8833c2b58..1fff2c589 100644 --- a/src/lib/synergy/KeyMap.cpp +++ b/src/lib/synergy/KeyMap.cpp @@ -692,7 +692,7 @@ KeyMap::findBestKey(const KeyEntryList& entryList, KeyModifierMask desiredState) const { // check for an item that can accommodate the desiredState exactly - for (SInt32 i = (SInt32)entryList.size() - 1; i >= 0; --i) { + for (SInt32 i = 0; i < (SInt32)entryList.size(); ++i) { const KeyItem& item = entryList[i].back(); if ((item.m_required & desiredState) == (item.m_sensitive & desiredState)) { @@ -704,7 +704,7 @@ KeyMap::findBestKey(const KeyEntryList& entryList, // choose the item that requires the fewest modifier changes SInt32 bestCount = 32; SInt32 bestIndex = -1; - for (SInt32 i = (SInt32)entryList.size() - 1; i >= 0; --i) { + for (SInt32 i = 0; i < (SInt32)entryList.size(); ++i) { const KeyItem& item = entryList[i].back(); KeyModifierMask change = ((item.m_required ^ desiredState) & item.m_sensitive); From 725cc7a2f89c42abf1ddc4f92c70dcc6a4b2a0ff Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 20 Jul 2016 12:12:18 -0700 Subject: [PATCH 238/572] #4922 Increased server help message buffer --- src/lib/synergy/ServerApp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/synergy/ServerApp.cpp b/src/lib/synergy/ServerApp.cpp index c4de7dd33..7d4a67184 100644 --- a/src/lib/synergy/ServerApp.cpp +++ b/src/lib/synergy/ServerApp.cpp @@ -120,7 +120,7 @@ ServerApp::help() # define WINAPI_INFO #endif - char buffer[2000]; + char buffer[3000]; sprintf( buffer, "Usage: %s" From 804b482c5e3c8e3fe3978fdf70f57a2e6922517f Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 21 Jul 2016 02:38:44 -0700 Subject: [PATCH 239/572] #5471 Added some tips and size restrictions --- src/gui/res/SetupWizardBase.ui | 60 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/src/gui/res/SetupWizardBase.ui b/src/gui/res/SetupWizardBase.ui index 24dea659b..3077c4d73 100644 --- a/src/gui/res/SetupWizardBase.ui +++ b/src/gui/res/SetupWizardBase.ui @@ -125,10 +125,42 @@ 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 + + - Log in + &Account login true @@ -164,7 +196,7 @@ 200 - 0 + 20 @@ -190,7 +222,7 @@ 200 - 0 + 20 @@ -201,7 +233,7 @@ - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://synergy-project.org/account/reset/?source=gui">Forgot password</a> true @@ -228,8 +260,14 @@ + + + 75 + true + + - Serial key + &Serial key @@ -255,7 +293,17 @@ - &Skip activation + S&kip activation + + + + + + + color: rgb(100, 100, 100); + + + You will see UNREGISTERED in the window title (not recommanded). From 47ef8b09bd423b5cb548a905114475179ef903d0 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 21 Jul 2016 03:17:02 -0700 Subject: [PATCH 240/572] #4768 Reset thread when it finishes --- src/lib/server/Server.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 7e85e811d..f4d26bff5 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -1870,6 +1870,8 @@ Server::sendClipboardThread(void*) for (ClipboardID id = 0; id < kClipboardEnd; ++id) { m_active->setClipboard(id, &m_clipboards[id].m_clipboard); } + + m_sendClipboardThread = NULL; } void From 71b2652a70b28192b84f31340cabdd37257d9373 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 21 Jul 2016 03:21:53 -0700 Subject: [PATCH 241/572] Versioned to 1.8.2-rc1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c3c19ba3f..0e6d21a64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 2) -set(VERSION_STAGE beta) +set(VERSION_STAGE rc1) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From 2f8f5fcb4e13003b53eeabfbc103c3cadd41a68b Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 8 Aug 2016 14:31:01 +0100 Subject: [PATCH 242/572] Updated ChangeLog --- ChangeLog | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ChangeLog b/ChangeLog index a6899ccd8..e8e8816db 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +v1.8.2-stable +============= +Bug #3044 - Unable to drag-select in MS Office +Bug #4768 - Copy paste causes 'server is dead' error on switching +Bug #4792 - Server logging crashes when switching with clipboard data +Bug #2975 - Middle click does not close Chrome tab on Mac client +Bug #5471 - Subscription textbox on activation screen overflows on Mac +Bug #4836 - Stop button resets to Start when settings dialog canceled +Enhancement #5277 - Auto restart service when synwinhk.dll fails on Windows +Enhancement #4913 - Future-proof GUI login by using newer auth URL +Enhancement #4922 - Add --enable-crypto argument to help text +Enhancement #5299 - High resolution App icon on Mac +Enhancement #4894 - Improve grammar in connection notification dialog + v1.8.1-stable ============= Bug #5461 - GUI crash during activation on Mac From 0c4746492f883c30c639a707c6c65698677eb3c5 Mon Sep 17 00:00:00 2001 From: Malcolm Lowe Date: Fri, 22 Jul 2016 13:18:17 +0100 Subject: [PATCH 243/572] Fixed issue title in ChangeLog --- ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index e8e8816db..83efa388e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,7 +4,7 @@ Bug #3044 - Unable to drag-select in MS Office Bug #4768 - Copy paste causes 'server is dead' error on switching Bug #4792 - Server logging crashes when switching with clipboard data Bug #2975 - Middle click does not close Chrome tab on Mac client -Bug #5471 - Subscription textbox on activation screen overflows on Mac +Bug #5471 - Serial key textbox on activation screen overflows on Mac Bug #4836 - Stop button resets to Start when settings dialog canceled Enhancement #5277 - Auto restart service when synwinhk.dll fails on Windows Enhancement #4913 - Future-proof GUI login by using newer auth URL From 8a4caba3865f8c3fa7189a809283c9d4582fdfc3 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 17 Jun 2016 13:31:09 +0100 Subject: [PATCH 244/572] #5087 Ensure createBlankCursor() doesn't try to create 0x0 cursor XQueryBestCursor sometimes returns 0 for both width and height of the cursor pixmap. Also caused #5322 and #5068 --- src/lib/platform/XWindowsScreen.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index d10b7e1c4..7579f66ff 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -36,6 +36,7 @@ #include #include +#include #if X_DISPLAY_MISSING # error X11 is required to build synergy #else @@ -1661,8 +1662,10 @@ XWindowsScreen::createBlankCursor() const // this seems just a bit more complicated than really necessary // get the closet cursor size to 1x1 - unsigned int w, h; + unsigned int w = 0, h = 0; XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); + w = std::max(1u, w); + h = std::max(1u, h); // make bitmap data for cursor of closet size. since the cursor // is blank we can use the same bitmap for shape and mask: all From 03d22c3746758442adb65c2e7ca7150296986879 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 5 Aug 2016 04:14:37 -0700 Subject: [PATCH 245/572] #5471 Fixed typo in activation wizard page --- src/gui/res/SetupWizardBase.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/res/SetupWizardBase.ui b/src/gui/res/SetupWizardBase.ui index 3077c4d73..e796b0b37 100644 --- a/src/gui/res/SetupWizardBase.ui +++ b/src/gui/res/SetupWizardBase.ui @@ -303,7 +303,7 @@ color: rgb(100, 100, 100); - You will see UNREGISTERED in the window title (not recommanded). + You will see UNREGISTERED in the window title (not recommended). From 7fa92869a40f3ee196947336c51e939df4140534 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 5 Aug 2016 04:23:49 -0700 Subject: [PATCH 246/572] #5471 Wrapped skip explanation in wizard activation page --- src/gui/res/SetupWizardBase.ui | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/res/SetupWizardBase.ui b/src/gui/res/SetupWizardBase.ui index e796b0b37..da7ae7c52 100644 --- a/src/gui/res/SetupWizardBase.ui +++ b/src/gui/res/SetupWizardBase.ui @@ -305,6 +305,9 @@ You will see UNREGISTERED in the window title (not recommended). + + true + From f6e1dd51fa5a653144f7aa595f45b979adb10820 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 8 Aug 2016 11:23:59 +0100 Subject: [PATCH 247/572] #4768 Workaround locking issue in X11 getCurrentTime() --- src/lib/platform/XWindowsUtil.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/platform/XWindowsUtil.cpp b/src/lib/platform/XWindowsUtil.cpp index 205e4e18a..6b016d217 100644 --- a/src/lib/platform/XWindowsUtil.cpp +++ b/src/lib/platform/XWindowsUtil.cpp @@ -1432,6 +1432,7 @@ XWindowsUtil::getCurrentTime(Display* display, Window window) // select property events on window XWindowAttributes attr; XGetWindowAttributes(display, window, &attr); + XLockDisplay(display); XSelectInput(display, window, attr.your_event_mask | PropertyChangeMask); // make a property name to receive dummy change @@ -1459,6 +1460,7 @@ XWindowsUtil::getCurrentTime(Display* display, Window window) // restore event mask XSelectInput(display, window, attr.your_event_mask); + XUnlockDisplay(display); return xevent.xproperty.time; } From 8901effc8b1d56c98b2d65673c12e224ea83b0c4 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 8 Aug 2016 13:40:23 +0100 Subject: [PATCH 248/572] Updated ChangeLog --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 83efa388e..9f641e2bf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ Bug #3044 - Unable to drag-select in MS Office Bug #4768 - Copy paste causes 'server is dead' error on switching Bug #4792 - Server logging crashes when switching with clipboard data Bug #2975 - Middle click does not close Chrome tab on Mac client +Bug #5087 - Linux client fails to start due to invalid cursor size Bug #5471 - Serial key textbox on activation screen overflows on Mac Bug #4836 - Stop button resets to Start when settings dialog canceled Enhancement #5277 - Auto restart service when synwinhk.dll fails on Windows From 130a248fbe3ee0b3e9801653a1a5f391c80c82d8 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Mon, 8 Aug 2016 14:31:18 +0100 Subject: [PATCH 249/572] Versioned 1.8.2-stable --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e6d21a64..459ab7b0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 2) -set(VERSION_STAGE rc1) +set(VERSION_STAGE stable) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From 145e7797bec109884e8144a3826dfc397a463f47 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 20 Sep 2016 17:20:55 +0100 Subject: [PATCH 250/572] Create ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..6a4337ff8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,5 @@ +### Operating Systems + +### Synergy Version + +### Steps to reproduce bug From 50fcdcf6726b2ef9a608f87db5f996043137e51c Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 21 Sep 2016 11:26:53 +0100 Subject: [PATCH 251/572] Improve GitHub issue template --- .github/ISSUE_TEMPLATE.md | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 6a4337ff8..fa15485af 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,5 +1,31 @@ -### Operating Systems +### Operating Systems ### -### Synergy Version +Client: Applesoft Windy OS 10 -### Steps to reproduce bug +Server: microOS Tiara + +**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 ;) + +### Synergy Version ### + +1.8.π + +**READ ME, DELETE ME**: Go to the 'Help' (on Windows) or 'Synergy' (on macOS) menu and then 'About Synergy' to check your version. Verify that you are using the same version across all of your machines, and that your issue still occurs with the latest release available at https://symless.com/account/login + +### Steps to reproduce bug ### + +**READ ME, DELETE ME**: Try to be succinct. If your bug is intermittent, try and describe what you're doing when it happens most. + +1. Click things. +2. Type things. +3. Bug occurs. +4. ... +5. Profit? + +### Other info ### + +* When did the problem start to occur? When I... +* Is there a way to work around it? No/Yes, you can... +* Does this bug prevent you from using Synergy entirely? Yes/No + +Put anything else you can think of here. From 8503c3c023c7db82d769febb1d341819d6e115b2 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 12 May 2016 14:07:04 +0100 Subject: [PATCH 252/572] Version to 1.8.2 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 459ab7b0b..c3c19ba3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 2) -set(VERSION_STAGE stable) +set(VERSION_STAGE beta) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From 312689039fbfde71bf7c03826ae26b9ea3828a85 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 7 Jun 2016 16:46:54 +0100 Subject: [PATCH 253/572] Versioned to 1.8.3 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c3c19ba3f..1d2513457 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 2) +set(VERSION_REV 3) set(VERSION_STAGE beta) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") From 3cd58f7c7d6580b50faba4de551549484abe0e2d Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 7 Jun 2016 17:20:22 +0100 Subject: [PATCH 254/572] #4323 Prepare settings dialog for elevation tristate --- src/gui/res/SettingsDialogBase.ui | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/gui/res/SettingsDialogBase.ui b/src/gui/res/SettingsDialogBase.ui index c7a5d3eca..1cd828016 100644 --- a/src/gui/res/SettingsDialogBase.ui +++ b/src/gui/res/SettingsDialogBase.ui @@ -7,7 +7,7 @@ 0 0 368 - 377 + 380 @@ -114,17 +114,42 @@ - - + + - Elevate mode + &Hide on startup - - + + + + Specify when the Synergy service should run at an elevated privilege level + + + 0 + + + + As Needed + + + + + Always + + + + + Never + + + + + + - &Hide on startup + Elevate From 9c26c7ea948e0971a777e7feab16ca1655a66cb9 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 7 Jun 2016 17:20:22 +0100 Subject: [PATCH 255/572] #4323 Implement Elevate tristrate in GUI --- src/gui/src/AppConfig.cpp | 16 ++++++++++++---- src/gui/src/AppConfig.h | 29 ++++++++++++++++++++++++++--- src/gui/src/IpcClient.cpp | 5 +++-- src/gui/src/IpcClient.h | 4 +++- src/gui/src/MainWindow.cpp | 9 ++++++++- src/gui/src/SettingsDialog.cpp | 30 +++++------------------------- src/gui/src/SettingsDialog.h | 2 -- 7 files changed, 57 insertions(+), 38 deletions(-) diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 69142d1d5..9f659738c 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -55,7 +55,7 @@ AppConfig::AppConfig(QSettings* settings) : m_WizardLastRun(0), m_ProcessMode(DEFAULT_PROCESS_MODE), m_AutoConfig(true), - m_ElevateMode(false), + m_ElevateMode(defaultElevateMode), m_AutoConfigPrompted(false), m_CryptoEnabled(false), m_AutoHide(false), @@ -126,7 +126,12 @@ void AppConfig::loadSettings() m_Language = settings().value("language", QLocale::system().name()).toString(); m_StartedBefore = settings().value("startedBefore", false).toBool(); m_AutoConfig = settings().value("autoConfig", true).toBool(); - m_ElevateMode = settings().value("elevateMode", false).toBool(); + QVariant elevateMode = settings().value("elevateModeEnum"); + if (!elevateMode.isValid()) { + elevateMode = settings().value ("elevateMode", + QVariant(static_cast(defaultElevateMode))); + } + m_ElevateMode = static_cast(elevateMode.toInt()); m_AutoConfigPrompted = settings().value("autoConfigPrompted", false).toBool(); m_Edition = settings().value("edition", Unknown).toInt(); m_ActivateEmail = settings().value("activateEmail", "").toString(); @@ -148,7 +153,10 @@ void AppConfig::saveSettings() settings().setValue("language", m_Language); settings().setValue("startedBefore", m_StartedBefore); settings().setValue("autoConfig", m_AutoConfig); - settings().setValue("elevateMode", m_ElevateMode); + // 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); settings().setValue("edition", m_Edition); settings().setValue("activateEmail", m_ActivateEmail); @@ -168,7 +176,7 @@ void AppConfig::setAutoConfigPrompted(bool prompted) m_AutoConfigPrompted = prompted; } -bool AppConfig::elevateMode() +ElevateMode AppConfig::elevateMode() { return m_ElevateMode; } diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index dfcb477a0..dcd5a4826 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -46,6 +46,29 @@ enum ProcessMode { Desktop }; +// The elevate mode tristate determines two behaviours on Windows. +// The first, switch-on-desk-switch (SodS), passed through synergyd as a +// command line argument to synergy core, determines if the server restarts +// when switching Windows desktops (e.g. when Windows UAC dialog pops up). +// The second, passed as a boolean flag to Synergyd over the IPC inside +// kIpcCommandMessage, determines whether Synergy should be started with +// elevated privileges. +// +// The matrix for these two behaviours is as follows: +// SodS Elevate +// ___________________________ +// ElevateAsNeeded | true | false +// ElevateAlways | false | true +// ElevateNever | false | false +// +enum ElevateMode { + ElevateAsNeeded = 0, + ElevateAlways = 1, + ElevateNever = 2 +}; + +static const ElevateMode defaultElevateMode = ElevateAsNeeded; + class AppConfig { friend class SettingsDialog; @@ -89,7 +112,7 @@ class AppConfig bool detectPath(const QString& name, QString& path); void persistLogDir(); - bool elevateMode(); + ElevateMode elevateMode(); void setCryptoEnabled(bool e) { m_CryptoEnabled = e; } bool getCryptoEnabled() { return m_CryptoEnabled; } @@ -109,7 +132,7 @@ class AppConfig void setWizardHasRun() { m_WizardLastRun = kWizardVersion; } void setLanguage(const QString language) { m_Language = language; } void setStartedBefore(bool b) { m_StartedBefore = b; } - void setElevateMode(bool b) { m_ElevateMode = b; } + void setElevateMode(ElevateMode em) { m_ElevateMode = em; } void loadSettings(); @@ -126,7 +149,7 @@ class AppConfig QString m_Language; bool m_StartedBefore; bool m_AutoConfig; - bool m_ElevateMode; + ElevateMode m_ElevateMode; bool m_AutoConfigPrompted; int m_Edition; QString m_ActivateEmail; diff --git a/src/gui/src/IpcClient.cpp b/src/gui/src/IpcClient.cpp index 397ebdc5d..728a800cd 100644 --- a/src/gui/src/IpcClient.cpp +++ b/src/gui/src/IpcClient.cpp @@ -98,7 +98,7 @@ void IpcClient::sendHello() stream.writeRawData(typeBuf, 1); } -void IpcClient::sendCommand(const QString& command, bool elevate) +void IpcClient::sendCommand(const QString& command, ElevateMode const elevate) { QDataStream stream(m_Socket); @@ -114,7 +114,8 @@ void IpcClient::sendCommand(const QString& command, bool elevate) stream.writeRawData(charCommand, length); char elevateBuf[1]; - elevateBuf[0] = elevate ? 1 : 0; + // Refer to enum ElevateMode documentation for why this flag is mapped this way + elevateBuf[0] = (elevate == ElevateAlways) ? 1 : 0; stream.writeRawData(elevateBuf, 1); } diff --git a/src/gui/src/IpcClient.h b/src/gui/src/IpcClient.h index b1cd224ea..439211bd1 100644 --- a/src/gui/src/IpcClient.h +++ b/src/gui/src/IpcClient.h @@ -21,6 +21,8 @@ #include #include +#include "AppConfig.h" + class QTcpSocket; class IpcReader; @@ -33,7 +35,7 @@ class IpcClient : public QObject virtual ~IpcClient(); void sendHello(); - void sendCommand(const QString& command, bool elevate); + void sendCommand(const QString& command, ElevateMode elevate); void connectToHost(); void disconnectFromHost(); diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 727f2dfb9..b28a46edc 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -536,7 +536,14 @@ void MainWindow::startSynergy() // 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). - args << "--stop-on-desk-switch"; + // 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 } diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index fbe81f2af..f3deebba2 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -36,8 +36,7 @@ static const char networkSecurity[] = "ns"; SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), Ui::SettingsDialogBase(), - m_AppConfig(config), - m_SuppressElevateWarning(false) + m_AppConfig(config) { setupUi(this); @@ -53,14 +52,13 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) : m_pCheckBoxAutoHide->setChecked(appConfig().getAutoHide()); #if defined(Q_OS_WIN) - m_SuppressElevateWarning = true; - m_pCheckBoxElevateMode->setChecked(appConfig().elevateMode()); - m_SuppressElevateWarning = false; + m_pComboElevate->setCurrentIndex(static_cast(appConfig().elevateMode())); m_pCheckBoxAutoHide->hide(); #else // elevate checkbox is only useful on ms windows. - m_pCheckBoxElevateMode->hide(); + m_pLabelElevate->hide(); + m_pComboElevate->hide(); #endif if (!PluginManager::exist(networkSecurity)) { @@ -81,7 +79,7 @@ void SettingsDialog::accept() appConfig().setLogToFile(m_pCheckBoxLogToFile->isChecked()); appConfig().setLogFilename(m_pLineEditLogFilename->text()); appConfig().setLanguage(m_pComboLanguage->itemData(m_pComboLanguage->currentIndex()).toString()); - appConfig().setElevateMode(m_pCheckBoxElevateMode->isChecked()); + appConfig().setElevateMode(static_cast(m_pComboElevate->currentIndex())); appConfig().setAutoHide(m_pCheckBoxAutoHide->isChecked()); appConfig().saveSettings(); QDialog::accept(); @@ -147,24 +145,6 @@ void SettingsDialog::on_m_pComboLanguage_currentIndexChanged(int index) QSynergyApplication::getInstance()->switchTranslator(ietfCode); } -void SettingsDialog::on_m_pCheckBoxElevateMode_toggled(bool checked) -{ - if (checked && !m_SuppressElevateWarning) { - int r = QMessageBox::warning( - this, tr("Elevate Synergy"), - tr("Are you sure you want to elevate Synergy?\n\n" - "This allows Synergy to interact with elevated processes " - "and the UAC dialog, but can cause problems with non-elevated " - "processes. Elevate Synergy only if you really need to."), - QMessageBox::Yes | QMessageBox::No); - - if (r != QMessageBox::Yes) { - m_pCheckBoxElevateMode->setChecked(false); - return; - } - } -} - void SettingsDialog::on_m_pCheckBoxEnableCrypto_toggled(bool checked) { m_AppConfig.setCryptoEnabled(checked); diff --git a/src/gui/src/SettingsDialog.h b/src/gui/src/SettingsDialog.h index 8ab2e1e04..ea30ac32e 100644 --- a/src/gui/src/SettingsDialog.h +++ b/src/gui/src/SettingsDialog.h @@ -46,11 +46,9 @@ class SettingsDialog : public QDialog, public Ui::SettingsDialogBase AppConfig& m_AppConfig; SynergyLocale m_Locale; CoreInterface m_CoreInterface; - bool m_SuppressElevateWarning; private slots: void on_m_pCheckBoxEnableCrypto_toggled(bool checked); - void on_m_pCheckBoxElevateMode_toggled(bool checked); void on_m_pComboLanguage_currentIndexChanged(int index); void on_m_pCheckBoxLogToFile_stateChanged(int ); void on_m_pButtonBrowseLog_clicked(); From 21df3290c2ba1e59963d1cbd45620f85e53ef64c Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 10 Jun 2016 15:33:30 +0100 Subject: [PATCH 256/572] #4323 Refactored ElevateMode into its own header --- src/gui/gui.pro | 3 ++- src/gui/src/AppConfig.cpp | 1 + src/gui/src/AppConfig.h | 24 +----------------------- src/gui/src/ElevateMode.h | 41 +++++++++++++++++++++++++++++++++++++++++ src/gui/src/IpcClient.h | 2 +- 5 files changed, 46 insertions(+), 25 deletions(-) create mode 100644 src/gui/src/ElevateMode.h diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 73368b84d..408d44dda 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -111,7 +111,8 @@ HEADERS += src/MainWindow.h \ src/WebClient.h \ ../lib/common/PluginVersion.h \ src/SubscriptionManager.h \ - src/ActivationNotifier.h + src/ActivationNotifier.h \ + src/ElevateMode.h RESOURCES += res/Synergy.qrc RC_FILE = res/win/Synergy.rc macx { diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index 9f659738c..e6309d042 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -27,6 +27,7 @@ const char AppConfig::m_SynergysName[] = "synergys.exe"; const char AppConfig::m_SynergycName[] = "synergyc.exe"; const char AppConfig::m_SynergyLogDir[] = "log/"; +const ElevateMode defaultElevateMode = ElevateAsNeeded; #define DEFAULT_PROCESS_MODE Service #else const char AppConfig::m_SynergysName[] = "synergys"; diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index dcd5a4826..713753f90 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -21,6 +21,7 @@ #define APPCONFIG_H #include +#include "ElevateMode.h" // this should be incremented each time a new page is added. this is // saved to settings when the user finishes running the wizard. if @@ -46,29 +47,6 @@ enum ProcessMode { Desktop }; -// The elevate mode tristate determines two behaviours on Windows. -// The first, switch-on-desk-switch (SodS), passed through synergyd as a -// command line argument to synergy core, determines if the server restarts -// when switching Windows desktops (e.g. when Windows UAC dialog pops up). -// The second, passed as a boolean flag to Synergyd over the IPC inside -// kIpcCommandMessage, determines whether Synergy should be started with -// elevated privileges. -// -// The matrix for these two behaviours is as follows: -// SodS Elevate -// ___________________________ -// ElevateAsNeeded | true | false -// ElevateAlways | false | true -// ElevateNever | false | false -// -enum ElevateMode { - ElevateAsNeeded = 0, - ElevateAlways = 1, - ElevateNever = 2 -}; - -static const ElevateMode defaultElevateMode = ElevateAsNeeded; - class AppConfig { friend class SettingsDialog; diff --git a/src/gui/src/ElevateMode.h b/src/gui/src/ElevateMode.h new file mode 100644 index 000000000..7e39eb52a --- /dev/null +++ b/src/gui/src/ElevateMode.h @@ -0,0 +1,41 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless + * + * 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 + +// The elevate mode tristate determines two behaviours on Windows. +// The first, switch-on-desk-switch (SodS), passed through synergyd as a +// command line argument to synergy core, determines if the server restarts +// when switching Windows desktops (e.g. when Windows UAC dialog pops up). +// The second, passed as a boolean flag to Synergyd over the IPC inside +// kIpcCommandMessage, determines whether Synergy should be started with +// elevated privileges. +// +// The matrix for these two behaviours is as follows: +// SodS Elevate +// ___________________________ +// ElevateAsNeeded | true | false +// ElevateAlways | false | true +// ElevateNever | false | false +// +enum ElevateMode { + ElevateAsNeeded = 0, + ElevateAlways = 1, + ElevateNever = 2 +}; + +extern const ElevateMode defaultElevateMode; diff --git a/src/gui/src/IpcClient.h b/src/gui/src/IpcClient.h index 439211bd1..b49d9e6a1 100644 --- a/src/gui/src/IpcClient.h +++ b/src/gui/src/IpcClient.h @@ -21,7 +21,7 @@ #include #include -#include "AppConfig.h" +#include "ElevateMode.h" class QTcpSocket; class IpcReader; From e72e86c9b2dd2074e81112850ec21559aba11de6 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 10 Jun 2016 15:43:44 +0100 Subject: [PATCH 257/572] #4323 Fix build wrt ElevateMode on non-Windows platforms --- src/gui/src/AppConfig.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index e6309d042..f96c6633c 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -27,7 +27,6 @@ const char AppConfig::m_SynergysName[] = "synergys.exe"; const char AppConfig::m_SynergycName[] = "synergyc.exe"; const char AppConfig::m_SynergyLogDir[] = "log/"; -const ElevateMode defaultElevateMode = ElevateAsNeeded; #define DEFAULT_PROCESS_MODE Service #else const char AppConfig::m_SynergysName[] = "synergys"; @@ -36,6 +35,8 @@ const char AppConfig::m_SynergyLogDir[] = "/var/log/"; #define DEFAULT_PROCESS_MODE Desktop #endif +const ElevateMode defaultElevateMode = ElevateAsNeeded; + static const char* logLevelNames[] = { "ERROR", From d9a6c14170c8254527c85f48567ed11653729875 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 16 Jun 2016 15:18:31 +0000 Subject: [PATCH 258/572] #3305 Used Mac native way to simulate media keys --- src/lib/platform/OSXKeyState.cpp | 7 +++ src/lib/platform/OSXKeyState.h | 1 + src/lib/platform/OSXMediaKeySimulator.h | 30 +++++++++++ src/lib/platform/OSXMediaKeySimulator.m | 84 ++++++++++++++++++++++++++++++ src/lib/synergy/IKeyState.h | 9 +++- src/lib/synergy/IPlatformScreen.cpp | 24 +++++++++ src/lib/synergy/IPlatformScreen.h | 1 + src/lib/synergy/KeyState.cpp | 10 ++++ src/lib/synergy/KeyState.h | 2 + src/lib/synergy/key_types.h | 8 ++- src/test/unittests/synergy/KeyStateTests.h | 1 + 11 files changed, 171 insertions(+), 6 deletions(-) create mode 100644 src/lib/platform/OSXMediaKeySimulator.h create mode 100644 src/lib/platform/OSXMediaKeySimulator.m create mode 100644 src/lib/synergy/IPlatformScreen.cpp diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 9f3d923f7..03d5d54e7 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -18,6 +18,7 @@ #include "platform/OSXKeyState.h" #include "platform/OSXUchrKeyResource.h" +#include "platform/OSXMediaKeySimulator.h" #include "arch/Arch.h" #include "base/Log.h" @@ -316,6 +317,12 @@ OSXKeyState::fakeCtrlAltDel() return false; } +bool +COSXKeyState::fakeMediaKey(KeyID id) +{ + return fakeNativeMediaKey(id);; +} + CGEventFlags OSXKeyState::getModifierStateAsOSXFlags() { diff --git a/src/lib/platform/OSXKeyState.h b/src/lib/platform/OSXKeyState.h index 29cd5d6c6..2aaffc471 100644 --- a/src/lib/platform/OSXKeyState.h +++ b/src/lib/platform/OSXKeyState.h @@ -92,6 +92,7 @@ class OSXKeyState : public KeyState { // IKeyState overrides virtual bool fakeCtrlAltDel(); + virtual bool fakeMediaKey(KeyID id); virtual KeyModifierMask pollActiveModifiers() const; virtual SInt32 pollActiveGroup() const; diff --git a/src/lib/platform/OSXMediaKeySimulator.h b/src/lib/platform/OSXMediaKeySimulator.h new file mode 100644 index 000000000..277778df0 --- /dev/null +++ b/src/lib/platform/OSXMediaKeySimulator.h @@ -0,0 +1,30 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless. + * + * 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 COPYING 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 + +#import + +#include "synergy/key_types.h" + +#if defined(__cplusplus) +extern "C" { +#endif +bool fakeNativeMediaKey(KeyID id); +#if defined(__cplusplus) +} +#endif diff --git a/src/lib/platform/OSXMediaKeySimulator.m b/src/lib/platform/OSXMediaKeySimulator.m new file mode 100644 index 000000000..b7d243f6b --- /dev/null +++ b/src/lib/platform/OSXMediaKeySimulator.m @@ -0,0 +1,84 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless. + * + * 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 COPYING 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. + */ + +#import "platform/OSXMediaKeySimulator.h" + +#import + +int convertKeyIDToNXKeyType(KeyID id) +{ + // hidsystem/ev_keymap.h + // NX_KEYTYPE_SOUND_UP 0 + // NX_KEYTYPE_SOUND_DOWN 1 + // NX_KEYTYPE_MUTE 7 + // NX_KEYTYPE_EJECT 14 + // NX_KEYTYPE_PLAY 16 + // NX_KEYTYPE_NEXT 17 + // NX_KEYTYPE_PREVIOUS 18 + // NX_KEYTYPE_FAST 19 + // NX_KEYTYPE_REWIND 20 + + int type = -1; + switch (id) { + case kKeyAudioUp: + type = 0; + break; + case kKeyAudioDown: + type = 1; + break; + case kKeyAudioMute: + type = 7; + break; + case kKeyEject: + type = 14; + break; + case kKeyAudioPlay: + type = 16; + break; + case kKeyAudioNext: + type = 17; + break; + case kKeyAudioPrev: + type = 18; + break; + default: + break; + } + + return type; +} + +bool +fakeNativeMediaKey(KeyID id) +{ + + NSEvent* downRef = [NSEvent otherEventWithType:NSSystemDefined + location: NSMakePoint(0, 0) modifierFlags:0xa00 + timestamp:0 windowNumber:0 context:0 subtype:8 + data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xa) << 8) + data2:-1]; + CGEventRef downEvent = [downRef CGEvent]; + + NSEvent* upRef = [NSEvent otherEventWithType:NSSystemDefined + location: NSMakePoint(0, 0) modifierFlags:0xa00 + timestamp:0 windowNumber:0 context:0 subtype:8 + data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xb) << 8) + data2:-1]; + CGEventRef upEvent = [upRef CGEvent]; + + CGEventPost(0, downEvent); + CGEventPost(0, upEvent); + + return true; +} diff --git a/src/lib/synergy/IKeyState.h b/src/lib/synergy/IKeyState.h index 0e6484c11..da0062c4f 100644 --- a/src/lib/synergy/IKeyState.h +++ b/src/lib/synergy/IKeyState.h @@ -122,7 +122,14 @@ class IKeyState : public IInterface { complete and false if normal key processing should continue. */ virtual bool fakeCtrlAltDel() = 0; - + + //! Fake a media key + /*! + Synthesizes a media key down and up. Only Mac would implement this by + use cocoa appkit framework. + */ + virtual bool fakeMediaKey(KeyID id) = 0; + //@} //! @name accessors //@{ diff --git a/src/lib/synergy/IPlatformScreen.cpp b/src/lib/synergy/IPlatformScreen.cpp new file mode 100644 index 000000000..7c2b679f5 --- /dev/null +++ b/src/lib/synergy/IPlatformScreen.cpp @@ -0,0 +1,24 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless. + * + * 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 COPYING 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/IPlatformScreen.h" + +bool +IPlatformScreen::fakeMediaKey(KeyID id) +{ + return false; +} diff --git a/src/lib/synergy/IPlatformScreen.h b/src/lib/synergy/IPlatformScreen.h index 98b7668cd..cd00b23ce 100644 --- a/src/lib/synergy/IPlatformScreen.h +++ b/src/lib/synergy/IPlatformScreen.h @@ -180,6 +180,7 @@ class IPlatformScreen : public IScreen, virtual bool fakeKeyUp(KeyButton button) = 0; virtual void fakeAllKeysUp() = 0; virtual bool fakeCtrlAltDel() = 0; + virtual bool fakeMediaKey(KeyID id); virtual bool isKeyDown(KeyButton) const = 0; virtual KeyModifierMask getActiveModifiers() const = 0; diff --git a/src/lib/synergy/KeyState.cpp b/src/lib/synergy/KeyState.cpp index 79b775f3a..6adeb2433 100644 --- a/src/lib/synergy/KeyState.cpp +++ b/src/lib/synergy/KeyState.cpp @@ -571,8 +571,18 @@ KeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID) m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers, getActiveModifiersRValue(), mask, false); if (keyItem == NULL) { + // a media key won't be mapped on mac, so we need to fake it in a + // special way + if (id == kKeyAudioDown || id == kKeyAudioUp || id == kKeyAudioMute || + id == kKeyAudioPrev || id == kKeyAudioNext || + id == kKeyAudioPlay) { + LOG((CLOG_DEBUG "emulating media key")); + fakeMediaKey(id); + } + return; } + KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask); updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers); if (localID != 0) { diff --git a/src/lib/synergy/KeyState.h b/src/lib/synergy/KeyState.h index af9bcd6ae..3d148353d 100644 --- a/src/lib/synergy/KeyState.h +++ b/src/lib/synergy/KeyState.h @@ -73,6 +73,8 @@ class KeyState : public IKeyState { virtual bool fakeKeyUp(KeyButton button); virtual void fakeAllKeysUp(); virtual bool fakeCtrlAltDel() = 0; + virtual bool fakeMediaKey(KeyID id) = 0; + virtual bool isKeyDown(KeyButton) const; virtual KeyModifierMask getActiveModifiers() const; diff --git a/src/lib/synergy/key_types.h b/src/lib/synergy/key_types.h index e789e1401..71d195917 100644 --- a/src/lib/synergy/key_types.h +++ b/src/lib/synergy/key_types.h @@ -18,7 +18,7 @@ #pragma once -#include "base/EventTypes.h" +#include "common/basic_types.h" //! Key ID /*! @@ -284,12 +284,10 @@ static const KeyID kKeyAppUser2 = 0xE0B7; //@} struct KeyNameMapEntry { -public: const char* m_name; KeyID m_id; }; struct KeyModifierNameMapEntry { -public: const char* m_name; KeyModifierMask m_mask; }; @@ -300,11 +298,11 @@ A table of key names to the corresponding KeyID. Only the keys listed above plus non-alphanumeric ASCII characters are in the table. The end of the table is the first pair with a NULL m_name. */ -extern const KeyNameMapEntry kKeyNameMap[]; +extern const struct KeyNameMapEntry kKeyNameMap[]; //! Modifier key name to KeyModifierMask table /*! A table of modifier key names to the corresponding KeyModifierMask. The end of the table is the first pair with a NULL m_name. */ -extern const KeyModifierNameMapEntry kModifierNameMap[]; +extern const struct KeyModifierNameMapEntry kModifierNameMap[]; diff --git a/src/test/unittests/synergy/KeyStateTests.h b/src/test/unittests/synergy/KeyStateTests.h index 236bf3dc0..8a9846c07 100644 --- a/src/test/unittests/synergy/KeyStateTests.h +++ b/src/test/unittests/synergy/KeyStateTests.h @@ -45,6 +45,7 @@ class MockKeyState : public KeyState MOCK_METHOD0(fakeCtrlAltDel, bool()); MOCK_METHOD1(getKeyMap, void(synergy::KeyMap&)); MOCK_METHOD1(fakeKey, void(const Keystroke&)); + MOCK_METHOD2(fakeMediaKey, bool(KeyID, bool)); MOCK_CONST_METHOD1(pollPressedKeys, void(KeyButtonSet&)); }; From bd0d8e73219e64e20517c5ce5bb2c93bfe17fdeb Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 8 Jun 2016 16:49:04 +0100 Subject: [PATCH 259/572] #3305 Added default implementation for fakeMediaKey for Linux and Mac --- src/lib/synergy/KeyState.cpp | 8 +++++++- src/lib/synergy/KeyState.h | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lib/synergy/KeyState.cpp b/src/lib/synergy/KeyState.cpp index 6adeb2433..6d4a4436c 100644 --- a/src/lib/synergy/KeyState.cpp +++ b/src/lib/synergy/KeyState.cpp @@ -722,7 +722,13 @@ KeyState::fakeAllKeysUp() } bool -KeyState::isKeyDown(KeyButton button) const +CKeyState::fakeMediaKey(KeyID id) +{ + return false; +} + +bool +CKeyState::isKeyDown(KeyButton button) const { return (m_keys[button & kButtonMask] > 0); } diff --git a/src/lib/synergy/KeyState.h b/src/lib/synergy/KeyState.h index 3d148353d..2cebe1689 100644 --- a/src/lib/synergy/KeyState.h +++ b/src/lib/synergy/KeyState.h @@ -73,7 +73,7 @@ class KeyState : public IKeyState { virtual bool fakeKeyUp(KeyButton button); virtual void fakeAllKeysUp(); virtual bool fakeCtrlAltDel() = 0; - virtual bool fakeMediaKey(KeyID id) = 0; + virtual bool fakeMediaKey(KeyID id); virtual bool isKeyDown(KeyButton) const; virtual KeyModifierMask From 82d2a9564e2657ffa0716a7cb980e126c5c9ead1 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 16 Jun 2016 15:21:16 +0000 Subject: [PATCH 260/572] #3305 Made mock match to the original KeyState --- src/test/unittests/synergy/KeyStateTests.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/unittests/synergy/KeyStateTests.h b/src/test/unittests/synergy/KeyStateTests.h index 8a9846c07..1ab49a220 100644 --- a/src/test/unittests/synergy/KeyStateTests.h +++ b/src/test/unittests/synergy/KeyStateTests.h @@ -45,7 +45,7 @@ class MockKeyState : public KeyState MOCK_METHOD0(fakeCtrlAltDel, bool()); MOCK_METHOD1(getKeyMap, void(synergy::KeyMap&)); MOCK_METHOD1(fakeKey, void(const Keystroke&)); - MOCK_METHOD2(fakeMediaKey, bool(KeyID, bool)); + MOCK_METHOD1(fakeMediaKey, bool(KeyID)); MOCK_CONST_METHOD1(pollPressedKeys, void(KeyButtonSet&)); }; From b62de406e25824985f3596c888be5de0718a3bca Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 8 Jun 2016 17:08:14 +0100 Subject: [PATCH 261/572] #3305 Refactor MockKeyState into a separate class --- .../synergy/MockKeyState.h} | 19 +------------------ src/test/unittests/synergy/KeyStateTests.cpp | 22 +++++++++++++++++++--- 2 files changed, 20 insertions(+), 21 deletions(-) rename src/test/{unittests/synergy/KeyStateTests.h => mock/synergy/MockKeyState.h} (76%) diff --git a/src/test/unittests/synergy/KeyStateTests.h b/src/test/mock/synergy/MockKeyState.h similarity index 76% rename from src/test/unittests/synergy/KeyStateTests.h rename to src/test/mock/synergy/MockKeyState.h index 1ab49a220..78dc33568 100644 --- a/src/test/unittests/synergy/KeyStateTests.h +++ b/src/test/mock/synergy/MockKeyState.h @@ -54,21 +54,4 @@ typedef ::testing::NiceMock KeyStateImpl; typedef UInt32 KeyID; typedef void (*ForeachKeyCallback)( - KeyID, SInt32 group, synergy::KeyMap::KeyItem&, void* userData); - -void -stubPollPressedKeys(IKeyState::KeyButtonSet& pressedKeys); - -void -assertMaskIsOne(ForeachKeyCallback cb, void* userData); - -const synergy::KeyMap::KeyItem* -stubMapKey( - synergy::KeyMap::Keystrokes& keys, KeyID id, SInt32 group, - synergy::KeyMap::ModifierToKeys& activeModifiers, - KeyModifierMask& currentState, - KeyModifierMask desiredMask, - bool isAutoRepeat); - -synergy::KeyMap::Keystroke s_stubKeystroke(1, false, false); -synergy::KeyMap::KeyItem s_stubKeyItem; + KeyID, SInt32 group, CKeyMap::KeyItem&, void* userData); diff --git a/src/test/unittests/synergy/KeyStateTests.cpp b/src/test/unittests/synergy/KeyStateTests.cpp index 1d0f6a588..b75c7b88d 100644 --- a/src/test/unittests/synergy/KeyStateTests.cpp +++ b/src/test/unittests/synergy/KeyStateTests.cpp @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#include "test/unittests/synergy/KeyStateTests.h" - +#include "test/mock/synergy/MockKeyState.h" #include "test/mock/synergy/MockEventQueue.h" #include "test/mock/synergy/MockKeyMap.h" @@ -31,7 +30,24 @@ using ::testing::Return; using ::testing::ReturnRef; using ::testing::SaveArg; -TEST(KeyStateTests, onKey_aKeyDown_keyStateOne) +void +stubPollPressedKeys(IKeyState::KeyButtonSet& pressedKeys); + +void +assertMaskIsOne(ForeachKeyCallback cb, void* userData); + +const CKeyMap::KeyItem* +stubMapKey( + CKeyMap::Keystrokes& keys, KeyID id, SInt32 group, + CKeyMap::ModifierToKeys& activeModifiers, + KeyModifierMask& currentState, + KeyModifierMask desiredMask, + bool isAutoRepeat); + +CKeyMap::Keystroke s_stubKeystroke(1, false, false); +CKeyMap::KeyItem s_stubKeyItem; + +TEST(CKeyStateTests, onKey_aKeyDown_keyStateOne) { MockKeyMap keyMap; MockEventQueue eventQueue; From 67f14da6910e226153e5f91befa6b5e9b1994e9e Mon Sep 17 00:00:00 2001 From: "Xinyu Hou (Jerry)" Date: Tue, 14 Jun 2016 12:08:07 +0100 Subject: [PATCH 262/572] #3305 Added brightness up and down support --- src/lib/platform/XWindowsScreen.cpp | 10 +++++++++- src/lib/platform/XWindowsUtil.cpp | 4 ++-- src/lib/synergy/key_types.h | 4 ++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index 7579f66ff..218e48f86 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -1475,6 +1475,7 @@ XWindowsScreen::onKeyPress(XKeyEvent& xkey) keycode = static_cast(m_lastKeycode); if (keycode == 0) { // no keycode + LOG((CLOG_DEBUG1 "event: KeyPress no keycode")); return; } } @@ -1489,6 +1490,9 @@ XWindowsScreen::onKeyPress(XKeyEvent& xkey) false, false, key, mask, 1, keycode); } } + else { + LOG((CLOG_DEBUG1 "can't map keycode to key id")); + } } void @@ -1862,8 +1866,12 @@ XWindowsScreen::mapKeyFromX(XKeyEvent* event) const XLookupString(event, dummy, 0, &keysym, NULL); } + LOG((CLOG_DEBUG2 "mapped code=%d to keysym=0x%04x", event->keycode, keysym)); + // convert key - return XWindowsUtil::mapKeySymToKeyID(keysym); + KeyID result = CXWindowsUtil::mapKeySymToKeyID(keysym); + LOG((CLOG_DEBUG2 "mapped keysym=0x%04x to keyID=%d", keysym, result)); + return result; } ButtonID diff --git a/src/lib/platform/XWindowsUtil.cpp b/src/lib/platform/XWindowsUtil.cpp index 6b016d217..6b9f4dabf 100644 --- a/src/lib/platform/XWindowsUtil.cpp +++ b/src/lib/platform/XWindowsUtil.cpp @@ -1250,7 +1250,7 @@ XK_uhorn // map "Internet" keys to KeyIDs static const KeySym s_map1008FF[] = { - /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x00 */ 0, 0, kKeyBrightnessUp, kKeyBrightnessDown, 0, 0, 0, 0, /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */ 0, kKeyAudioDown, kKeyAudioMute, kKeyAudioUp, /* 0x14 */ kKeyAudioPlay, kKeyAudioStop, kKeyAudioPrev, kKeyAudioNext, @@ -1260,7 +1260,7 @@ static const KeySym s_map1008FF[] = /* 0x30 */ kKeyWWWFavorites, 0, kKeyAppMedia, 0, 0, 0, 0, 0, /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */ kKeyAppUser1, kKeyAppUser2, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, 0, 0, 0, 0, 0, 0, + /* 0x48 */ 0, 0, kKeyExposeDesktop, kKeyExposeApp, 0, 0, 0, 0, /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/src/lib/synergy/key_types.h b/src/lib/synergy/key_types.h index 71d195917..b02d80e0f 100644 --- a/src/lib/synergy/key_types.h +++ b/src/lib/synergy/key_types.h @@ -280,6 +280,10 @@ static const KeyID kKeyAppMail = 0xE0B4; static const KeyID kKeyAppMedia = 0xE0B5; static const KeyID kKeyAppUser1 = 0xE0B6; static const KeyID kKeyAppUser2 = 0xE0B7; +static const KeyID kKeyBrightnessDown = 0xE0B8; +static const KeyID kKeyBrightnessUp = 0xE0B9; +static const KeyID kKeyExposeDesktop = 0xE0C0; +static const KeyID kKeyExposeApp = 0xE0C1; //@} From ab4f59eac4dace4f21904c75f25df9e71139510c Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 16 Jun 2016 15:31:00 +0000 Subject: [PATCH 263/572] #3305 Made Mac clients aware of brightness keys --- src/lib/platform/OSXMediaKeySimulator.m | 26 +++++++++++++++++--------- src/lib/synergy/KeyState.cpp | 8 +++++--- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/lib/platform/OSXMediaKeySimulator.m b/src/lib/platform/OSXMediaKeySimulator.m index b7d243f6b..646807e31 100644 --- a/src/lib/platform/OSXMediaKeySimulator.m +++ b/src/lib/platform/OSXMediaKeySimulator.m @@ -19,15 +19,17 @@ int convertKeyIDToNXKeyType(KeyID id) { // hidsystem/ev_keymap.h - // NX_KEYTYPE_SOUND_UP 0 - // NX_KEYTYPE_SOUND_DOWN 1 - // NX_KEYTYPE_MUTE 7 - // NX_KEYTYPE_EJECT 14 - // NX_KEYTYPE_PLAY 16 - // NX_KEYTYPE_NEXT 17 - // NX_KEYTYPE_PREVIOUS 18 - // NX_KEYTYPE_FAST 19 - // NX_KEYTYPE_REWIND 20 + // NX_KEYTYPE_SOUND_UP 0 + // NX_KEYTYPE_SOUND_DOWN 1 + // NX_KEYTYPE_BRIGHTNESS_UP 2 + // NX_KEYTYPE_BRIGHTNESS_DOWN 3 + // NX_KEYTYPE_MUTE 7 + // NX_KEYTYPE_EJECT 14 + // NX_KEYTYPE_PLAY 16 + // NX_KEYTYPE_NEXT 17 + // NX_KEYTYPE_PREVIOUS 18 + // NX_KEYTYPE_FAST 19 + // NX_KEYTYPE_REWIND 20 int type = -1; switch (id) { @@ -37,6 +39,12 @@ int convertKeyIDToNXKeyType(KeyID id) case kKeyAudioDown: type = 1; break; + case kKeyBrightnessUp: + type = 2; + break; + case kKeyBrightnessDown: + type = 3; + break; case kKeyAudioMute: type = 7; break; diff --git a/src/lib/synergy/KeyState.cpp b/src/lib/synergy/KeyState.cpp index 6d4a4436c..a1ab6b7a6 100644 --- a/src/lib/synergy/KeyState.cpp +++ b/src/lib/synergy/KeyState.cpp @@ -573,10 +573,12 @@ KeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID) if (keyItem == NULL) { // a media key won't be mapped on mac, so we need to fake it in a // special way - if (id == kKeyAudioDown || id == kKeyAudioUp || id == kKeyAudioMute || + if (id == kKeyAudioDown || id == kKeyAudioUp || + id == kKeyAudioMute || id == kKeyAudioPlay || id == kKeyAudioPrev || id == kKeyAudioNext || - id == kKeyAudioPlay) { - LOG((CLOG_DEBUG "emulating media key")); + id == kKeyBrightnessDown || id == kKeyBrightnessUp + ) { + LOG((CLOG_DEBUG1 "emulating media key")); fakeMediaKey(id); } From 1a4a313ece261421cf607a4bf39cb5ea47ba5c01 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 15 Jun 2016 11:08:08 +0100 Subject: [PATCH 264/572] #3305 Added mission control and launchpad support --- src/lib/platform/OSXKeyState.cpp | 8 +++++++- src/lib/synergy/key_types.h | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 03d5d54e7..cf95fc92e 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -35,6 +35,9 @@ static const UInt32 s_superVK = kVK_Command; static const UInt32 s_capsLockVK = kVK_CapsLock; static const UInt32 s_numLockVK = kVK_ANSI_KeypadClear; // 71 +static const UInt32 s_missionControlVK = 160; +static const UInt32 s_launchpadVK = 131; + static const UInt32 s_osxNumLock = 1 << 16; struct KeyEntry { @@ -111,7 +114,10 @@ static const KeyEntry s_controlKeys[] = { // toggle modifiers { kKeyNumLock, s_numLockVK }, - { kKeyCapsLock, s_capsLockVK } + { kKeyCapsLock, s_capsLockVK }, + + { kKeyMissionControl, s_missionControlVK }, + { kKeyLaunchpad, s_launchpadVK } }; diff --git a/src/lib/synergy/key_types.h b/src/lib/synergy/key_types.h index b02d80e0f..eccfb0867 100644 --- a/src/lib/synergy/key_types.h +++ b/src/lib/synergy/key_types.h @@ -282,8 +282,8 @@ static const KeyID kKeyAppUser1 = 0xE0B6; static const KeyID kKeyAppUser2 = 0xE0B7; static const KeyID kKeyBrightnessDown = 0xE0B8; static const KeyID kKeyBrightnessUp = 0xE0B9; -static const KeyID kKeyExposeDesktop = 0xE0C0; -static const KeyID kKeyExposeApp = 0xE0C1; +static const KeyID kKeyMissionControl = 0xE0C0; +static const KeyID kKeyLaunchpad = 0xE0C1; //@} From df8443e795f9e2fc41523316124b090add236e99 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 16 Jun 2016 15:37:13 +0000 Subject: [PATCH 265/572] #3305 Fixed Linux key types incompatible issue --- src/lib/platform/XWindowsUtil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/XWindowsUtil.cpp b/src/lib/platform/XWindowsUtil.cpp index 6b9f4dabf..dbac968be 100644 --- a/src/lib/platform/XWindowsUtil.cpp +++ b/src/lib/platform/XWindowsUtil.cpp @@ -1260,7 +1260,7 @@ static const KeySym s_map1008FF[] = /* 0x30 */ kKeyWWWFavorites, 0, kKeyAppMedia, 0, 0, 0, 0, 0, /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 */ kKeyAppUser1, kKeyAppUser2, 0, 0, 0, 0, 0, 0, - /* 0x48 */ 0, 0, kKeyExposeDesktop, kKeyExposeApp, 0, 0, 0, 0, + /* 0x48 */ 0, 0, kKeyMissionControl, kKeyLaunchpad, 0, 0, 0, 0, /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0, From b27a59fb0d113a6910ea9f916072060399ebeaef Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 16 Jun 2016 15:41:20 +0000 Subject: [PATCH 266/572] #3305 Fixed class name convention from porting code --- src/lib/synergy/KeyState.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/synergy/KeyState.cpp b/src/lib/synergy/KeyState.cpp index a1ab6b7a6..a08caf0f6 100644 --- a/src/lib/synergy/KeyState.cpp +++ b/src/lib/synergy/KeyState.cpp @@ -724,13 +724,13 @@ KeyState::fakeAllKeysUp() } bool -CKeyState::fakeMediaKey(KeyID id) +KeyState::fakeMediaKey(KeyID id) { return false; } bool -CKeyState::isKeyDown(KeyButton button) const +KeyState::isKeyDown(KeyButton button) const { return (m_keys[button & kButtonMask] > 0); } From cc10e319d1c620a2019cc034a3eaf76feba75624 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 16 Jun 2016 08:57:39 -0700 Subject: [PATCH 267/572] #3305 Resolved namespace issue --- src/test/mock/synergy/MockKeyState.h | 2 +- src/test/unittests/synergy/KeyStateTests.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/mock/synergy/MockKeyState.h b/src/test/mock/synergy/MockKeyState.h index 78dc33568..0b05da40a 100644 --- a/src/test/mock/synergy/MockKeyState.h +++ b/src/test/mock/synergy/MockKeyState.h @@ -54,4 +54,4 @@ typedef ::testing::NiceMock KeyStateImpl; typedef UInt32 KeyID; typedef void (*ForeachKeyCallback)( - KeyID, SInt32 group, CKeyMap::KeyItem&, void* userData); + KeyID, SInt32 group, synergy::KeyMap::KeyItem&, void* userData); diff --git a/src/test/unittests/synergy/KeyStateTests.cpp b/src/test/unittests/synergy/KeyStateTests.cpp index b75c7b88d..1987881a2 100644 --- a/src/test/unittests/synergy/KeyStateTests.cpp +++ b/src/test/unittests/synergy/KeyStateTests.cpp @@ -36,16 +36,16 @@ stubPollPressedKeys(IKeyState::KeyButtonSet& pressedKeys); void assertMaskIsOne(ForeachKeyCallback cb, void* userData); -const CKeyMap::KeyItem* +const synergy::KeyMap::KeyItem* stubMapKey( - CKeyMap::Keystrokes& keys, KeyID id, SInt32 group, - CKeyMap::ModifierToKeys& activeModifiers, + synergy::KeyMap::Keystrokes& keys, KeyID id, SInt32 group, + synergy::KeyMap::ModifierToKeys& activeModifiers, KeyModifierMask& currentState, KeyModifierMask desiredMask, bool isAutoRepeat); -CKeyMap::Keystroke s_stubKeystroke(1, false, false); -CKeyMap::KeyItem s_stubKeyItem; +synergy::KeyMap::Keystroke s_stubKeystroke(1, false, false); +synergy::KeyMap::KeyItem s_stubKeyItem; TEST(CKeyStateTests, onKey_aKeyDown_keyStateOne) { From f4bd2dab8290912eed57ec7385e0a62ea227d763 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 16 Jun 2016 16:11:25 +0000 Subject: [PATCH 268/572] #3305 Fixed class name convention for Mac and Linux --- src/lib/platform/OSXKeyState.cpp | 2 +- src/lib/platform/XWindowsScreen.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index cf95fc92e..7646193d3 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -324,7 +324,7 @@ OSXKeyState::fakeCtrlAltDel() } bool -COSXKeyState::fakeMediaKey(KeyID id) +OSXKeyState::fakeMediaKey(KeyID id) { return fakeNativeMediaKey(id);; } diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index 218e48f86..eb68666e3 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -1869,7 +1869,7 @@ XWindowsScreen::mapKeyFromX(XKeyEvent* event) const LOG((CLOG_DEBUG2 "mapped code=%d to keysym=0x%04x", event->keycode, keysym)); // convert key - KeyID result = CXWindowsUtil::mapKeySymToKeyID(keysym); + KeyID result = XWindowsUtil::mapKeySymToKeyID(keysym); LOG((CLOG_DEBUG2 "mapped keysym=0x%04x to keyID=%d", keysym, result)); return result; } From c772a595720447ede2c733b92dc84df0a665e5fd Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 11 Aug 2016 16:59:15 +0100 Subject: [PATCH 269/572] Revert "Revert "Made index 1 based in log #2765"" This reverts commit fec53e812f3c3b75e51735288de94eb12b698090. --- src/lib/synergy/KeyMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/synergy/KeyMap.cpp b/src/lib/synergy/KeyMap.cpp index 1fff2c589..b4e10e2f4 100644 --- a/src/lib/synergy/KeyMap.cpp +++ b/src/lib/synergy/KeyMap.cpp @@ -696,7 +696,7 @@ KeyMap::findBestKey(const KeyEntryList& entryList, const KeyItem& item = entryList[i].back(); if ((item.m_required & desiredState) == (item.m_sensitive & desiredState)) { - LOG((CLOG_DEBUG1 "best key index %d of %d (exact)", i, entryList.size())); + LOG((CLOG_DEBUG1 "best key index %d of %d (exact)", i + 1, entryList.size())); return i; } } From 57950d39a308253652aa1b2b46d73299e92cfd7d Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 11 Aug 2016 16:59:36 +0100 Subject: [PATCH 270/572] Revert "Revert "Used input source ID as the key in group map #2765"" This reverts commit a70cba80ea3c35afcde25997b81377aba504b977. --- src/lib/platform/OSXKeyState.cpp | 18 +++++++++++------- src/lib/platform/OSXKeyState.h | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 7646193d3..78c794a81 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -392,15 +392,17 @@ OSXKeyState::pollActiveModifiers() const SInt32 OSXKeyState::pollActiveGroup() const { - bool layoutValid = true; TISInputSourceRef keyboardLayout = TISCopyCurrentKeyboardLayoutInputSource(); + CFDataRef id = (CFDataRef)TISGetInputSourceProperty( + keyboardLayout, kTISPropertyInputSourceID); - if (layoutValid) { - GroupMap::const_iterator i = m_groupMap.find(keyboardLayout); - if (i != m_groupMap.end()) { - return i->second; - } + GroupMap::const_iterator i = m_groupMap.find(id); + if (i != m_groupMap.end()) { + return i->second; } + + LOG((CLOG_DEBUG "can't get the active group, use the first group instead")); + return 0; } @@ -427,7 +429,9 @@ OSXKeyState::getKeyMap(synergy::KeyMap& keyMap) m_groupMap.clear(); SInt32 numGroups = (SInt32)m_groups.size(); for (SInt32 g = 0; g < numGroups; ++g) { - m_groupMap[m_groups[g]] = g; + CFDataRef id = (CFDataRef)TISGetInputSourceProperty( + m_groups[g], kTISPropertyInputSourceID); + m_groupMap[id] = g; } } diff --git a/src/lib/platform/OSXKeyState.h b/src/lib/platform/OSXKeyState.h index 2aaffc471..26050a284 100644 --- a/src/lib/platform/OSXKeyState.h +++ b/src/lib/platform/OSXKeyState.h @@ -159,7 +159,7 @@ class OSXKeyState : public KeyState { KeyButtonOffset = 1 }; - typedef std::map GroupMap; + typedef std::map GroupMap; typedef std::map VirtualKeyMap; VirtualKeyMap m_virtualKeyMap; From 7a0fae2f6cb83dfa59c951589c5b382f2a6ed4cd Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 11 Aug 2016 17:01:09 +0100 Subject: [PATCH 271/572] #2765 Fixed wrong bit shifting to match OSX modifier mask --- src/lib/platform/OSXKeyState.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 78c794a81..1bd6d08a0 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -698,10 +698,10 @@ OSXKeyState::getKeyMap(synergy::KeyMap& keyMap, } // now add a key entry for each key/required modifier pair. - item.m_sensitive = mapModifiersFromOSX(sensitive << 8); + item.m_sensitive = mapModifiersFromOSX(sensitive << 16); for (std::set::iterator k = required.begin(); k != required.end(); ++k) { - item.m_required = mapModifiersFromOSX(*k << 8); + item.m_required = mapModifiersFromOSX(*k << 16); keyMap.addKeyEntry(item); } } From bbb652e5047fef8e3dffc5d14cb1f1fb4797ca9b Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 12 Aug 2016 14:22:06 +0100 Subject: [PATCH 272/572] #2765 Added unit test for modifier mapping --- src/test/unittests/CMakeLists.txt | 19 ++++++++ src/test/unittests/platform/OSXKeyStateTests.cpp | 57 ++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/test/unittests/platform/OSXKeyStateTests.cpp diff --git a/src/test/unittests/CMakeLists.txt b/src/test/unittests/CMakeLists.txt index ba919a168..65d05d803 100644 --- a/src/test/unittests/CMakeLists.txt +++ b/src/test/unittests/CMakeLists.txt @@ -17,6 +17,10 @@ file(GLOB_RECURSE headers "*.h") file(GLOB_RECURSE sources "*.cpp") +file(GLOB_RECURSE remove_platform "platform/*") +list(REMOVE_ITEM headers ${remove_platform}) +list(REMOVE_ITEM sources ${remove_platform}) + file(GLOB_RECURSE global_headers "../../test/global/*.h") file(GLOB_RECURSE global_sources "../../test/global/*.cpp") @@ -29,6 +33,21 @@ file(GLOB_RECURSE mock_sources "../../test/mock/*.cpp") list(APPEND headers ${mock_headers}) list(APPEND sources ${mock_sources}) +# platform +if (WIN32) + file(GLOB platform_sources "platform/MSWindows*.cpp") + file(GLOB platform_headers "platform/MSWindows*.h") +elseif (APPLE) + file(GLOB platform_sources "platform/OSX*.cpp") + file(GLOB platform_headers "platform/OSX*.h") +elseif (UNIX) + file(GLOB platform_sources "platform/XWindows*.cpp") + file(GLOB platform_headers "platform/XWindows*.h") +endif() + +list(APPEND sources ${platform_headers}) +list(APPEND headers ${platform_sources}) + include_directories( ../../ ../../lib/ diff --git a/src/test/unittests/platform/OSXKeyStateTests.cpp b/src/test/unittests/platform/OSXKeyStateTests.cpp new file mode 100644 index 000000000..c5190e833 --- /dev/null +++ b/src/test/unittests/platform/OSXKeyStateTests.cpp @@ -0,0 +1,57 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2011 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 "test/mock/synergy/MockKeyMap.h" +#include "test/mock/synergy/MockEventQueue.h" +#include "platform/OSXKeyState.h" + +#include "test/global/gtest.h" +#include "test/global/gmock.h" + +TEST(OSXKeyStateTests, mapModifiersFromOSX_OSXMask_returnSynergyMask) +{ + synergy::KeyMap keyMap; + MockEventQueue eventQueue; + OSXKeyState keyState(&eventQueue, keyMap); + + KeyModifierMask outMask = 0; + + UInt32 shiftMask = 0 | kCGEventFlagMaskShift; + outMask = keyState.mapModifiersFromOSX(shiftMask); + EXPECT_EQ(KeyModifierShift, outMask); + + UInt32 ctrlMask = 0 | kCGEventFlagMaskControl; + outMask = keyState.mapModifiersFromOSX(ctrlMask); + EXPECT_EQ(KeyModifierControl, outMask); + + UInt32 altMask = 0 | kCGEventFlagMaskAlternate; + outMask = keyState.mapModifiersFromOSX(altMask); + EXPECT_EQ(KeyModifierAlt, outMask); + + UInt32 cmdMask = 0 | kCGEventFlagMaskCommand; + outMask = keyState.mapModifiersFromOSX(cmdMask); + EXPECT_EQ(KeyModifierSuper, outMask); + + UInt32 capsMask = 0 | kCGEventFlagMaskAlphaShift; + outMask = keyState.mapModifiersFromOSX(capsMask); + EXPECT_EQ(KeyModifierCapsLock, outMask); + + UInt32 numMask = 0 | kCGEventFlagMaskNumericPad; + outMask = keyState.mapModifiersFromOSX(numMask); + EXPECT_EQ(KeyModifierNumLock, outMask); +} From 178b7cc6731af5bca62c9e811b3670722456289b Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Tue, 16 Aug 2016 14:45:00 +0100 Subject: [PATCH 273/572] #2765 Made sure required modifiers already down when map command keys --- src/lib/synergy/KeyMap.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/lib/synergy/KeyMap.cpp b/src/lib/synergy/KeyMap.cpp index b4e10e2f4..656434cac 100644 --- a/src/lib/synergy/KeyMap.cpp +++ b/src/lib/synergy/KeyMap.cpp @@ -535,14 +535,17 @@ KeyMap::mapCommandKey(Keystrokes& keys, KeyID id, SInt32 group, continue; } - // only match based on shift; we're after the right button - // not the right character. we'll use desiredMask as-is, - // overriding the key's required modifiers, when synthesizing - // this button. + // match based on shift and make sure all required modifiers, + // except shift, are already in the desired mask; we're + // after the right button not the right character. + // we'll use desiredMask as-is, overriding the key's required + // modifiers, when synthesizing this button. const KeyItem& item = entryList[i].back(); - if ((item.m_required & KeyModifierShift & desiredMask) == - (item.m_sensitive & KeyModifierShift & desiredMask)) { - LOG((CLOG_DEBUG1 "found key in group %d", effectiveGroup)); + KeyModifierMask desiredShiftMask = KeyModifierShift & desiredMask; + KeyModifierMask requiredIgnoreShiftMask = item.m_required & ~KeyModifierShift; + if ((item.m_required & desiredShiftMask) == (item.m_sensitive & desiredShiftMask) && + ((requiredIgnoreShiftMask & desiredMask) == requiredIgnoreShiftMask)) { + LOG((CLOG_INFO "found key in group %d", effectiveGroup)); keyItem = &item; break; } From b80760bb2d7d4d32313b494da5a5e31ab93e97b2 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 17 Aug 2016 12:43:01 +0100 Subject: [PATCH 274/572] #2765 Matched exactly only when all required modifiers are ready down --- src/lib/synergy/KeyMap.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/synergy/KeyMap.cpp b/src/lib/synergy/KeyMap.cpp index 656434cac..db37c6111 100644 --- a/src/lib/synergy/KeyMap.cpp +++ b/src/lib/synergy/KeyMap.cpp @@ -697,8 +697,7 @@ KeyMap::findBestKey(const KeyEntryList& entryList, // check for an item that can accommodate the desiredState exactly for (SInt32 i = 0; i < (SInt32)entryList.size(); ++i) { const KeyItem& item = entryList[i].back(); - if ((item.m_required & desiredState) == - (item.m_sensitive & desiredState)) { + if ((item.m_required & desiredState) == item.m_required) { LOG((CLOG_DEBUG1 "best key index %d of %d (exact)", i + 1, entryList.size())); return i; } From adaf325e6f4028fbff91724b646925045f2f8426 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 17 Aug 2016 15:58:07 +0100 Subject: [PATCH 275/572] #2765 Made sure extra sensitive modifier is not down on exact match --- src/lib/synergy/KeyMap.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/synergy/KeyMap.cpp b/src/lib/synergy/KeyMap.cpp index db37c6111..405334fcd 100644 --- a/src/lib/synergy/KeyMap.cpp +++ b/src/lib/synergy/KeyMap.cpp @@ -697,7 +697,8 @@ KeyMap::findBestKey(const KeyEntryList& entryList, // check for an item that can accommodate the desiredState exactly for (SInt32 i = 0; i < (SInt32)entryList.size(); ++i) { const KeyItem& item = entryList[i].back(); - if ((item.m_required & desiredState) == item.m_required) { + if ((item.m_required & desiredState) == item.m_required && + (item.m_required & desiredState) == (item.m_sensitive & desiredState)) { LOG((CLOG_DEBUG1 "best key index %d of %d (exact)", i + 1, entryList.size())); return i; } From 2ffaf42faae0af4cb7496ad0a9649a9b12ae7714 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 17 Aug 2016 15:58:56 +0100 Subject: [PATCH 276/572] #2765 Made index output 1 based --- src/lib/synergy/KeyMap.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/synergy/KeyMap.cpp b/src/lib/synergy/KeyMap.cpp index 405334fcd..30d1b24b7 100644 --- a/src/lib/synergy/KeyMap.cpp +++ b/src/lib/synergy/KeyMap.cpp @@ -718,7 +718,8 @@ KeyMap::findBestKey(const KeyEntryList& entryList, } } if (bestIndex != -1) { - LOG((CLOG_DEBUG1 "best key index %d of %d (%d modifiers)", bestIndex, entryList.size(), bestCount)); + LOG((CLOG_DEBUG1 "best key index %d of %d (%d modifiers)", + bestIndex + 1, entryList.size(), bestCount)); } return bestIndex; From de91be103579b53181a8f1b5c02bfd5e3eec0776 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 17 Aug 2016 16:16:33 +0100 Subject: [PATCH 277/572] #2765 Added unit tests for KeyMap --- src/lib/platform/CMakeLists.txt | 1 + src/lib/server/CMakeLists.txt | 1 + src/lib/synergy/KeyMap.h | 17 +++ src/test/unittests/synergy/KeyMapTests.cpp | 216 +++++++++++++++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 src/test/unittests/synergy/KeyMapTests.cpp diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index 2070d487b..00e5d1bcb 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -31,6 +31,7 @@ endif() include_directories( ../ + ../../../ext/gtest-1.6.0/include ) if (UNIX) diff --git a/src/lib/server/CMakeLists.txt b/src/lib/server/CMakeLists.txt index 891ffc447..ecafba8c9 100644 --- a/src/lib/server/CMakeLists.txt +++ b/src/lib/server/CMakeLists.txt @@ -24,6 +24,7 @@ endif() include_directories( ../ ../../../ext + ../../../ext/gtest-1.6.0/include ) if (UNIX) diff --git a/src/lib/synergy/KeyMap.h b/src/lib/synergy/KeyMap.h index 57d9c373c..991b32696 100644 --- a/src/lib/synergy/KeyMap.h +++ b/src/lib/synergy/KeyMap.h @@ -24,6 +24,8 @@ #include "common/stdset.h" #include "common/stdvector.h" +#include "gtest/gtest_prod.h" + namespace synergy { //! Key map @@ -324,6 +326,21 @@ class KeyMap { //@} +private: + FRIEND_TEST(KeyMapTests, + findBestKey_requiredDown_matchExactFirstItem); + FRIEND_TEST(KeyMapTests, + findBestKey_requiredAndExtraSensitiveDown_matchExactFirstItem); + FRIEND_TEST(KeyMapTests, + findBestKey_requiredAndExtraSensitiveDown_matchExactSecondItem); + FRIEND_TEST(KeyMapTests, + findBestKey_extraSensitiveDown_matchExactSecondItem); + FRIEND_TEST(KeyMapTests, + findBestKey_noRequiredDown_matchOneRequiredChangeItem); + FRIEND_TEST(KeyMapTests, + findBestKey_onlyOneRequiredDown_matchTwoRequiredChangesItem); + FRIEND_TEST(KeyMapTests, findBestKey_noRequiredDown_cannotMatch); + private: //! Ways to synthesize a key enum EKeystroke { diff --git a/src/test/unittests/synergy/KeyMapTests.cpp b/src/test/unittests/synergy/KeyMapTests.cpp new file mode 100644 index 000000000..30b5b6db8 --- /dev/null +++ b/src/test/unittests/synergy/KeyMapTests.cpp @@ -0,0 +1,216 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 Symless + * + * 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/KeyMap.h" + +#include "test/global/gtest.h" +#include "test/global/gmock.h" + +using ::testing::_; +using ::testing::NiceMock; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::ReturnRef; +using ::testing::SaveArg; + +namespace synergy { + +TEST(KeyMapTests, findBestKey_requiredDown_matchExactFirstItem) +{ + KeyMap keyMap; + KeyMap::KeyEntryList entryList; + KeyMap::KeyItemList itemList; + KeyMap::KeyItem item; + item.m_required = KeyModifierShift; + item.m_sensitive = KeyModifierShift; + KeyModifierMask currentState = KeyModifierShift; + KeyModifierMask desiredState = KeyModifierShift; + itemList.push_back(item); + entryList.push_back(itemList); + + EXPECT_EQ(0, keyMap.findBestKey(entryList, currentState, desiredState)); +} + +TEST(KeyMapTests, findBestKey_requiredAndExtraSensitiveDown_matchExactFirstItem) +{ + KeyMap keyMap; + KeyMap::KeyEntryList entryList; + KeyMap::KeyItemList itemList; + KeyMap::KeyItem item; + item.m_required = KeyModifierShift; + item.m_sensitive = KeyModifierShift | KeyModifierAlt; + KeyModifierMask currentState = KeyModifierShift; + KeyModifierMask desiredState = KeyModifierShift; + itemList.push_back(item); + entryList.push_back(itemList); + + EXPECT_EQ(0, keyMap.findBestKey(entryList, currentState, desiredState)); +} + +TEST(KeyMapTests, findBestKey_requiredAndExtraSensitiveDown_matchExactSecondItem) +{ + KeyMap keyMap; + KeyMap::KeyEntryList entryList; + KeyMap::KeyItemList itemList1; + KeyMap::KeyItem item1; + item1.m_required = KeyModifierAlt; + item1.m_sensitive = KeyModifierShift | KeyModifierAlt; + KeyMap::KeyItemList itemList2; + KeyMap::KeyItem item2; + item2.m_required = KeyModifierShift; + item2.m_sensitive = KeyModifierShift | KeyModifierAlt; + KeyModifierMask currentState = KeyModifierShift; + KeyModifierMask desiredState = KeyModifierShift; + itemList1.push_back(item1); + itemList2.push_back(item2); + entryList.push_back(itemList1); + entryList.push_back(itemList2); + + EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState)); +} + +TEST(KeyMapTests, findBestKey_extraSensitiveDown_matchExactSecondItem) +{ + KeyMap keyMap; + KeyMap::KeyEntryList entryList; + KeyMap::KeyItemList itemList1; + KeyMap::KeyItem item1; + item1.m_required = 0; + item1.m_sensitive = KeyModifierAlt; + KeyMap::KeyItemList itemList2; + KeyMap::KeyItem item2; + item2.m_required = 0; + item2.m_sensitive = KeyModifierShift; + KeyModifierMask currentState = KeyModifierAlt; + KeyModifierMask desiredState = KeyModifierAlt; + itemList1.push_back(item1); + itemList2.push_back(item2); + entryList.push_back(itemList1); + entryList.push_back(itemList2); + + EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState)); +} + +TEST(KeyMapTests, findBestKey_noRequiredDown_matchOneRequiredChangeItem) +{ + KeyMap keyMap; + KeyMap::KeyEntryList entryList; + KeyMap::KeyItemList itemList1; + KeyMap::KeyItem item1; + item1.m_required = KeyModifierShift | KeyModifierAlt; + item1.m_sensitive = KeyModifierShift | KeyModifierAlt; + KeyMap::KeyItemList itemList2; + KeyMap::KeyItem item2; + item2.m_required = KeyModifierShift; + item2.m_sensitive = KeyModifierShift | KeyModifierAlt; + KeyModifierMask currentState = 0; + KeyModifierMask desiredState = 0; + itemList1.push_back(item1); + itemList2.push_back(item2); + entryList.push_back(itemList1); + entryList.push_back(itemList2); + + EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState)); +} + +TEST(KeyMapTests, findBestKey_onlyOneRequiredDown_matchTwoRequiredChangesItem) +{ + KeyMap keyMap; + KeyMap::KeyEntryList entryList; + KeyMap::KeyItemList itemList1; + KeyMap::KeyItem item1; + item1.m_required = KeyModifierShift | KeyModifierAlt | KeyModifierControl; + item1.m_sensitive = KeyModifierShift | KeyModifierAlt | KeyModifierControl; + KeyMap::KeyItemList itemList2; + KeyMap::KeyItem item2; + item2.m_required = KeyModifierShift| KeyModifierAlt; + item2.m_sensitive = KeyModifierShift | KeyModifierAlt | KeyModifierControl; + KeyModifierMask currentState = 0; + KeyModifierMask desiredState = 0; + itemList1.push_back(item1); + itemList2.push_back(item2); + entryList.push_back(itemList1); + entryList.push_back(itemList2); + + EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState)); +} + +TEST(KeyMapTests, findBestKey_noRequiredDown_cannotMatch) +{ + KeyMap keyMap; + KeyMap::KeyEntryList entryList; + KeyMap::KeyItemList itemList; + KeyMap::KeyItem item; + item.m_required = 0xffffffff; + item.m_sensitive = 0xffffffff; + KeyModifierMask currentState = 0; + KeyModifierMask desiredState = 0; + itemList.push_back(item); + entryList.push_back(itemList); + + EXPECT_EQ(-1, keyMap.findBestKey(entryList, currentState, desiredState)); +} + +TEST(KeyMapTests, isCommand_shiftMask_returnFalse) +{ + KeyMap keyMap; + KeyModifierMask mask= KeyModifierShift; + + EXPECT_EQ(false, keyMap.isCommand(mask)); +} + +TEST(KeyMapTests, isCommand_controlMask_returnTrue) +{ + KeyMap keyMap; + KeyModifierMask mask= KeyModifierControl; + + EXPECT_EQ(true, keyMap.isCommand(mask)); +} + +TEST(KeyMapTests, isCommand_alternateMask_returnTrue) +{ + KeyMap keyMap; + KeyModifierMask mask= KeyModifierAlt; + + EXPECT_EQ(true, keyMap.isCommand(mask)); +} + +TEST(KeyMapTests, isCommand_alternateGraphicMask_returnTrue) +{ + KeyMap keyMap; + KeyModifierMask mask= KeyModifierAltGr; + + EXPECT_EQ(true, keyMap.isCommand(mask)); +} + +TEST(KeyMapTests, isCommand_metaMask_returnTrue) +{ + KeyMap keyMap; + KeyModifierMask mask= KeyModifierMeta; + + EXPECT_EQ(true, keyMap.isCommand(mask)); +} + +TEST(KeyMapTests, isCommand_superMask_returnTrue) +{ + KeyMap keyMap; + KeyModifierMask mask= KeyModifierSuper; + + EXPECT_EQ(true, keyMap.isCommand(mask)); +} + +} From 47ea124c777fbfdd1d3c36442de4738e2b7a3d12 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Wed, 17 Aug 2016 08:49:55 -0700 Subject: [PATCH 278/572] #2765 Used EXPECT_FALSE instead of EXPECT_EQ --- src/test/unittests/synergy/KeyMapTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/unittests/synergy/KeyMapTests.cpp b/src/test/unittests/synergy/KeyMapTests.cpp index 30b5b6db8..1a195af5f 100644 --- a/src/test/unittests/synergy/KeyMapTests.cpp +++ b/src/test/unittests/synergy/KeyMapTests.cpp @@ -170,7 +170,7 @@ TEST(KeyMapTests, isCommand_shiftMask_returnFalse) KeyMap keyMap; KeyModifierMask mask= KeyModifierShift; - EXPECT_EQ(false, keyMap.isCommand(mask)); + EXPECT_FALSE(keyMap.isCommand(mask)); } TEST(KeyMapTests, isCommand_controlMask_returnTrue) From 10add6c231a1c7449200531ab8fd05c6db7d7a3d Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 19 Aug 2016 11:09:22 +0100 Subject: [PATCH 279/572] Versioned to 1.8.3-rc1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d2513457..2d5f72a2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 3) -set(VERSION_STAGE beta) +set(VERSION_STAGE rc1) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From e4b61cd881ca758c5d8f5c3417bb2f4c393c0bdf Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 19 Aug 2016 15:47:22 +0100 Subject: [PATCH 280/572] Updated ChangeLog --- ChangeLog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ChangeLog b/ChangeLog index 9f641e2bf..4cefb2049 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +v1.8.3-stable +============= +Bug #2765 - A letter is typed when spacebar is pressed on Mac client +Bug #5373 - Tab behaves like shift tab on client +Bug #3241 - UAC causes discconnection while in elevate mode +Enhancement #4323 - Make auto-elevate optional +Enhancement #3305 - Media key support for Mac clients + v1.8.2-stable ============= Bug #3044 - Unable to drag-select in MS Office From 7d11004575ec542df27531dbe30f212792372d28 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 5 Sep 2016 16:19:13 +0100 Subject: [PATCH 281/572] #4768 Expand scope of X display lock in getCurrentTime() --- src/lib/platform/XWindowsUtil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/XWindowsUtil.cpp b/src/lib/platform/XWindowsUtil.cpp index dbac968be..aec69a070 100644 --- a/src/lib/platform/XWindowsUtil.cpp +++ b/src/lib/platform/XWindowsUtil.cpp @@ -1429,10 +1429,10 @@ XWindowsUtil::setWindowProperty(Display* display, Window window, Time XWindowsUtil::getCurrentTime(Display* display, Window window) { + XLockDisplay(display); // select property events on window XWindowAttributes attr; XGetWindowAttributes(display, window, &attr); - XLockDisplay(display); XSelectInput(display, window, attr.your_event_mask | PropertyChangeMask); // make a property name to receive dummy change From 36be2051c227df94fb1a7604909bc803ba02c3bc Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 6 Sep 2016 16:13:50 +0100 Subject: [PATCH 282/572] Change company domain name --- COMPILE | 2 +- INSTALL | 4 ++-- README | 2 +- doc/MacReadme.txt | 2 +- doc/org.synergy-foss.org.synergyc.plist | 2 +- doc/org.synergy-foss.org.synergys.plist | 2 +- ext/toolchain/commands1.py | 2 +- res/Readme.txt | 4 ++-- res/deb/changelog | 2 +- res/deb/control.in | 2 +- res/synergy.nsh | 2 +- res/synergy.spec.in | 8 ++++---- src/gui/gui.ts | 6 +++--- src/gui/res/AboutDialogBase.ui | 2 +- src/gui/res/SetupWizardBase.ui | 2 +- src/gui/res/lang/gui_af-ZA.ts | 6 +++--- src/gui/res/lang/gui_ar.ts | 6 +++--- src/gui/res/lang/gui_bg-BG.ts | 6 +++--- src/gui/res/lang/gui_ca-AD.ts | 6 +++--- src/gui/res/lang/gui_cs-CZ.ts | 6 +++--- src/gui/res/lang/gui_cy.ts | 6 +++--- src/gui/res/lang/gui_da.ts | 6 +++--- src/gui/res/lang/gui_de.ts | 6 +++--- src/gui/res/lang/gui_es.ts | 6 +++--- src/gui/res/lang/gui_et-EE.ts | 6 +++--- src/gui/res/lang/gui_fi.ts | 6 +++--- src/gui/res/lang/gui_fr.ts | 6 +++--- src/gui/res/lang/gui_gl.ts | 6 +++--- src/gui/res/lang/gui_grk.ts | 6 +++--- src/gui/res/lang/gui_he.ts | 6 +++--- src/gui/res/lang/gui_hi.ts | 6 +++--- src/gui/res/lang/gui_hr-HR.ts | 6 +++--- src/gui/res/lang/gui_hu-HU.ts | 6 +++--- src/gui/res/lang/gui_id.ts | 6 +++--- src/gui/res/lang/gui_is-IS.ts | 6 +++--- src/gui/res/lang/gui_it.ts | 6 +++--- src/gui/res/lang/gui_ja-JP.ts | 6 +++--- src/gui/res/lang/gui_ko.ts | 6 +++--- src/gui/res/lang/gui_lt.ts | 6 +++--- src/gui/res/lang/gui_lv.ts | 6 +++--- src/gui/res/lang/gui_mr.ts | 6 +++--- src/gui/res/lang/gui_nl-NL.ts | 6 +++--- src/gui/res/lang/gui_no.ts | 6 +++--- src/gui/res/lang/gui_pes-IR.ts | 6 +++--- src/gui/res/lang/gui_pl-PL.ts | 6 +++--- src/gui/res/lang/gui_pt-BR.ts | 6 +++--- src/gui/res/lang/gui_pt-PT.ts | 6 +++--- src/gui/res/lang/gui_ro.ts | 6 +++--- src/gui/res/lang/gui_ru.ts | 6 +++--- src/gui/res/lang/gui_si.ts | 6 +++--- src/gui/res/lang/gui_sk-SK.ts | 6 +++--- src/gui/res/lang/gui_sl-SI.ts | 6 +++--- src/gui/res/lang/gui_sq-AL.ts | 6 +++--- src/gui/res/lang/gui_sr.ts | 6 +++--- src/gui/res/lang/gui_sv.ts | 6 +++--- src/gui/res/lang/gui_th-TH.ts | 6 +++--- src/gui/res/lang/gui_tr-TR.ts | 6 +++--- src/gui/res/lang/gui_uk.ts | 6 +++--- src/gui/res/lang/gui_ur.ts | 6 +++--- src/gui/res/lang/gui_vi.ts | 6 +++--- src/gui/res/lang/gui_zh-CN.ts | 6 +++--- src/gui/res/lang/gui_zh-TW.ts | 6 +++--- src/gui/src/MainWindow.cpp | 4 ++-- src/gui/src/Plugin.cpp | 2 +- src/gui/src/SubscriptionManager.cpp | 2 +- src/gui/src/VersionChecker.cpp | 2 +- src/gui/src/main.cpp | 2 +- src/lib/common/Version.cpp | 4 ++-- src/lib/platform/MSWindowsClipboard.cpp | 4 ++-- src/lib/platform/MSWindowsWatchdog.cpp | 2 +- src/lib/platform/OSXScreen.cpp | 2 +- src/lib/synergy/ToolApp.cpp | 2 +- src/test/integtests/arch/ArchInternetTests.cpp | 2 +- src/test/integtests/platform/MSWindowsClipboardTests.cpp | 4 ++-- 74 files changed, 179 insertions(+), 179 deletions(-) diff --git a/COMPILE b/COMPILE index ac5c398c9..e05a82363 100644 --- a/COMPILE +++ b/COMPILE @@ -1 +1 @@ -Compiling: http://synergy-project.org/wiki/Compiling +Compiling: http://symless.com/wiki/Compiling diff --git a/INSTALL b/INSTALL index aa8971f85..77af84443 100644 --- a/INSTALL +++ b/INSTALL @@ -1,2 +1,2 @@ -Help: http://synergy-project.org/help/ -Wiki: http://synergy-project.org/wiki/ +Help: http://symless.com/help/ +Wiki: http://symless.com/wiki/ diff --git a/README b/README index b6bdee305..b2d40b5d8 100644 --- a/README +++ b/README @@ -11,6 +11,6 @@ Just use "hm conf" and "hm build" to compile (./hm.sh on Linux and Mac). For detailed compile instructions: -http://synergy-project.org/wiki/Compiling +http://symless.com/wiki/Compiling Happy hacking! diff --git a/doc/MacReadme.txt b/doc/MacReadme.txt index da2930505..49e6d93a2 100755 --- a/doc/MacReadme.txt +++ b/doc/MacReadme.txt @@ -15,4 +15,4 @@ Once the binaries have been copied to /usr/bin, you should follow the configurat http://synergy2.sourceforge.net/configuration.html If you have any problems, see the [[Support]] page: - http://synergy-project.org/help/ + http://symless.com/help/ diff --git a/doc/org.synergy-foss.org.synergyc.plist b/doc/org.synergy-foss.org.synergyc.plist index f0f2e1e02..385ad74b3 100644 --- a/doc/org.synergy-foss.org.synergyc.plist +++ b/doc/org.synergy-foss.org.synergyc.plist @@ -5,7 +5,7 @@ Label - org.synergy-project.org.synergyc.plist + org.symless.com.synergyc.plist OnDemand ProgramArguments diff --git a/doc/org.synergy-foss.org.synergys.plist b/doc/org.synergy-foss.org.synergys.plist index 3232b4243..116615d1f 100644 --- a/doc/org.synergy-foss.org.synergys.plist +++ b/doc/org.synergy-foss.org.synergys.plist @@ -5,7 +5,7 @@ Label - org.synergy-project.org.synergys.plist + org.symless.com.synergys.plist OnDemand ProgramArguments diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index dc26cc76d..8e21192de 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -201,7 +201,7 @@ class InternalCommands: project = 'synergy' setup_version = 5 # increment to force setup/config - website_url = 'http://synergy-project.org/' + website_url = 'http://symless.com/' this_cmd = 'hm' cmake_cmd = 'cmake' diff --git a/res/Readme.txt b/res/Readme.txt index c5ce0b0e9..e4bfb1752 100644 --- a/res/Readme.txt +++ b/res/Readme.txt @@ -1,10 +1,10 @@ Thank you for chosing Synergy! -http://synergy-project.org/ +http://symless.com/ Synergy allows you to share your keyboard and mouse between computers over a network. For FAQ, setup, and usage instructions, please visit our online Readme: -http://synergy-project.org/pm/projects/synergy/wiki/Readme +http://symless.com/pm/projects/synergy/wiki/Readme Have fun! diff --git a/res/deb/changelog b/res/deb/changelog index 13a46350a..b78b58019 100644 --- a/res/deb/changelog +++ b/res/deb/changelog @@ -2,4 +2,4 @@ synergy (1.0) unstable; urgency=low * Initial release. - -- Nick Bolton Wed, 09 Apr 2014 15:16:48 +0100 + -- Nick Bolton Wed, 09 Apr 2014 15:16:48 +0100 diff --git a/res/deb/control.in b/res/deb/control.in index 6de60736c..63dc4440d 100644 --- a/res/deb/control.in +++ b/res/deb/control.in @@ -1,7 +1,7 @@ Package: synergy Section: net Priority: optional -Maintainer: Nick Bolton +Maintainer: Nick Bolton Version: ${in:version} Architecture: ${in:arch} Depends: libc6 (>= 2.11), libstdc++6 (>= 4.4.3), libx11-6 (>= 1.3.2), libxext6 (>= 1.1.1), libxi6 (>= 1.3), libxinerama1 (>= 1.1), libxtst6 (>= 1.1), libqtcore4 (>= 4.6.2), libqtgui4 (>= 4.6.2), libqt4-network (>= 4.6.2), libcurl3 (>= 7.19.7), libavahi-compat-libdnssd1 (>= 0.6.25), openssl (>= 1.0.1) diff --git a/res/synergy.nsh b/res/synergy.nsh index 7d8501210..f4859d0f2 100644 --- a/res/synergy.nsh +++ b/res/synergy.nsh @@ -20,7 +20,7 @@ !define platform "Windows" !define publisher "The Synergy Project" !define publisherOld "The Synergy+ Project" -!define helpUrl "http://synergy-project.org/support" +!define helpUrl "http://symless.com/support" !define vcRedistFile "vcredist_${arch}.exe" !define startMenuApp "synergy.exe" !define binDir "..\bin" diff --git a/res/synergy.spec.in b/res/synergy.spec.in index b63daa6f7..16b695e3e 100644 --- a/res/synergy.spec.in +++ b/res/synergy.spec.in @@ -4,10 +4,10 @@ Name: synergy Release: 1 License: GPLv2 Group: Applications/Productivity -URL: http://synergy-project.org/ -Source: http://synergy-project.org/download/ +URL: http://symless.com/ +Source: http://symless.com/download/ Vendor: The Synergy Project -Packager: Nick Bolton +Packager: Nick Bolton Version: ${in:version} %description @@ -43,5 +43,5 @@ cp $source/bin/plugins/* %{buildroot}%{_bindir}/../lib/synergy/plugins %attr(644,-,-) %{_bindir}/../lib/synergy/plugins/* %changelog -* Thu Mar 20 2014 Nick Bolton +* Thu Mar 20 2014 Nick Bolton - Initial version of the package diff --git a/src/gui/gui.ts b/src/gui/gui.ts index d7d27136d..d1e1133d0 100644 --- a/src/gui/gui.ts +++ b/src/gui/gui.ts @@ -17,7 +17,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -26,7 +26,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1227,7 +1227,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/AboutDialogBase.ui b/src/gui/res/AboutDialogBase.ui index bd2e993d7..cc8189ba5 100644 --- a/src/gui/res/AboutDialogBase.ui +++ b/src/gui/res/AboutDialogBase.ui @@ -57,7 +57,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> diff --git a/src/gui/res/SetupWizardBase.ui b/src/gui/res/SetupWizardBase.ui index da7ae7c52..6788a3d53 100644 --- a/src/gui/res/SetupWizardBase.ui +++ b/src/gui/res/SetupWizardBase.ui @@ -233,7 +233,7 @@ - <a href="https://synergy-project.org/account/reset/?source=gui">Forgot password</a> + <a href="https://symless.com/account/reset/?source=gui">Forgot password</a> true diff --git a/src/gui/res/lang/gui_af-ZA.ts b/src/gui/res/lang/gui_af-ZA.ts index f42f4e5fe..bcad2d59e 100644 --- a/src/gui/res/lang/gui_af-ZA.ts +++ b/src/gui/res/lang/gui_af-ZA.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_ar.ts b/src/gui/res/lang/gui_ar.ts index 3a3511b26..b323be946 100644 --- a/src/gui/res/lang/gui_ar.ts +++ b/src/gui/res/lang/gui_ar.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_bg-BG.ts b/src/gui/res/lang/gui_bg-BG.ts index 413ad5dde..1dab7b9a7 100644 --- a/src/gui/res/lang/gui_bg-BG.ts +++ b/src/gui/res/lang/gui_bg-BG.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1227,7 +1227,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_ca-AD.ts b/src/gui/res/lang/gui_ca-AD.ts index 2dfce1379..91c810503 100644 --- a/src/gui/res/lang/gui_ca-AD.ts +++ b/src/gui/res/lang/gui_ca-AD.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ Això permet a Synergy interactuar amb processos elevats i el diàleg del UAC, p - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_cs-CZ.ts b/src/gui/res/lang/gui_cs-CZ.ts index 547724372..42368004c 100644 --- a/src/gui/res/lang/gui_cs-CZ.ts +++ b/src/gui/res/lang/gui_cs-CZ.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ Sice to Synergy umožní pracovat s procesy, které mají také takový stupeň - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_cy.ts b/src/gui/res/lang/gui_cy.ts index 9092318f8..74494f6da 100644 --- a/src/gui/res/lang/gui_cy.ts +++ b/src/gui/res/lang/gui_cy.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1227,7 +1227,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_da.ts b/src/gui/res/lang/gui_da.ts index 3b57116c3..67b3e74ac 100644 --- a/src/gui/res/lang/gui_da.ts +++ b/src/gui/res/lang/gui_da.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1227,7 +1227,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_de.ts b/src/gui/res/lang/gui_de.ts index 9151d516f..651849d9a 100644 --- a/src/gui/res/lang/gui_de.ts +++ b/src/gui/res/lang/gui_de.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ Das erlaubt Synergy mit Prozessen die höhere Rechte haben und dem UAC-Dialog zu - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_es.ts b/src/gui/res/lang/gui_es.ts index f841d393a..1d470d6f6 100644 --- a/src/gui/res/lang/gui_es.ts +++ b/src/gui/res/lang/gui_es.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1229,7 +1229,7 @@ Esto permitirá que puedas interactuar con procesos elevados y el cuadro de diá - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_et-EE.ts b/src/gui/res/lang/gui_et-EE.ts index 95ce210c3..198105873 100644 --- a/src/gui/res/lang/gui_et-EE.ts +++ b/src/gui/res/lang/gui_et-EE.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ See võimaldab Synergy'l toimetada ülendatud protsessidega ja UAC dialoogiga, k - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_fi.ts b/src/gui/res/lang/gui_fi.ts index 215db456f..d887384e8 100644 --- a/src/gui/res/lang/gui_fi.ts +++ b/src/gui/res/lang/gui_fi.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ Tämä mahdollistaa Synergyn käyttämisen korotetuissa prosesseissa ja UAC-dial - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_fr.ts b/src/gui/res/lang/gui_fr.ts index 8e0b04046..238cb2c82 100644 --- a/src/gui/res/lang/gui_fr.ts +++ b/src/gui/res/lang/gui_fr.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ Ceci autorise Synergy à interagir avec les processus élevés et le dialogue UA - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_gl.ts b/src/gui/res/lang/gui_gl.ts index d5528b075..8613dee87 100644 --- a/src/gui/res/lang/gui_gl.ts +++ b/src/gui/res/lang/gui_gl.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_grk.ts b/src/gui/res/lang/gui_grk.ts index 9d8ebc11c..70575fa1c 100644 --- a/src/gui/res/lang/gui_grk.ts +++ b/src/gui/res/lang/gui_grk.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_he.ts b/src/gui/res/lang/gui_he.ts index 3370ae501..07bff415e 100644 --- a/src/gui/res/lang/gui_he.ts +++ b/src/gui/res/lang/gui_he.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_hi.ts b/src/gui/res/lang/gui_hi.ts index 1c3380bc7..7c7593aca 100644 --- a/src/gui/res/lang/gui_hi.ts +++ b/src/gui/res/lang/gui_hi.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_hr-HR.ts b/src/gui/res/lang/gui_hr-HR.ts index 303040fd9..2c71bbded 100644 --- a/src/gui/res/lang/gui_hr-HR.ts +++ b/src/gui/res/lang/gui_hr-HR.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ To omogućuje Synergyu interakciju s ovlaštenim procesima i UAC dijalogom, ali - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_hu-HU.ts b/src/gui/res/lang/gui_hu-HU.ts index fb4c4555d..d2299bafc 100644 --- a/src/gui/res/lang/gui_hu-HU.ts +++ b/src/gui/res/lang/gui_hu-HU.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1227,7 +1227,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_id.ts b/src/gui/res/lang/gui_id.ts index 611bfd41a..f25e48e16 100644 --- a/src/gui/res/lang/gui_id.ts +++ b/src/gui/res/lang/gui_id.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_is-IS.ts b/src/gui/res/lang/gui_is-IS.ts index 7204185d4..055cdaca3 100644 --- a/src/gui/res/lang/gui_is-IS.ts +++ b/src/gui/res/lang/gui_is-IS.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_it.ts b/src/gui/res/lang/gui_it.ts index 441dd96a3..b39098f67 100644 --- a/src/gui/res/lang/gui_it.ts +++ b/src/gui/res/lang/gui_it.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ Ciò consentirà a Synergy di interagire con molti processi e con il dialogo UAC - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_ja-JP.ts b/src/gui/res/lang/gui_ja-JP.ts index 600148c31..e2e16961c 100644 --- a/src/gui/res/lang/gui_ja-JP.ts +++ b/src/gui/res/lang/gui_ja-JP.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_ko.ts b/src/gui/res/lang/gui_ko.ts index 5e5682e8d..7cc6bcb4d 100644 --- a/src/gui/res/lang/gui_ko.ts +++ b/src/gui/res/lang/gui_ko.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_lt.ts b/src/gui/res/lang/gui_lt.ts index 89e344ffd..d60cdd5ac 100644 --- a/src/gui/res/lang/gui_lt.ts +++ b/src/gui/res/lang/gui_lt.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_lv.ts b/src/gui/res/lang/gui_lv.ts index 43c69ce97..6edf11d60 100644 --- a/src/gui/res/lang/gui_lv.ts +++ b/src/gui/res/lang/gui_lv.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_mr.ts b/src/gui/res/lang/gui_mr.ts index 5caf3fb19..095b7f912 100644 --- a/src/gui/res/lang/gui_mr.ts +++ b/src/gui/res/lang/gui_mr.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_nl-NL.ts b/src/gui/res/lang/gui_nl-NL.ts index 0e2e4c9cb..d0d1b22c9 100644 --- a/src/gui/res/lang/gui_nl-NL.ts +++ b/src/gui/res/lang/gui_nl-NL.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1227,7 +1227,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_no.ts b/src/gui/res/lang/gui_no.ts index 96589637d..65069333d 100644 --- a/src/gui/res/lang/gui_no.ts +++ b/src/gui/res/lang/gui_no.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ Dette tillater Synergy å kommunisere med forhøyede prosesser og UAC dialoger. - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_pes-IR.ts b/src/gui/res/lang/gui_pes-IR.ts index 89c0472e8..a7a6bbdd9 100644 --- a/src/gui/res/lang/gui_pes-IR.ts +++ b/src/gui/res/lang/gui_pes-IR.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_pl-PL.ts b/src/gui/res/lang/gui_pl-PL.ts index 57afd7710..b95016d8b 100644 --- a/src/gui/res/lang/gui_pl-PL.ts +++ b/src/gui/res/lang/gui_pl-PL.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ Pozwoli to Synergy współpracować z innymi aplikacjami o podniesionych uprawni - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_pt-BR.ts b/src/gui/res/lang/gui_pt-BR.ts index 4c3b90d99..20e450a1c 100644 --- a/src/gui/res/lang/gui_pt-BR.ts +++ b/src/gui/res/lang/gui_pt-BR.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ Isso permite ao Synergy interagir com processos privilegiados e com o UAC, mas p - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_pt-PT.ts b/src/gui/res/lang/gui_pt-PT.ts index e9cbb66f6..bb2e461aa 100644 --- a/src/gui/res/lang/gui_pt-PT.ts +++ b/src/gui/res/lang/gui_pt-PT.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1227,7 +1227,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_ro.ts b/src/gui/res/lang/gui_ro.ts index 1e1185012..edce79395 100644 --- a/src/gui/res/lang/gui_ro.ts +++ b/src/gui/res/lang/gui_ro.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1227,7 +1227,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_ru.ts b/src/gui/res/lang/gui_ru.ts index 300a86081..8015fa4db 100644 --- a/src/gui/res/lang/gui_ru.ts +++ b/src/gui/res/lang/gui_ru.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_si.ts b/src/gui/res/lang/gui_si.ts index 549459257..aa4668738 100644 --- a/src/gui/res/lang/gui_si.ts +++ b/src/gui/res/lang/gui_si.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_sk-SK.ts b/src/gui/res/lang/gui_sk-SK.ts index ed8b9f45a..d96cc3f52 100644 --- a/src/gui/res/lang/gui_sk-SK.ts +++ b/src/gui/res/lang/gui_sk-SK.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_sl-SI.ts b/src/gui/res/lang/gui_sl-SI.ts index 1234c69f3..87ef1e35a 100644 --- a/src/gui/res/lang/gui_sl-SI.ts +++ b/src/gui/res/lang/gui_sl-SI.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_sq-AL.ts b/src/gui/res/lang/gui_sq-AL.ts index 75c8f190a..64290cdfa 100644 --- a/src/gui/res/lang/gui_sq-AL.ts +++ b/src/gui/res/lang/gui_sq-AL.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ Kjo i jep mundesi Synergy te bashkeveproje me procese te larta dhe UAC, por ju m - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_sr.ts b/src/gui/res/lang/gui_sr.ts index eda24527d..95dd152fb 100644 --- a/src/gui/res/lang/gui_sr.ts +++ b/src/gui/res/lang/gui_sr.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_sv.ts b/src/gui/res/lang/gui_sv.ts index cae92d59d..13bff5f23 100644 --- a/src/gui/res/lang/gui_sv.ts +++ b/src/gui/res/lang/gui_sv.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ Detta låter Synergy interagera med förhöjda processer och UAC-dialogen, men k - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_th-TH.ts b/src/gui/res/lang/gui_th-TH.ts index 60de5dc43..8c51bdd5c 100644 --- a/src/gui/res/lang/gui_th-TH.ts +++ b/src/gui/res/lang/gui_th-TH.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_tr-TR.ts b/src/gui/res/lang/gui_tr-TR.ts index 1a8be7404..72d30933f 100644 --- a/src/gui/res/lang/gui_tr-TR.ts +++ b/src/gui/res/lang/gui_tr-TR.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1227,7 +1227,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_uk.ts b/src/gui/res/lang/gui_uk.ts index ee5fd9a80..cd0b85905 100644 --- a/src/gui/res/lang/gui_uk.ts +++ b/src/gui/res/lang/gui_uk.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_ur.ts b/src/gui/res/lang/gui_ur.ts index ddab60325..f7f1da4f5 100644 --- a/src/gui/res/lang/gui_ur.ts +++ b/src/gui/res/lang/gui_ur.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_vi.ts b/src/gui/res/lang/gui_vi.ts index 6fd2c831d..d98c947e5 100644 --- a/src/gui/res/lang/gui_vi.ts +++ b/src/gui/res/lang/gui_vi.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1225,7 +1225,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_zh-CN.ts b/src/gui/res/lang/gui_zh-CN.ts index 653ddd2da..7cc0da77e 100644 --- a/src/gui/res/lang/gui_zh-CN.ts +++ b/src/gui/res/lang/gui_zh-CN.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/res/lang/gui_zh-TW.ts b/src/gui/res/lang/gui_zh-TW.ts index 025e9b01a..049f4081f 100644 --- a/src/gui/res/lang/gui_zh-TW.ts +++ b/src/gui/res/lang/gui_zh-TW.ts @@ -15,7 +15,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> @@ -24,7 +24,7 @@ Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.<br />&l 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 (synergy-project.org). +Visit our website for help and info (symless.com). </p> @@ -1228,7 +1228,7 @@ This allows Synergy to interact with elevated processes and the UAC dialog, but - <a href="https://synergy-project.org/account/reset/">Forgot password</a> + <a href="https://symless.com/account/reset/">Forgot password</a> diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index b28a46edc..8f7cbc5e0 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -#define DOWNLOAD_URL "http://synergy-project.org/?source=gui" +#define DOWNLOAD_URL "http://symless.com/?source=gui" #include @@ -59,7 +59,7 @@ #if defined(Q_OS_WIN) static const char synergyConfigName[] = "synergy.sgc"; static const QString synergyConfigFilter(QObject::tr("Synergy Configurations (*.sgc);;All files (*.*)")); -static QString bonjourBaseUrl = "http://synergy-project.org/bonjour/"; +static QString bonjourBaseUrl = "http://symless.com/bonjour/"; static const char bonjourFilename32[] = "Bonjour.msi"; static const char bonjourFilename64[] = "Bonjour64.msi"; static const char bonjourTargetFilename[] = "Bonjour.msi"; diff --git a/src/gui/src/Plugin.cpp b/src/gui/src/Plugin.cpp index ebdc70cea..8118d9507 100644 --- a/src/gui/src/Plugin.cpp +++ b/src/gui/src/Plugin.cpp @@ -19,7 +19,7 @@ #include "CoreInterface.h" -static const char kBaseUrl[] = "http://synergy-project.org/files"; +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"; diff --git a/src/gui/src/SubscriptionManager.cpp b/src/gui/src/SubscriptionManager.cpp index f3f55611e..52096a11a 100644 --- a/src/gui/src/SubscriptionManager.cpp +++ b/src/gui/src/SubscriptionManager.cpp @@ -28,7 +28,7 @@ #include #include -static const char purchaseURL[] = "https://synergy-project.org/account/"; +static const char purchaseURL[] = "https://symless.com/account/"; SubscriptionManager::SubscriptionManager(QWidget* parent, AppConfig& appConfig, int& edition) : m_pParent(parent), diff --git a/src/gui/src/VersionChecker.cpp b/src/gui/src/VersionChecker.cpp index dcb5720fc..a7af296c5 100644 --- a/src/gui/src/VersionChecker.cpp +++ b/src/gui/src/VersionChecker.cpp @@ -25,7 +25,7 @@ #include #define VERSION_REGEX "(\\d+\\.\\d+\\.\\d+)" -#define VERSION_URL "http://synergy-project.org/version/" +#define VERSION_URL "http://symless.com/version/" VersionChecker::VersionChecker() { diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index 70816276b..1aaddadc7 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -52,7 +52,7 @@ bool checkMacAssistiveDevices(); int main(int argc, char* argv[]) { QCoreApplication::setOrganizationName("Synergy"); - QCoreApplication::setOrganizationDomain("http://synergy-project.org/"); + QCoreApplication::setOrganizationDomain("http://symless.com/"); QCoreApplication::setApplicationName("Synergy"); QSynergyApplication app(argc, argv); diff --git a/src/lib/common/Version.cpp b/src/lib/common/Version.cpp index 5d478c645..18724fc61 100644 --- a/src/lib/common/Version.cpp +++ b/src/lib/common/Version.cpp @@ -22,7 +22,7 @@ const char* kApplication = "Synergy"; const char* kCopyright = "Copyright (C) 2012-2014 Synergy Si Ltd.\n" "Copyright (C) 2008-2014 Nick Bolton\n" "Copyright (C) 2002-2014 Chris Schoeneman"; -const char* kContact = "Email: nick@synergy-project.org"; -const char* kWebsite = "http://synergy-project.org/"; +const char* kContact = "Email: nick@symless.com"; +const char* kWebsite = "http://symless.com/"; const char* kVersion = VERSION; const char* kAppVersion = "Synergy " VERSION; diff --git a/src/lib/platform/MSWindowsClipboard.cpp b/src/lib/platform/MSWindowsClipboard.cpp index b0dd3a3f7..359e43e52 100644 --- a/src/lib/platform/MSWindowsClipboard.cpp +++ b/src/lib/platform/MSWindowsClipboard.cpp @@ -121,8 +121,8 @@ MSWindowsClipboard::open(Time time) const if (!OpenClipboard(m_window)) { // unable to cause this in integ tests; but this can happen! - // * http://synergy-project.org/pm/issues/86 - // * http://synergy-project.org/pm/issues/1256 + // * http://symless.com/pm/issues/86 + // * http://symless.com/pm/issues/1256 // logging improved to see if we can catch more info next time. LOG((CLOG_WARN "failed to open clipboard: %d", GetLastError())); return false; diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index 11cbc7a92..a19fad8ca 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -298,7 +298,7 @@ MSWindowsWatchdog::startProcess() // patch by Jack Zhou and Henry Tung // set UIAccess to fix Windows 8 GUI interaction - // http://synergy-project.org/spit/issues/details/3338/#c70 + // http://symless.com/spit/issues/details/3338/#c70 DWORD uiAccess = 1; SetTokenInformation(userToken, TokenUIAccess, &uiAccess, sizeof(DWORD)); diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index a216dc1dc..4714d2bf0 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -835,7 +835,7 @@ OSXScreen::enter() // patch by Yutaka Tsutano // wakes the client screen - // http://synergy-project.org/spit/issues/details/3287#c12 + // http://symless.com/spit/issues/details/3287#c12 io_registry_entry_t entry = IORegistryEntryFromPath( kIOMasterPortDefault, "IOService:/IOResources/IODisplayWrangler"); diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index c04faa072..49c8930f0 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -30,7 +30,7 @@ #include "platform/MSWindowsSession.h" #endif -#define JSON_URL "https://synergy-project.org/account/json/" +#define JSON_URL "https://symless.com/account/json/" enum { kErrorOk, diff --git a/src/test/integtests/arch/ArchInternetTests.cpp b/src/test/integtests/arch/ArchInternetTests.cpp index 245e570f0..4402578a0 100644 --- a/src/test/integtests/arch/ArchInternetTests.cpp +++ b/src/test/integtests/arch/ArchInternetTests.cpp @@ -19,7 +19,7 @@ #include "test/global/gtest.h" -#define TEST_URL "https://synergy-project.org/tests/?testString" +#define TEST_URL "https://symless.com/tests/?testString" //#define TEST_URL "http://localhost/synergy/tests/?testString" TEST(ArchInternetTests, get) diff --git a/src/test/integtests/platform/MSWindowsClipboardTests.cpp b/src/test/integtests/platform/MSWindowsClipboardTests.cpp index fcabd20f6..8e1b76943 100644 --- a/src/test/integtests/platform/MSWindowsClipboardTests.cpp +++ b/src/test/integtests/platform/MSWindowsClipboardTests.cpp @@ -146,7 +146,7 @@ TEST_F(MSWindowsClipboardTests, close_isOpen_noErrors) } // looks like this test may fail intermittently: -// * http://buildbot.synergy-project.org:8000/builders/trunk-win32/builds/246/steps/shell_3/logs/stdio +// * http://buildbot.symless.com:8000/builders/trunk-win32/builds/246/steps/shell_3/logs/stdio /*TEST_F(MSWindowsClipboardTests, getTime_openWithNoEmpty_returnsOne) { MSWindowsClipboard clipboard(NULL); @@ -160,7 +160,7 @@ TEST_F(MSWindowsClipboardTests, close_isOpen_noErrors) }*/ // this also fails intermittently: -// http://buildbot.synergy-project.org:8000/builders/trunk-win32/builds/266/steps/shell_3/logs/stdio +// http://buildbot.symless.com:8000/builders/trunk-win32/builds/266/steps/shell_3/logs/stdio /*TEST_F(MSWindowsClipboardTests, getTime_openAndEmpty_returnsOne) { MSWindowsClipboard clipboard(NULL); From 77a7b67431776c7dfb7db5d6327a68fadb7e06a6 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 6 Sep 2016 16:17:28 +0100 Subject: [PATCH 283/572] Fix up old wiki links --- COMPILE | 2 +- INSTALL | 2 +- README | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/COMPILE b/COMPILE index e05a82363..f9931fd72 100644 --- a/COMPILE +++ b/COMPILE @@ -1 +1 @@ -Compiling: http://symless.com/wiki/Compiling +Compiling: https://github.com/symless/synergy/wiki/Compiling diff --git a/INSTALL b/INSTALL index 77af84443..d2840a0a8 100644 --- a/INSTALL +++ b/INSTALL @@ -1,2 +1,2 @@ Help: http://symless.com/help/ -Wiki: http://symless.com/wiki/ +Wiki: https://github.com/symless/synergy/wiki/ diff --git a/README b/README index b2d40b5d8..09875287b 100644 --- a/README +++ b/README @@ -11,6 +11,6 @@ Just use "hm conf" and "hm build" to compile (./hm.sh on Linux and Mac). For detailed compile instructions: -http://symless.com/wiki/Compiling +https://github.com/symless/synergy/wiki/Compiling Happy hacking! From 9a6f82d89dfdfb01c1c9beb45f939ec75adab2d2 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 6 Sep 2016 16:21:21 +0100 Subject: [PATCH 284/572] Fix typo in hm.py toolchain --- 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 8e21192de..3b3ca7aaa 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -1515,7 +1515,7 @@ def dist_usage(self): 'Replace [package-type] with one of:\n' ' src .tar.gz source (Posix only)\n' ' rpm .rpm package (Red Hat)\n' - ' deb .deb paclage (Debian)\n' + ' deb .deb package (Debian)\n' ' win .exe installer (Windows)\n' ' mac .dmg package (Mac OS X)\n' '\n' From a8472d2eb2106a0d1503d25369e6bdc6e3b88c7d Mon Sep 17 00:00:00 2001 From: Epakai Date: Wed, 27 Jul 2016 00:37:08 -0500 Subject: [PATCH 285/572] Add exemption for linking GPL code with OpenSSL Per OpenSSL FAQ https://www.openssl.org/docs/faq.html#LEGAL2 Please include a blanket exception for linking with OpenSSL code. --- LICENSE | 3 +++ 1 file changed, 3 insertions(+) diff --git a/LICENSE b/LICENSE index 60ce79389..eb247025b 100644 --- a/LICENSE +++ b/LICENSE @@ -3,6 +3,9 @@ Copyright (C) 2012-2014 Synergy Si Ltd. Copyright (C) 2008-2014 Nick Bolton Copyright (C) 2002-2014 Chris Schoeneman +This program is released under the GPL with the additional exemption +that compiling, linking, and/or using OpenSSL is allowed. + GNU GENERAL PUBLIC LICENSE Version 2, June 1991 From c3c0913633041584fa41180640d2e4c83fa92820 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 7 Sep 2016 15:24:00 +0100 Subject: [PATCH 286/572] Update company copyright notice --- CMakeLists.txt | 2 +- LICENSE | 2 +- ext/toolchain/__init__.py | 2 +- ext/toolchain/commands1.py | 2 +- ext/toolchain/ftputil.py | 2 +- ext/toolchain/generators.py | 2 +- hm.py | 2 +- res/deb/copyright | 2 +- src/CMakeLists.txt | 2 +- src/cmd/CMakeLists.txt | 2 +- src/cmd/synergyc/CMakeLists.txt | 2 +- src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp | 2 +- src/cmd/synergyc/MSWindowsClientTaskBarReceiver.h | 2 +- src/cmd/synergyc/OSXClientTaskBarReceiver.cpp | 2 +- src/cmd/synergyc/OSXClientTaskBarReceiver.h | 2 +- src/cmd/synergyc/XWindowsClientTaskBarReceiver.cpp | 2 +- src/cmd/synergyc/XWindowsClientTaskBarReceiver.h | 2 +- src/cmd/synergyc/synergyc.cpp | 2 +- src/cmd/synergyd/CMakeLists.txt | 2 +- src/cmd/synergyd/synergyd.cpp | 2 +- src/cmd/synergyp/CMakeLists.txt | 2 +- src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp | 2 +- src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.h | 2 +- src/cmd/synergyp/OSXPortableTaskBarReceiver.cpp | 2 +- src/cmd/synergyp/OSXPortableTaskBarReceiver.h | 2 +- src/cmd/synergyp/XWindowsPortableTaskBarReceiver.cpp | 2 +- src/cmd/synergyp/XWindowsPortableTaskBarReceiver.h | 2 +- src/cmd/synergyp/synergyp.cpp | 2 +- src/cmd/synergys/CMakeLists.txt | 2 +- src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp | 2 +- src/cmd/synergys/MSWindowsServerTaskBarReceiver.h | 2 +- src/cmd/synergys/OSXServerTaskBarReceiver.cpp | 2 +- src/cmd/synergys/OSXServerTaskBarReceiver.h | 2 +- src/cmd/synergys/XWindowsServerTaskBarReceiver.cpp | 2 +- src/cmd/synergys/XWindowsServerTaskBarReceiver.h | 2 +- src/cmd/synergys/synergys.cpp | 2 +- src/cmd/syntool/CMakeLists.txt | 2 +- src/cmd/syntool/syntool.cpp | 2 +- src/cmd/usynergy/CMakeLists.txt | 2 +- src/gui/gui.ts | 4 ++-- src/gui/res/AboutDialogBase.ui | 2 +- src/gui/res/lang/gui_af-ZA.ts | 4 ++-- src/gui/res/lang/gui_ar.ts | 4 ++-- src/gui/res/lang/gui_bg-BG.ts | 4 ++-- src/gui/res/lang/gui_ca-AD.ts | 4 ++-- src/gui/res/lang/gui_cs-CZ.ts | 4 ++-- src/gui/res/lang/gui_cy.ts | 4 ++-- src/gui/res/lang/gui_da.ts | 4 ++-- src/gui/res/lang/gui_de.ts | 4 ++-- src/gui/res/lang/gui_es.ts | 4 ++-- src/gui/res/lang/gui_et-EE.ts | 4 ++-- src/gui/res/lang/gui_fi.ts | 4 ++-- src/gui/res/lang/gui_fr.ts | 4 ++-- src/gui/res/lang/gui_gl.ts | 4 ++-- src/gui/res/lang/gui_grk.ts | 4 ++-- src/gui/res/lang/gui_he.ts | 4 ++-- src/gui/res/lang/gui_hi.ts | 4 ++-- src/gui/res/lang/gui_hr-HR.ts | 4 ++-- src/gui/res/lang/gui_hu-HU.ts | 4 ++-- src/gui/res/lang/gui_id.ts | 4 ++-- src/gui/res/lang/gui_is-IS.ts | 4 ++-- src/gui/res/lang/gui_it.ts | 4 ++-- src/gui/res/lang/gui_ja-JP.ts | 4 ++-- src/gui/res/lang/gui_ko.ts | 4 ++-- src/gui/res/lang/gui_lt.ts | 4 ++-- src/gui/res/lang/gui_lv.ts | 4 ++-- src/gui/res/lang/gui_mr.ts | 4 ++-- src/gui/res/lang/gui_nl-NL.ts | 4 ++-- src/gui/res/lang/gui_no.ts | 4 ++-- src/gui/res/lang/gui_pes-IR.ts | 4 ++-- src/gui/res/lang/gui_pl-PL.ts | 4 ++-- src/gui/res/lang/gui_pt-BR.ts | 4 ++-- src/gui/res/lang/gui_pt-PT.ts | 4 ++-- src/gui/res/lang/gui_ro.ts | 4 ++-- src/gui/res/lang/gui_ru.ts | 4 ++-- src/gui/res/lang/gui_si.ts | 4 ++-- src/gui/res/lang/gui_sk-SK.ts | 4 ++-- src/gui/res/lang/gui_sl-SI.ts | 4 ++-- src/gui/res/lang/gui_sq-AL.ts | 4 ++-- src/gui/res/lang/gui_sr.ts | 4 ++-- src/gui/res/lang/gui_sv.ts | 4 ++-- src/gui/res/lang/gui_th-TH.ts | 4 ++-- src/gui/res/lang/gui_tr-TR.ts | 4 ++-- src/gui/res/lang/gui_uk.ts | 4 ++-- src/gui/res/lang/gui_ur.ts | 4 ++-- src/gui/res/lang/gui_vi.ts | 4 ++-- src/gui/res/lang/gui_zh-CN.ts | 4 ++-- src/gui/res/lang/gui_zh-TW.ts | 4 ++-- src/gui/src/AboutDialog.cpp | 2 +- src/gui/src/AboutDialog.h | 2 +- src/gui/src/Action.cpp | 2 +- src/gui/src/Action.h | 2 +- src/gui/src/ActionDialog.cpp | 2 +- src/gui/src/ActionDialog.h | 2 +- src/gui/src/AddClientDialog.cpp | 2 +- src/gui/src/AddClientDialog.h | 2 +- src/gui/src/AppConfig.cpp | 2 +- src/gui/src/AppConfig.h | 2 +- src/gui/src/BaseConfig.cpp | 2 +- src/gui/src/BaseConfig.h | 2 +- src/gui/src/CommandProcess.cpp | 2 +- src/gui/src/CommandProcess.h | 2 +- src/gui/src/CoreInterface.cpp | 2 +- src/gui/src/CoreInterface.h | 2 +- src/gui/src/DataDownloader.cpp | 2 +- src/gui/src/DataDownloader.h | 2 +- src/gui/src/EditionType.h | 2 +- src/gui/src/Fingerprint.cpp | 2 +- src/gui/src/Fingerprint.h | 2 +- src/gui/src/Hotkey.cpp | 2 +- src/gui/src/Hotkey.h | 2 +- src/gui/src/HotkeyDialog.cpp | 2 +- src/gui/src/HotkeyDialog.h | 2 +- src/gui/src/Ipc.cpp | 2 +- src/gui/src/Ipc.h | 2 +- src/gui/src/IpcClient.cpp | 2 +- src/gui/src/IpcClient.h | 2 +- src/gui/src/IpcReader.cpp | 2 +- src/gui/src/IpcReader.h | 2 +- src/gui/src/KeySequence.cpp | 2 +- src/gui/src/KeySequence.h | 2 +- src/gui/src/KeySequenceWidget.cpp | 2 +- src/gui/src/KeySequenceWidget.h | 2 +- src/gui/src/MainWindow.cpp | 2 +- src/gui/src/MainWindow.h | 2 +- src/gui/src/NewScreenWidget.cpp | 2 +- src/gui/src/NewScreenWidget.h | 2 +- src/gui/src/Plugin.cpp | 2 +- src/gui/src/Plugin.h | 2 +- src/gui/src/PluginManager.cpp | 2 +- src/gui/src/PluginManager.h | 2 +- src/gui/src/PluginWizardPage.cpp | 2 +- src/gui/src/PluginWizardPage.h | 2 +- src/gui/src/ProcessorArch.h | 2 +- src/gui/src/QSynergyApplication.cpp | 2 +- src/gui/src/QSynergyApplication.h | 2 +- src/gui/src/QUtility.cpp | 2 +- src/gui/src/QUtility.h | 2 +- src/gui/src/Screen.cpp | 2 +- src/gui/src/Screen.h | 2 +- src/gui/src/ScreenSettingsDialog.cpp | 2 +- src/gui/src/ScreenSettingsDialog.h | 2 +- src/gui/src/ScreenSetupModel.cpp | 2 +- src/gui/src/ScreenSetupModel.h | 2 +- src/gui/src/ScreenSetupView.cpp | 2 +- src/gui/src/ScreenSetupView.h | 2 +- src/gui/src/ServerConfig.cpp | 2 +- src/gui/src/ServerConfig.h | 2 +- src/gui/src/ServerConfigDialog.cpp | 2 +- src/gui/src/ServerConfigDialog.h | 2 +- src/gui/src/SettingsDialog.cpp | 2 +- src/gui/src/SettingsDialog.h | 2 +- src/gui/src/SetupWizard.cpp | 2 +- src/gui/src/SetupWizard.h | 2 +- src/gui/src/SslCertificate.cpp | 2 +- src/gui/src/SslCertificate.h | 2 +- src/gui/src/SynergyLocale.cpp | 2 +- src/gui/src/SynergyLocale.h | 2 +- src/gui/src/TcpSocketReader.cpp | 2 +- src/gui/src/TrashScreenWidget.cpp | 2 +- src/gui/src/TrashScreenWidget.h | 2 +- src/gui/src/VersionChecker.cpp | 2 +- src/gui/src/VersionChecker.h | 2 +- src/gui/src/WebClient.cpp | 2 +- src/gui/src/WebClient.h | 2 +- src/gui/src/ZeroconfBrowser.cpp | 2 +- src/gui/src/ZeroconfBrowser.h | 2 +- src/gui/src/ZeroconfRecord.h | 2 +- src/gui/src/ZeroconfRegister.cpp | 2 +- src/gui/src/ZeroconfRegister.h | 2 +- src/gui/src/ZeroconfServer.cpp | 2 +- src/gui/src/ZeroconfServer.h | 2 +- src/gui/src/ZeroconfService.cpp | 2 +- src/gui/src/ZeroconfService.h | 2 +- src/gui/src/ZeroconfThread.cpp | 2 +- src/gui/src/ZeroconfThread.h | 2 +- src/gui/src/main.cpp | 2 +- src/lib/CMakeLists.txt | 2 +- src/lib/arch/Arch.cpp | 2 +- src/lib/arch/Arch.h | 2 +- src/lib/arch/ArchConsoleStd.cpp | 2 +- src/lib/arch/ArchConsoleStd.h | 2 +- src/lib/arch/ArchDaemonNone.cpp | 2 +- src/lib/arch/ArchDaemonNone.h | 2 +- src/lib/arch/CMakeLists.txt | 2 +- src/lib/arch/IArchConsole.h | 2 +- src/lib/arch/IArchDaemon.h | 2 +- src/lib/arch/IArchFile.h | 2 +- src/lib/arch/IArchLog.h | 2 +- src/lib/arch/IArchMultithread.h | 2 +- src/lib/arch/IArchNetwork.h | 2 +- src/lib/arch/IArchPlugin.h | 2 +- src/lib/arch/IArchSleep.h | 2 +- src/lib/arch/IArchString.cpp | 2 +- src/lib/arch/IArchString.h | 2 +- src/lib/arch/IArchSystem.h | 2 +- src/lib/arch/IArchTaskBar.h | 2 +- src/lib/arch/IArchTaskBarReceiver.h | 2 +- src/lib/arch/IArchTime.h | 2 +- src/lib/arch/XArch.h | 2 +- src/lib/arch/multibyte.h | 2 +- src/lib/arch/unix/ArchConsoleUnix.cpp | 2 +- src/lib/arch/unix/ArchConsoleUnix.h | 2 +- src/lib/arch/unix/ArchDaemonUnix.cpp | 2 +- src/lib/arch/unix/ArchDaemonUnix.h | 2 +- src/lib/arch/unix/ArchFileUnix.cpp | 2 +- src/lib/arch/unix/ArchFileUnix.h | 2 +- src/lib/arch/unix/ArchInternetUnix.cpp | 2 +- src/lib/arch/unix/ArchInternetUnix.h | 2 +- src/lib/arch/unix/ArchLogUnix.cpp | 2 +- src/lib/arch/unix/ArchLogUnix.h | 2 +- src/lib/arch/unix/ArchMultithreadPosix.cpp | 2 +- src/lib/arch/unix/ArchMultithreadPosix.h | 2 +- src/lib/arch/unix/ArchNetworkBSD.cpp | 2 +- src/lib/arch/unix/ArchNetworkBSD.h | 2 +- src/lib/arch/unix/ArchPluginUnix.cpp | 2 +- src/lib/arch/unix/ArchPluginUnix.h | 2 +- src/lib/arch/unix/ArchSleepUnix.cpp | 2 +- src/lib/arch/unix/ArchSleepUnix.h | 2 +- src/lib/arch/unix/ArchStringUnix.cpp | 2 +- src/lib/arch/unix/ArchStringUnix.h | 2 +- src/lib/arch/unix/ArchSystemUnix.cpp | 2 +- src/lib/arch/unix/ArchSystemUnix.h | 2 +- src/lib/arch/unix/ArchTaskBarXWindows.cpp | 2 +- src/lib/arch/unix/ArchTaskBarXWindows.h | 2 +- src/lib/arch/unix/ArchTimeUnix.cpp | 2 +- src/lib/arch/unix/ArchTimeUnix.h | 2 +- src/lib/arch/unix/XArchUnix.cpp | 2 +- src/lib/arch/unix/XArchUnix.h | 2 +- src/lib/arch/vsnprintf.h | 2 +- src/lib/arch/win32/ArchConsoleWindows.cpp | 2 +- src/lib/arch/win32/ArchConsoleWindows.h | 2 +- src/lib/arch/win32/ArchDaemonWindows.cpp | 2 +- src/lib/arch/win32/ArchDaemonWindows.h | 2 +- src/lib/arch/win32/ArchFileWindows.cpp | 2 +- src/lib/arch/win32/ArchFileWindows.h | 2 +- src/lib/arch/win32/ArchInternetWindows.cpp | 2 +- src/lib/arch/win32/ArchInternetWindows.h | 2 +- src/lib/arch/win32/ArchLogWindows.cpp | 2 +- src/lib/arch/win32/ArchLogWindows.h | 2 +- src/lib/arch/win32/ArchMiscWindows.cpp | 2 +- src/lib/arch/win32/ArchMiscWindows.h | 2 +- src/lib/arch/win32/ArchMultithreadWindows.cpp | 2 +- src/lib/arch/win32/ArchMultithreadWindows.h | 2 +- src/lib/arch/win32/ArchNetworkWinsock.cpp | 2 +- src/lib/arch/win32/ArchNetworkWinsock.h | 2 +- src/lib/arch/win32/ArchPluginWindows.cpp | 2 +- src/lib/arch/win32/ArchPluginWindows.h | 2 +- src/lib/arch/win32/ArchSleepWindows.cpp | 2 +- src/lib/arch/win32/ArchSleepWindows.h | 2 +- src/lib/arch/win32/ArchStringWindows.cpp | 2 +- src/lib/arch/win32/ArchStringWindows.h | 2 +- src/lib/arch/win32/ArchSystemWindows.cpp | 2 +- src/lib/arch/win32/ArchSystemWindows.h | 2 +- src/lib/arch/win32/ArchTaskBarWindows.cpp | 2 +- src/lib/arch/win32/ArchTaskBarWindows.h | 2 +- src/lib/arch/win32/ArchTimeWindows.cpp | 2 +- src/lib/arch/win32/ArchTimeWindows.h | 2 +- src/lib/arch/win32/XArchWindows.cpp | 2 +- src/lib/arch/win32/XArchWindows.h | 2 +- src/lib/base/CMakeLists.txt | 2 +- src/lib/base/ELevel.h | 2 +- src/lib/base/Event.cpp | 2 +- src/lib/base/Event.h | 2 +- src/lib/base/EventQueue.cpp | 2 +- src/lib/base/EventQueue.h | 2 +- src/lib/base/EventTypes.cpp | 2 +- src/lib/base/EventTypes.h | 2 +- src/lib/base/FunctionEventJob.cpp | 2 +- src/lib/base/FunctionEventJob.h | 2 +- src/lib/base/FunctionJob.cpp | 2 +- src/lib/base/FunctionJob.h | 2 +- src/lib/base/IEventJob.h | 2 +- src/lib/base/IEventQueue.h | 2 +- src/lib/base/IEventQueueBuffer.h | 2 +- src/lib/base/IJob.h | 2 +- src/lib/base/ILogOutputter.h | 2 +- src/lib/base/Log.cpp | 2 +- src/lib/base/Log.h | 2 +- src/lib/base/PriorityQueue.h | 2 +- src/lib/base/SimpleEventQueueBuffer.cpp | 2 +- src/lib/base/SimpleEventQueueBuffer.h | 2 +- src/lib/base/Stopwatch.cpp | 2 +- src/lib/base/Stopwatch.h | 2 +- src/lib/base/String.cpp | 2 +- src/lib/base/String.h | 2 +- src/lib/base/TMethodEventJob.h | 2 +- src/lib/base/TMethodJob.h | 2 +- src/lib/base/Unicode.cpp | 2 +- src/lib/base/Unicode.h | 2 +- src/lib/base/XBase.cpp | 2 +- src/lib/base/XBase.h | 2 +- src/lib/base/log_outputters.cpp | 2 +- src/lib/base/log_outputters.h | 2 +- src/lib/client/CMakeLists.txt | 2 +- src/lib/client/Client.cpp | 2 +- src/lib/client/Client.h | 2 +- src/lib/client/ServerProxy.cpp | 2 +- src/lib/client/ServerProxy.h | 2 +- src/lib/common/CMakeLists.txt | 2 +- src/lib/common/IInterface.h | 2 +- src/lib/common/MacOSXPrecomp.h | 2 +- src/lib/common/PluginVersion.cpp | 2 +- src/lib/common/PluginVersion.h | 2 +- src/lib/common/Version.cpp | 4 ++-- src/lib/common/Version.h | 2 +- src/lib/common/basic_types.h | 2 +- src/lib/common/common.h | 2 +- src/lib/common/stdbitset.h | 2 +- src/lib/common/stddeque.h | 2 +- src/lib/common/stdexcept.h | 2 +- src/lib/common/stdfstream.h | 2 +- src/lib/common/stdistream.h | 2 +- src/lib/common/stdlist.h | 2 +- src/lib/common/stdmap.h | 2 +- src/lib/common/stdostream.h | 2 +- src/lib/common/stdpost.h | 2 +- src/lib/common/stdpre.h | 2 +- src/lib/common/stdset.h | 2 +- src/lib/common/stdsstream.h | 4 ++-- src/lib/common/stdstring.h | 2 +- src/lib/common/stdvector.h | 2 +- src/lib/io/CMakeLists.txt | 2 +- src/lib/io/IStream.h | 2 +- src/lib/io/StreamBuffer.cpp | 2 +- src/lib/io/StreamBuffer.h | 2 +- src/lib/io/StreamFilter.cpp | 2 +- src/lib/io/StreamFilter.h | 2 +- src/lib/io/XIO.cpp | 2 +- src/lib/io/XIO.h | 2 +- src/lib/ipc/CMakeLists.txt | 2 +- src/lib/ipc/Ipc.cpp | 2 +- src/lib/ipc/Ipc.h | 2 +- src/lib/ipc/IpcClient.cpp | 2 +- src/lib/ipc/IpcClient.h | 2 +- src/lib/ipc/IpcClientProxy.cpp | 2 +- src/lib/ipc/IpcClientProxy.h | 2 +- src/lib/ipc/IpcLogOutputter.cpp | 2 +- src/lib/ipc/IpcLogOutputter.h | 2 +- src/lib/ipc/IpcMessage.cpp | 2 +- src/lib/ipc/IpcMessage.h | 2 +- src/lib/ipc/IpcServer.cpp | 2 +- src/lib/ipc/IpcServer.h | 2 +- src/lib/ipc/IpcServerProxy.cpp | 2 +- src/lib/ipc/IpcServerProxy.h | 2 +- src/lib/mt/CMakeLists.txt | 2 +- src/lib/mt/CondVar.cpp | 2 +- src/lib/mt/CondVar.h | 2 +- src/lib/mt/Lock.cpp | 2 +- src/lib/mt/Lock.h | 2 +- src/lib/mt/Mutex.cpp | 2 +- src/lib/mt/Mutex.h | 2 +- src/lib/mt/Thread.cpp | 2 +- src/lib/mt/Thread.h | 2 +- src/lib/mt/XMT.cpp | 2 +- src/lib/mt/XMT.h | 2 +- src/lib/mt/XThread.h | 2 +- src/lib/net/CMakeLists.txt | 2 +- src/lib/net/IDataSocket.cpp | 2 +- src/lib/net/IDataSocket.h | 2 +- src/lib/net/IListenSocket.h | 2 +- src/lib/net/ISocket.h | 2 +- src/lib/net/ISocketFactory.h | 2 +- src/lib/net/ISocketMultiplexerJob.h | 2 +- src/lib/net/NetworkAddress.cpp | 2 +- src/lib/net/NetworkAddress.h | 2 +- src/lib/net/SocketMultiplexer.cpp | 2 +- src/lib/net/SocketMultiplexer.h | 2 +- src/lib/net/TCPListenSocket.cpp | 2 +- src/lib/net/TCPListenSocket.h | 2 +- src/lib/net/TCPSocket.cpp | 2 +- src/lib/net/TCPSocket.h | 2 +- src/lib/net/TCPSocketFactory.cpp | 2 +- src/lib/net/TCPSocketFactory.h | 2 +- src/lib/net/TSocketMultiplexerMethodJob.h | 2 +- src/lib/net/XSocket.cpp | 2 +- src/lib/net/XSocket.h | 2 +- src/lib/platform/CMakeLists.txt | 2 +- src/lib/platform/IMSWindowsClipboardFacade.h | 2 +- src/lib/platform/MSWindowsClipboard.cpp | 2 +- src/lib/platform/MSWindowsClipboard.h | 2 +- src/lib/platform/MSWindowsClipboardAnyTextConverter.cpp | 2 +- src/lib/platform/MSWindowsClipboardAnyTextConverter.h | 2 +- src/lib/platform/MSWindowsClipboardBitmapConverter.cpp | 2 +- src/lib/platform/MSWindowsClipboardBitmapConverter.h | 2 +- src/lib/platform/MSWindowsClipboardFacade.cpp | 2 +- src/lib/platform/MSWindowsClipboardFacade.h | 2 +- src/lib/platform/MSWindowsClipboardHTMLConverter.cpp | 2 +- src/lib/platform/MSWindowsClipboardHTMLConverter.h | 2 +- src/lib/platform/MSWindowsClipboardTextConverter.cpp | 2 +- src/lib/platform/MSWindowsClipboardTextConverter.h | 2 +- src/lib/platform/MSWindowsClipboardUTF16Converter.cpp | 2 +- src/lib/platform/MSWindowsClipboardUTF16Converter.h | 2 +- src/lib/platform/MSWindowsDebugOutputter.cpp | 2 +- src/lib/platform/MSWindowsDebugOutputter.h | 2 +- src/lib/platform/MSWindowsDesks.cpp | 2 +- src/lib/platform/MSWindowsDesks.h | 2 +- src/lib/platform/MSWindowsDropTarget.cpp | 2 +- src/lib/platform/MSWindowsDropTarget.h | 2 +- src/lib/platform/MSWindowsEventQueueBuffer.cpp | 2 +- src/lib/platform/MSWindowsEventQueueBuffer.h | 2 +- src/lib/platform/MSWindowsHook.cpp | 2 +- src/lib/platform/MSWindowsHook.h | 2 +- src/lib/platform/MSWindowsKeyState.cpp | 2 +- src/lib/platform/MSWindowsKeyState.h | 2 +- src/lib/platform/MSWindowsScreen.cpp | 2 +- src/lib/platform/MSWindowsScreen.h | 2 +- src/lib/platform/MSWindowsScreenSaver.cpp | 2 +- src/lib/platform/MSWindowsScreenSaver.h | 2 +- src/lib/platform/MSWindowsSession.cpp | 2 +- src/lib/platform/MSWindowsSession.h | 2 +- src/lib/platform/MSWindowsUtil.cpp | 2 +- src/lib/platform/MSWindowsUtil.h | 2 +- src/lib/platform/MSWindowsWatchdog.cpp | 2 +- src/lib/platform/MSWindowsWatchdog.h | 2 +- src/lib/platform/OSXClipboard.cpp | 2 +- src/lib/platform/OSXClipboard.h | 2 +- src/lib/platform/OSXClipboardAnyBitmapConverter.cpp | 2 +- src/lib/platform/OSXClipboardAnyBitmapConverter.h | 2 +- src/lib/platform/OSXClipboardAnyTextConverter.cpp | 2 +- src/lib/platform/OSXClipboardAnyTextConverter.h | 2 +- src/lib/platform/OSXClipboardBMPConverter.cpp | 2 +- src/lib/platform/OSXClipboardBMPConverter.h | 2 +- src/lib/platform/OSXClipboardHTMLConverter.cpp | 2 +- src/lib/platform/OSXClipboardHTMLConverter.h | 2 +- src/lib/platform/OSXClipboardTextConverter.cpp | 2 +- src/lib/platform/OSXClipboardTextConverter.h | 2 +- src/lib/platform/OSXClipboardUTF16Converter.cpp | 2 +- src/lib/platform/OSXClipboardUTF16Converter.h | 2 +- src/lib/platform/OSXDragSimulator.h | 2 +- src/lib/platform/OSXDragSimulator.m | 2 +- src/lib/platform/OSXDragView.h | 2 +- src/lib/platform/OSXDragView.m | 2 +- src/lib/platform/OSXEventQueueBuffer.cpp | 2 +- src/lib/platform/OSXEventQueueBuffer.h | 2 +- src/lib/platform/OSXKeyState.cpp | 2 +- src/lib/platform/OSXKeyState.h | 2 +- src/lib/platform/OSXPasteboardPeeker.h | 2 +- src/lib/platform/OSXPasteboardPeeker.m | 2 +- src/lib/platform/OSXScreen.cpp | 2 +- src/lib/platform/OSXScreen.h | 2 +- src/lib/platform/OSXScreenSaver.cpp | 2 +- src/lib/platform/OSXScreenSaver.h | 2 +- src/lib/platform/OSXScreenSaverControl.h | 2 +- src/lib/platform/OSXScreenSaverUtil.h | 2 +- src/lib/platform/XWindowsClipboard.cpp | 2 +- src/lib/platform/XWindowsClipboard.h | 2 +- src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp | 2 +- src/lib/platform/XWindowsClipboardAnyBitmapConverter.h | 2 +- src/lib/platform/XWindowsClipboardBMPConverter.cpp | 2 +- src/lib/platform/XWindowsClipboardBMPConverter.h | 2 +- src/lib/platform/XWindowsClipboardHTMLConverter.cpp | 2 +- src/lib/platform/XWindowsClipboardHTMLConverter.h | 2 +- src/lib/platform/XWindowsClipboardTextConverter.cpp | 2 +- src/lib/platform/XWindowsClipboardTextConverter.h | 2 +- src/lib/platform/XWindowsClipboardUCS2Converter.cpp | 2 +- src/lib/platform/XWindowsClipboardUCS2Converter.h | 2 +- src/lib/platform/XWindowsClipboardUTF8Converter.cpp | 2 +- src/lib/platform/XWindowsClipboardUTF8Converter.h | 2 +- src/lib/platform/XWindowsEventQueueBuffer.cpp | 2 +- src/lib/platform/XWindowsEventQueueBuffer.h | 2 +- src/lib/platform/XWindowsKeyState.cpp | 2 +- src/lib/platform/XWindowsKeyState.h | 2 +- src/lib/platform/XWindowsScreen.cpp | 2 +- src/lib/platform/XWindowsScreen.h | 2 +- src/lib/platform/XWindowsScreenSaver.cpp | 2 +- src/lib/platform/XWindowsScreenSaver.h | 2 +- src/lib/platform/XWindowsUtil.cpp | 2 +- src/lib/platform/XWindowsUtil.h | 2 +- src/lib/plugin/CMakeLists.txt | 2 +- src/lib/plugin/ns/CMakeLists.txt | 2 +- src/lib/plugin/ns/SecureListenSocket.cpp | 2 +- src/lib/plugin/ns/SecureListenSocket.h | 2 +- src/lib/plugin/ns/SecureSocket.cpp | 2 +- src/lib/plugin/ns/SecureSocket.h | 2 +- src/lib/plugin/ns/ns.cpp | 2 +- src/lib/plugin/ns/ns.h | 2 +- src/lib/plugin/winmmjoy/CMakeLists.txt | 2 +- src/lib/plugin/winmmjoy/winmmjoy.cpp | 2 +- src/lib/plugin/winmmjoy/winmmjoy.h | 2 +- src/lib/server/BaseClientProxy.cpp | 2 +- src/lib/server/BaseClientProxy.h | 2 +- src/lib/server/CMakeLists.txt | 2 +- src/lib/server/ClientListener.cpp | 2 +- src/lib/server/ClientListener.h | 2 +- src/lib/server/ClientProxy.cpp | 2 +- src/lib/server/ClientProxy.h | 2 +- src/lib/server/ClientProxy1_0.cpp | 2 +- src/lib/server/ClientProxy1_0.h | 2 +- src/lib/server/ClientProxy1_1.cpp | 2 +- src/lib/server/ClientProxy1_1.h | 2 +- src/lib/server/ClientProxy1_2.cpp | 2 +- src/lib/server/ClientProxy1_2.h | 2 +- src/lib/server/ClientProxy1_3.cpp | 2 +- src/lib/server/ClientProxy1_3.h | 2 +- src/lib/server/ClientProxy1_4.cpp | 2 +- src/lib/server/ClientProxy1_4.h | 2 +- src/lib/server/ClientProxy1_5.cpp | 2 +- src/lib/server/ClientProxy1_5.h | 2 +- src/lib/server/ClientProxy1_6.cpp | 2 +- src/lib/server/ClientProxy1_6.h | 2 +- src/lib/server/ClientProxyUnknown.cpp | 2 +- src/lib/server/ClientProxyUnknown.h | 2 +- src/lib/server/Config.cpp | 2 +- src/lib/server/Config.h | 2 +- src/lib/server/InputFilter.cpp | 2 +- src/lib/server/InputFilter.h | 2 +- src/lib/server/PrimaryClient.cpp | 2 +- src/lib/server/PrimaryClient.h | 2 +- src/lib/server/Server.cpp | 2 +- src/lib/server/Server.h | 2 +- src/lib/synergy/App.cpp | 2 +- src/lib/synergy/App.h | 2 +- src/lib/synergy/AppUtil.cpp | 2 +- src/lib/synergy/AppUtil.h | 2 +- src/lib/synergy/ArgParser.cpp | 2 +- src/lib/synergy/ArgParser.h | 2 +- src/lib/synergy/ArgsBase.cpp | 2 +- src/lib/synergy/ArgsBase.h | 2 +- src/lib/synergy/CMakeLists.txt | 2 +- src/lib/synergy/Chunk.cpp | 2 +- src/lib/synergy/Chunk.h | 2 +- src/lib/synergy/ClientApp.cpp | 2 +- src/lib/synergy/ClientApp.h | 2 +- src/lib/synergy/ClientArgs.cpp | 2 +- src/lib/synergy/ClientArgs.h | 2 +- src/lib/synergy/ClientTaskBarReceiver.cpp | 2 +- src/lib/synergy/ClientTaskBarReceiver.h | 2 +- src/lib/synergy/Clipboard.cpp | 2 +- src/lib/synergy/Clipboard.h | 2 +- src/lib/synergy/ClipboardChunk.cpp | 2 +- src/lib/synergy/ClipboardChunk.h | 2 +- src/lib/synergy/DaemonApp.cpp | 2 +- src/lib/synergy/DaemonApp.h | 2 +- src/lib/synergy/DragInformation.cpp | 2 +- src/lib/synergy/DragInformation.h | 2 +- src/lib/synergy/DropHelper.cpp | 2 +- src/lib/synergy/DropHelper.h | 2 +- src/lib/synergy/FileChunk.cpp | 2 +- src/lib/synergy/FileChunk.h | 2 +- src/lib/synergy/IApp.h | 2 +- src/lib/synergy/IAppUtil.h | 2 +- src/lib/synergy/IClient.h | 2 +- src/lib/synergy/IClipboard.cpp | 2 +- src/lib/synergy/IClipboard.h | 2 +- src/lib/synergy/IKeyState.cpp | 2 +- src/lib/synergy/IKeyState.h | 2 +- src/lib/synergy/INode.h | 2 +- src/lib/synergy/IPlatformScreen.h | 2 +- src/lib/synergy/IPrimaryScreen.cpp | 2 +- src/lib/synergy/IPrimaryScreen.h | 2 +- src/lib/synergy/IScreen.h | 2 +- src/lib/synergy/IScreenSaver.h | 2 +- src/lib/synergy/ISecondaryScreen.h | 2 +- src/lib/synergy/KeyMap.cpp | 2 +- src/lib/synergy/KeyMap.h | 2 +- src/lib/synergy/KeyState.cpp | 2 +- src/lib/synergy/KeyState.h | 2 +- src/lib/synergy/PacketStreamFilter.cpp | 2 +- src/lib/synergy/PacketStreamFilter.h | 2 +- src/lib/synergy/PlatformScreen.cpp | 2 +- src/lib/synergy/PlatformScreen.h | 2 +- src/lib/synergy/PortableTaskBarReceiver.cpp | 2 +- src/lib/synergy/PortableTaskBarReceiver.h | 2 +- src/lib/synergy/ProtocolUtil.cpp | 2 +- src/lib/synergy/ProtocolUtil.h | 2 +- src/lib/synergy/Screen.cpp | 2 +- src/lib/synergy/Screen.h | 2 +- src/lib/synergy/ServerApp.cpp | 2 +- src/lib/synergy/ServerApp.h | 2 +- src/lib/synergy/ServerArgs.cpp | 2 +- src/lib/synergy/ServerArgs.h | 2 +- src/lib/synergy/ServerTaskBarReceiver.cpp | 2 +- src/lib/synergy/ServerTaskBarReceiver.h | 2 +- src/lib/synergy/StreamChunker.cpp | 2 +- src/lib/synergy/StreamChunker.h | 2 +- src/lib/synergy/ToolApp.cpp | 2 +- src/lib/synergy/ToolApp.h | 2 +- src/lib/synergy/ToolArgs.cpp | 2 +- src/lib/synergy/ToolArgs.h | 2 +- src/lib/synergy/XScreen.cpp | 2 +- src/lib/synergy/XScreen.h | 2 +- src/lib/synergy/XSynergy.cpp | 2 +- src/lib/synergy/XSynergy.h | 2 +- src/lib/synergy/clipboard_types.h | 2 +- src/lib/synergy/key_types.cpp | 2 +- src/lib/synergy/key_types.h | 2 +- src/lib/synergy/mouse_types.h | 2 +- src/lib/synergy/option_types.h | 2 +- src/lib/synergy/protocol_types.cpp | 2 +- src/lib/synergy/protocol_types.h | 2 +- src/lib/synergy/unix/AppUtilUnix.cpp | 2 +- src/lib/synergy/unix/AppUtilUnix.h | 2 +- src/lib/synergy/win32/AppUtilWindows.cpp | 2 +- src/lib/synergy/win32/AppUtilWindows.h | 2 +- src/lib/synwinhk/CMakeLists.txt | 2 +- src/lib/synwinhk/synwinhk.cpp | 2 +- src/lib/synwinhk/synwinhk.h | 2 +- src/micro/CMakeLists.txt | 2 +- src/micro/uSynergy.h | 2 +- src/test/CMakeLists.txt | 2 +- src/test/global/TestEventQueue.cpp | 2 +- src/test/global/TestEventQueue.h | 2 +- src/test/global/gmock.h | 2 +- src/test/global/gtest.h | 2 +- src/test/guitests/src/VersionCheckerTests.cpp | 2 +- src/test/guitests/src/VersionCheckerTests.h | 2 +- src/test/guitests/src/main.cpp | 2 +- src/test/integtests/CMakeLists.txt | 2 +- src/test/integtests/Main.cpp | 2 +- src/test/integtests/arch/ArchInternetTests.cpp | 2 +- src/test/integtests/ipc/IpcTests.cpp | 2 +- src/test/integtests/net/NetworkTests.cpp | 2 +- src/test/integtests/platform/MSWindowsClipboardTests.cpp | 2 +- src/test/integtests/platform/MSWindowsKeyStateTests.cpp | 2 +- src/test/integtests/platform/OSXClipboardTests.cpp | 2 +- src/test/integtests/platform/OSXKeyStateTests.cpp | 2 +- src/test/integtests/platform/OSXScreenTests.cpp | 2 +- src/test/integtests/platform/XWindowsClipboardTests.cpp | 2 +- src/test/integtests/platform/XWindowsKeyStateTests.cpp | 2 +- src/test/integtests/platform/XWindowsScreenSaverTests.cpp | 2 +- src/test/integtests/platform/XWindowsScreenTests.cpp | 2 +- src/test/mock/io/MockStream.h | 2 +- src/test/mock/ipc/MockIpcServer.h | 2 +- src/test/mock/server/MockConfig.h | 2 +- src/test/mock/server/MockInputFilter.h | 2 +- src/test/mock/server/MockPrimaryClient.h | 2 +- src/test/mock/server/MockServer.h | 2 +- src/test/mock/synergy/MockApp.h | 2 +- src/test/mock/synergy/MockArgParser.h | 2 +- src/test/mock/synergy/MockEventQueue.h | 2 +- src/test/mock/synergy/MockKeyMap.h | 2 +- src/test/mock/synergy/MockKeyState.h | 2 +- src/test/mock/synergy/MockScreen.h | 2 +- src/test/unittests/CMakeLists.txt | 2 +- src/test/unittests/Main.cpp | 2 +- src/test/unittests/base/StringTests.cpp | 2 +- src/test/unittests/ipc/IpcLogOutputterTests.cpp | 2 +- src/test/unittests/platform/OSXKeyStateTests.cpp | 2 +- src/test/unittests/synergy/ArgParserTests.cpp | 2 +- src/test/unittests/synergy/ClientArgsParsingTests.cpp | 2 +- src/test/unittests/synergy/ClipboardChunkTests.cpp | 2 +- src/test/unittests/synergy/ClipboardTests.cpp | 2 +- src/test/unittests/synergy/DeprecatedArgsParsingTests.cpp | 2 +- src/test/unittests/synergy/GenericArgsParsingTests.cpp | 2 +- src/test/unittests/synergy/KeyStateTests.cpp | 2 +- src/test/unittests/synergy/ServerArgsParsingTests.cpp | 2 +- 647 files changed, 697 insertions(+), 697 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d5f72a2f..71708bbf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/LICENSE b/LICENSE index eb247025b..d1d394da9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ synergy -- mouse and keyboard sharing utility -Copyright (C) 2012-2014 Synergy Si Ltd. +Copyright (C) 2012-2016 Symless Ltd. Copyright (C) 2008-2014 Nick Bolton Copyright (C) 2002-2014 Chris Schoeneman diff --git a/ext/toolchain/__init__.py b/ext/toolchain/__init__.py index 906f67196..c10a454f2 100644 --- a/ext/toolchain/__init__.py +++ b/ext/toolchain/__init__.py @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/ext/toolchain/commands1.py b/ext/toolchain/commands1.py index 3b3ca7aaa..79cfc6f1b 100644 --- a/ext/toolchain/commands1.py +++ b/ext/toolchain/commands1.py @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/ext/toolchain/ftputil.py b/ext/toolchain/ftputil.py index b9a193060..bc90722f7 100644 --- a/ext/toolchain/ftputil.py +++ b/ext/toolchain/ftputil.py @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2010 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/ext/toolchain/generators.py b/ext/toolchain/generators.py index 3a754759c..44a4d287d 100644 --- a/ext/toolchain/generators.py +++ b/ext/toolchain/generators.py @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/hm.py b/hm.py index 1dec6f858..32415ea0e 100644 --- a/hm.py +++ b/hm.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/res/deb/copyright b/res/deb/copyright index 9fc5f71ba..917a06885 100644 --- a/res/deb/copyright +++ b/res/deb/copyright @@ -1,7 +1,7 @@ /usr/share/common-licenses/GPL-2 synergy -- mouse and keyboard sharing utility -Copyright (C) 2014 Synergy Si Ltd. +Copyright (C) 2014-2016 Symless Ltd. Copyright (C) 2004 Chris Schoeneman This package is free software; you can redistribute it and/or diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 45aa7df4b..d75c0adcc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2011 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/cmd/CMakeLists.txt b/src/cmd/CMakeLists.txt index ad087c6c1..7a7ed82a3 100644 --- a/src/cmd/CMakeLists.txt +++ b/src/cmd/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2011 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyc/CMakeLists.txt b/src/cmd/synergyc/CMakeLists.txt index 80d869dd9..11d428d44 100644 --- a/src/cmd/synergyc/CMakeLists.txt +++ b/src/cmd/synergyc/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp b/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp index 7455d8c7e..039246cc0 100644 --- a/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp +++ b/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.h b/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.h index d5723dcf7..f02a9950a 100644 --- a/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.h +++ b/src/cmd/synergyc/MSWindowsClientTaskBarReceiver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyc/OSXClientTaskBarReceiver.cpp b/src/cmd/synergyc/OSXClientTaskBarReceiver.cpp index 9ed75bce9..569df39f6 100644 --- a/src/cmd/synergyc/OSXClientTaskBarReceiver.cpp +++ b/src/cmd/synergyc/OSXClientTaskBarReceiver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyc/OSXClientTaskBarReceiver.h b/src/cmd/synergyc/OSXClientTaskBarReceiver.h index 84c85cb31..5ab07a6bc 100644 --- a/src/cmd/synergyc/OSXClientTaskBarReceiver.h +++ b/src/cmd/synergyc/OSXClientTaskBarReceiver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyc/XWindowsClientTaskBarReceiver.cpp b/src/cmd/synergyc/XWindowsClientTaskBarReceiver.cpp index b3a50cb40..4cb5394d6 100644 --- a/src/cmd/synergyc/XWindowsClientTaskBarReceiver.cpp +++ b/src/cmd/synergyc/XWindowsClientTaskBarReceiver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyc/XWindowsClientTaskBarReceiver.h b/src/cmd/synergyc/XWindowsClientTaskBarReceiver.h index 6becf6586..45a1bab88 100644 --- a/src/cmd/synergyc/XWindowsClientTaskBarReceiver.h +++ b/src/cmd/synergyc/XWindowsClientTaskBarReceiver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyc/synergyc.cpp b/src/cmd/synergyc/synergyc.cpp index 2e40db981..3b4ea57d5 100644 --- a/src/cmd/synergyc/synergyc.cpp +++ b/src/cmd/synergyc/synergyc.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyd/CMakeLists.txt b/src/cmd/synergyd/CMakeLists.txt index a3395561a..1caa35b52 100644 --- a/src/cmd/synergyd/CMakeLists.txt +++ b/src/cmd/synergyd/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2012 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyd/synergyd.cpp b/src/cmd/synergyd/synergyd.cpp index d0ee0f01c..a0ad7449c 100644 --- a/src/cmd/synergyd/synergyd.cpp +++ b/src/cmd/synergyd/synergyd.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyp/CMakeLists.txt b/src/cmd/synergyp/CMakeLists.txt index 5dc5a2bd8..171ef9990 100644 --- a/src/cmd/synergyp/CMakeLists.txt +++ b/src/cmd/synergyp/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp b/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp index c09234a2b..4f35fe4f2 100644 --- a/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp +++ b/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.h b/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.h index aae9aaaac..970dafed3 100644 --- a/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.h +++ b/src/cmd/synergyp/MSWindowsPortableTaskBarReceiver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyp/OSXPortableTaskBarReceiver.cpp b/src/cmd/synergyp/OSXPortableTaskBarReceiver.cpp index e69ed1016..1909ead49 100644 --- a/src/cmd/synergyp/OSXPortableTaskBarReceiver.cpp +++ b/src/cmd/synergyp/OSXPortableTaskBarReceiver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyp/OSXPortableTaskBarReceiver.h b/src/cmd/synergyp/OSXPortableTaskBarReceiver.h index 810391477..3c2540679 100644 --- a/src/cmd/synergyp/OSXPortableTaskBarReceiver.h +++ b/src/cmd/synergyp/OSXPortableTaskBarReceiver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyp/XWindowsPortableTaskBarReceiver.cpp b/src/cmd/synergyp/XWindowsPortableTaskBarReceiver.cpp index 2b2ec5204..c97f5b29c 100644 --- a/src/cmd/synergyp/XWindowsPortableTaskBarReceiver.cpp +++ b/src/cmd/synergyp/XWindowsPortableTaskBarReceiver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyp/XWindowsPortableTaskBarReceiver.h b/src/cmd/synergyp/XWindowsPortableTaskBarReceiver.h index 6b694bb27..ee6f1385f 100644 --- a/src/cmd/synergyp/XWindowsPortableTaskBarReceiver.h +++ b/src/cmd/synergyp/XWindowsPortableTaskBarReceiver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergyp/synergyp.cpp b/src/cmd/synergyp/synergyp.cpp index 5cefb2e38..70cff2b4b 100644 --- a/src/cmd/synergyp/synergyp.cpp +++ b/src/cmd/synergyp/synergyp.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/cmd/synergys/CMakeLists.txt b/src/cmd/synergys/CMakeLists.txt index 737eba029..c749e09a8 100644 --- a/src/cmd/synergys/CMakeLists.txt +++ b/src/cmd/synergys/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp b/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp index d7ced0737..f65e2a352 100644 --- a/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp +++ b/src/cmd/synergys/MSWindowsServerTaskBarReceiver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergys/MSWindowsServerTaskBarReceiver.h b/src/cmd/synergys/MSWindowsServerTaskBarReceiver.h index 0eb9f2c18..15c11078a 100644 --- a/src/cmd/synergys/MSWindowsServerTaskBarReceiver.h +++ b/src/cmd/synergys/MSWindowsServerTaskBarReceiver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergys/OSXServerTaskBarReceiver.cpp b/src/cmd/synergys/OSXServerTaskBarReceiver.cpp index f69b9c24b..d919f76f9 100644 --- a/src/cmd/synergys/OSXServerTaskBarReceiver.cpp +++ b/src/cmd/synergys/OSXServerTaskBarReceiver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergys/OSXServerTaskBarReceiver.h b/src/cmd/synergys/OSXServerTaskBarReceiver.h index fa6a94410..1943c65af 100644 --- a/src/cmd/synergys/OSXServerTaskBarReceiver.h +++ b/src/cmd/synergys/OSXServerTaskBarReceiver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergys/XWindowsServerTaskBarReceiver.cpp b/src/cmd/synergys/XWindowsServerTaskBarReceiver.cpp index 505d36723..f0e69fde7 100644 --- a/src/cmd/synergys/XWindowsServerTaskBarReceiver.cpp +++ b/src/cmd/synergys/XWindowsServerTaskBarReceiver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergys/XWindowsServerTaskBarReceiver.h b/src/cmd/synergys/XWindowsServerTaskBarReceiver.h index 0158e551c..34a0dc268 100644 --- a/src/cmd/synergys/XWindowsServerTaskBarReceiver.h +++ b/src/cmd/synergys/XWindowsServerTaskBarReceiver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/synergys/synergys.cpp b/src/cmd/synergys/synergys.cpp index 9c7c170f0..97258f85d 100644 --- a/src/cmd/synergys/synergys.cpp +++ b/src/cmd/synergys/synergys.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/cmd/syntool/CMakeLists.txt b/src/cmd/syntool/CMakeLists.txt index a7a54579e..7f10c010b 100644 --- a/src/cmd/syntool/CMakeLists.txt +++ b/src/cmd/syntool/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2014 Synergy Si Ltd. +# 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 diff --git a/src/cmd/syntool/syntool.cpp b/src/cmd/syntool/syntool.cpp index eb05c03c8..c93d42564 100644 --- a/src/cmd/syntool/syntool.cpp +++ b/src/cmd/syntool/syntool.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/cmd/usynergy/CMakeLists.txt b/src/cmd/usynergy/CMakeLists.txt index c2831080a..9a6f9a3d2 100644 --- a/src/cmd/usynergy/CMakeLists.txt +++ b/src/cmd/usynergy/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2012 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/gui/gui.ts b/src/gui/gui.ts index d1e1133d0..96eeeb7d0 100644 --- a/src/gui/gui.ts +++ b/src/gui/gui.ts @@ -12,7 +12,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -21,7 +21,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/AboutDialogBase.ui b/src/gui/res/AboutDialogBase.ui index cc8189ba5..ba18e3091 100644 --- a/src/gui/res/AboutDialogBase.ui +++ b/src/gui/res/AboutDialogBase.ui @@ -52,7 +52,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_af-ZA.ts b/src/gui/res/lang/gui_af-ZA.ts index bcad2d59e..1102b22b7 100644 --- a/src/gui/res/lang/gui_af-ZA.ts +++ b/src/gui/res/lang/gui_af-ZA.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_ar.ts b/src/gui/res/lang/gui_ar.ts index b323be946..cde17ab08 100644 --- a/src/gui/res/lang/gui_ar.ts +++ b/src/gui/res/lang/gui_ar.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_bg-BG.ts b/src/gui/res/lang/gui_bg-BG.ts index 1dab7b9a7..096fa7b4b 100644 --- a/src/gui/res/lang/gui_bg-BG.ts +++ b/src/gui/res/lang/gui_bg-BG.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_ca-AD.ts b/src/gui/res/lang/gui_ca-AD.ts index 91c810503..7e41d58bf 100644 --- a/src/gui/res/lang/gui_ca-AD.ts +++ b/src/gui/res/lang/gui_ca-AD.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_cs-CZ.ts b/src/gui/res/lang/gui_cs-CZ.ts index 42368004c..5dbe5cdd0 100644 --- a/src/gui/res/lang/gui_cs-CZ.ts +++ b/src/gui/res/lang/gui_cs-CZ.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_cy.ts b/src/gui/res/lang/gui_cy.ts index 74494f6da..70b352c9e 100644 --- a/src/gui/res/lang/gui_cy.ts +++ b/src/gui/res/lang/gui_cy.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_da.ts b/src/gui/res/lang/gui_da.ts index 67b3e74ac..c871d64ec 100644 --- a/src/gui/res/lang/gui_da.ts +++ b/src/gui/res/lang/gui_da.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_de.ts b/src/gui/res/lang/gui_de.ts index 651849d9a..89cc2361a 100644 --- a/src/gui/res/lang/gui_de.ts +++ b/src/gui/res/lang/gui_de.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_es.ts b/src/gui/res/lang/gui_es.ts index 1d470d6f6..076441551 100644 --- a/src/gui/res/lang/gui_es.ts +++ b/src/gui/res/lang/gui_es.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_et-EE.ts b/src/gui/res/lang/gui_et-EE.ts index 198105873..35ae37198 100644 --- a/src/gui/res/lang/gui_et-EE.ts +++ b/src/gui/res/lang/gui_et-EE.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_fi.ts b/src/gui/res/lang/gui_fi.ts index d887384e8..046bbc8b2 100644 --- a/src/gui/res/lang/gui_fi.ts +++ b/src/gui/res/lang/gui_fi.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_fr.ts b/src/gui/res/lang/gui_fr.ts index 238cb2c82..0b5ebecc7 100644 --- a/src/gui/res/lang/gui_fr.ts +++ b/src/gui/res/lang/gui_fr.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_gl.ts b/src/gui/res/lang/gui_gl.ts index 8613dee87..66036a03f 100644 --- a/src/gui/res/lang/gui_gl.ts +++ b/src/gui/res/lang/gui_gl.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_grk.ts b/src/gui/res/lang/gui_grk.ts index 70575fa1c..5d06431db 100644 --- a/src/gui/res/lang/gui_grk.ts +++ b/src/gui/res/lang/gui_grk.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_he.ts b/src/gui/res/lang/gui_he.ts index 07bff415e..b4e81210a 100644 --- a/src/gui/res/lang/gui_he.ts +++ b/src/gui/res/lang/gui_he.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_hi.ts b/src/gui/res/lang/gui_hi.ts index 7c7593aca..ccc4872cf 100644 --- a/src/gui/res/lang/gui_hi.ts +++ b/src/gui/res/lang/gui_hi.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_hr-HR.ts b/src/gui/res/lang/gui_hr-HR.ts index 2c71bbded..26e0249a3 100644 --- a/src/gui/res/lang/gui_hr-HR.ts +++ b/src/gui/res/lang/gui_hr-HR.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_hu-HU.ts b/src/gui/res/lang/gui_hu-HU.ts index d2299bafc..f21be596b 100644 --- a/src/gui/res/lang/gui_hu-HU.ts +++ b/src/gui/res/lang/gui_hu-HU.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_id.ts b/src/gui/res/lang/gui_id.ts index f25e48e16..362191871 100644 --- a/src/gui/res/lang/gui_id.ts +++ b/src/gui/res/lang/gui_id.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_is-IS.ts b/src/gui/res/lang/gui_is-IS.ts index 055cdaca3..7dd220513 100644 --- a/src/gui/res/lang/gui_is-IS.ts +++ b/src/gui/res/lang/gui_is-IS.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_it.ts b/src/gui/res/lang/gui_it.ts index b39098f67..fb83ab6b0 100644 --- a/src/gui/res/lang/gui_it.ts +++ b/src/gui/res/lang/gui_it.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_ja-JP.ts b/src/gui/res/lang/gui_ja-JP.ts index e2e16961c..efb549c47 100644 --- a/src/gui/res/lang/gui_ja-JP.ts +++ b/src/gui/res/lang/gui_ja-JP.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_ko.ts b/src/gui/res/lang/gui_ko.ts index 7cc6bcb4d..27a8275fa 100644 --- a/src/gui/res/lang/gui_ko.ts +++ b/src/gui/res/lang/gui_ko.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_lt.ts b/src/gui/res/lang/gui_lt.ts index d60cdd5ac..af48b53f9 100644 --- a/src/gui/res/lang/gui_lt.ts +++ b/src/gui/res/lang/gui_lt.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_lv.ts b/src/gui/res/lang/gui_lv.ts index 6edf11d60..11216486c 100644 --- a/src/gui/res/lang/gui_lv.ts +++ b/src/gui/res/lang/gui_lv.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_mr.ts b/src/gui/res/lang/gui_mr.ts index 095b7f912..d4b4017de 100644 --- a/src/gui/res/lang/gui_mr.ts +++ b/src/gui/res/lang/gui_mr.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_nl-NL.ts b/src/gui/res/lang/gui_nl-NL.ts index d0d1b22c9..79b2b40b7 100644 --- a/src/gui/res/lang/gui_nl-NL.ts +++ b/src/gui/res/lang/gui_nl-NL.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_no.ts b/src/gui/res/lang/gui_no.ts index 65069333d..15f672e44 100644 --- a/src/gui/res/lang/gui_no.ts +++ b/src/gui/res/lang/gui_no.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_pes-IR.ts b/src/gui/res/lang/gui_pes-IR.ts index a7a6bbdd9..65d570a72 100644 --- a/src/gui/res/lang/gui_pes-IR.ts +++ b/src/gui/res/lang/gui_pes-IR.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_pl-PL.ts b/src/gui/res/lang/gui_pl-PL.ts index b95016d8b..062876965 100644 --- a/src/gui/res/lang/gui_pl-PL.ts +++ b/src/gui/res/lang/gui_pl-PL.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_pt-BR.ts b/src/gui/res/lang/gui_pt-BR.ts index 20e450a1c..93c629758 100644 --- a/src/gui/res/lang/gui_pt-BR.ts +++ b/src/gui/res/lang/gui_pt-BR.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_pt-PT.ts b/src/gui/res/lang/gui_pt-PT.ts index bb2e461aa..ed1fa7f53 100644 --- a/src/gui/res/lang/gui_pt-PT.ts +++ b/src/gui/res/lang/gui_pt-PT.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_ro.ts b/src/gui/res/lang/gui_ro.ts index edce79395..eb69251b8 100644 --- a/src/gui/res/lang/gui_ro.ts +++ b/src/gui/res/lang/gui_ro.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_ru.ts b/src/gui/res/lang/gui_ru.ts index 8015fa4db..242135708 100644 --- a/src/gui/res/lang/gui_ru.ts +++ b/src/gui/res/lang/gui_ru.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_si.ts b/src/gui/res/lang/gui_si.ts index aa4668738..cad8e119f 100644 --- a/src/gui/res/lang/gui_si.ts +++ b/src/gui/res/lang/gui_si.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_sk-SK.ts b/src/gui/res/lang/gui_sk-SK.ts index d96cc3f52..aa3fe7a22 100644 --- a/src/gui/res/lang/gui_sk-SK.ts +++ b/src/gui/res/lang/gui_sk-SK.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_sl-SI.ts b/src/gui/res/lang/gui_sl-SI.ts index 87ef1e35a..5092d91b4 100644 --- a/src/gui/res/lang/gui_sl-SI.ts +++ b/src/gui/res/lang/gui_sl-SI.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_sq-AL.ts b/src/gui/res/lang/gui_sq-AL.ts index 64290cdfa..eebd4aa04 100644 --- a/src/gui/res/lang/gui_sq-AL.ts +++ b/src/gui/res/lang/gui_sq-AL.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_sr.ts b/src/gui/res/lang/gui_sr.ts index 95dd152fb..8cb55ed3d 100644 --- a/src/gui/res/lang/gui_sr.ts +++ b/src/gui/res/lang/gui_sr.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_sv.ts b/src/gui/res/lang/gui_sv.ts index 13bff5f23..eccf78cbb 100644 --- a/src/gui/res/lang/gui_sv.ts +++ b/src/gui/res/lang/gui_sv.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_th-TH.ts b/src/gui/res/lang/gui_th-TH.ts index 8c51bdd5c..d81d707c1 100644 --- a/src/gui/res/lang/gui_th-TH.ts +++ b/src/gui/res/lang/gui_th-TH.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_tr-TR.ts b/src/gui/res/lang/gui_tr-TR.ts index 72d30933f..2bb1ad87d 100644 --- a/src/gui/res/lang/gui_tr-TR.ts +++ b/src/gui/res/lang/gui_tr-TR.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_uk.ts b/src/gui/res/lang/gui_uk.ts index cd0b85905..07b0b8f93 100644 --- a/src/gui/res/lang/gui_uk.ts +++ b/src/gui/res/lang/gui_uk.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_ur.ts b/src/gui/res/lang/gui_ur.ts index f7f1da4f5..eeccb241d 100644 --- a/src/gui/res/lang/gui_ur.ts +++ b/src/gui/res/lang/gui_ur.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_vi.ts b/src/gui/res/lang/gui_vi.ts index d98c947e5..9d63796d9 100644 --- a/src/gui/res/lang/gui_vi.ts +++ b/src/gui/res/lang/gui_vi.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_zh-CN.ts b/src/gui/res/lang/gui_zh-CN.ts index 7cc0da77e..ee0638f1a 100644 --- a/src/gui/res/lang/gui_zh-CN.ts +++ b/src/gui/res/lang/gui_zh-CN.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/res/lang/gui_zh-TW.ts b/src/gui/res/lang/gui_zh-TW.ts index 049f4081f..d0a9e4002 100644 --- a/src/gui/res/lang/gui_zh-TW.ts +++ b/src/gui/res/lang/gui_zh-TW.ts @@ -10,7 +10,7 @@ <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> @@ -19,7 +19,7 @@ Visit our website for help and info (symless.com). </p> <p> Keyboard and mouse sharing application. Cross platform and open source.<br /><br /> -Copyright © 2012 Synergy Si Ltd.<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 /> Synergy is based on CosmoSynergy by Richard Lee and Adam Feder.<br /> diff --git a/src/gui/src/AboutDialog.cpp b/src/gui/src/AboutDialog.cpp index aefd3b72c..6fc1d57ed 100644 --- a/src/gui/src/AboutDialog.cpp +++ b/src/gui/src/AboutDialog.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/AboutDialog.h b/src/gui/src/AboutDialog.h index b2f6623d8..8e1d966ff 100644 --- a/src/gui/src/AboutDialog.h +++ b/src/gui/src/AboutDialog.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/Action.cpp b/src/gui/src/Action.cpp index c950b8ed3..e4930e105 100644 --- a/src/gui/src/Action.cpp +++ b/src/gui/src/Action.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/Action.h b/src/gui/src/Action.h index 11893c919..18845df44 100644 --- a/src/gui/src/Action.h +++ b/src/gui/src/Action.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ActionDialog.cpp b/src/gui/src/ActionDialog.cpp index 84f745d4d..3fc951fc1 100644 --- a/src/gui/src/ActionDialog.cpp +++ b/src/gui/src/ActionDialog.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ActionDialog.h b/src/gui/src/ActionDialog.h index b652fc281..882d907f3 100644 --- a/src/gui/src/ActionDialog.h +++ b/src/gui/src/ActionDialog.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/AddClientDialog.cpp b/src/gui/src/AddClientDialog.cpp index 36a19a8cf..f14253b79 100644 --- a/src/gui/src/AddClientDialog.cpp +++ b/src/gui/src/AddClientDialog.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/gui/src/AddClientDialog.h b/src/gui/src/AddClientDialog.h index 4877e3509..6b5623767 100644 --- a/src/gui/src/AddClientDialog.h +++ b/src/gui/src/AddClientDialog.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp index f96c6633c..a48bbc04c 100644 --- a/src/gui/src/AppConfig.cpp +++ b/src/gui/src/AppConfig.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h index 713753f90..43e35c7fc 100644 --- a/src/gui/src/AppConfig.h +++ b/src/gui/src/AppConfig.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/BaseConfig.cpp b/src/gui/src/BaseConfig.cpp index 6d115a2d7..5df0f0087 100644 --- a/src/gui/src/BaseConfig.cpp +++ b/src/gui/src/BaseConfig.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/BaseConfig.h b/src/gui/src/BaseConfig.h index 33bb6cf54..006f945cc 100644 --- a/src/gui/src/BaseConfig.h +++ b/src/gui/src/BaseConfig.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/CommandProcess.cpp b/src/gui/src/CommandProcess.cpp index e1b4a7e4c..96f00e6b3 100644 --- a/src/gui/src/CommandProcess.cpp +++ b/src/gui/src/CommandProcess.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/gui/src/CommandProcess.h b/src/gui/src/CommandProcess.h index ff6c16126..6808ac14f 100644 --- a/src/gui/src/CommandProcess.h +++ b/src/gui/src/CommandProcess.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp index 13560c1b8..d537a24f7 100644 --- a/src/gui/src/CoreInterface.cpp +++ b/src/gui/src/CoreInterface.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/CoreInterface.h b/src/gui/src/CoreInterface.h index f65348af8..13e8fd873 100644 --- a/src/gui/src/CoreInterface.h +++ b/src/gui/src/CoreInterface.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/DataDownloader.cpp b/src/gui/src/DataDownloader.cpp index d88699037..d88203635 100644 --- a/src/gui/src/DataDownloader.cpp +++ b/src/gui/src/DataDownloader.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/gui/src/DataDownloader.h b/src/gui/src/DataDownloader.h index 9e2106447..07802583d 100644 --- a/src/gui/src/DataDownloader.h +++ b/src/gui/src/DataDownloader.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/gui/src/EditionType.h b/src/gui/src/EditionType.h index 2229cd231..d294cda53 100644 --- a/src/gui/src/EditionType.h +++ b/src/gui/src/EditionType.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/Fingerprint.cpp b/src/gui/src/Fingerprint.cpp index b222f242c..ba7119249 100644 --- a/src/gui/src/Fingerprint.cpp +++ b/src/gui/src/Fingerprint.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/Fingerprint.h b/src/gui/src/Fingerprint.h index a6f021fb1..e53ffd3c4 100644 --- a/src/gui/src/Fingerprint.h +++ b/src/gui/src/Fingerprint.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/Hotkey.cpp b/src/gui/src/Hotkey.cpp index e75b80a40..bc3938b6a 100644 --- a/src/gui/src/Hotkey.cpp +++ b/src/gui/src/Hotkey.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/Hotkey.h b/src/gui/src/Hotkey.h index e2aa6f1e9..2ea0f4abe 100644 --- a/src/gui/src/Hotkey.h +++ b/src/gui/src/Hotkey.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/HotkeyDialog.cpp b/src/gui/src/HotkeyDialog.cpp index 56d94491d..c072fdccd 100644 --- a/src/gui/src/HotkeyDialog.cpp +++ b/src/gui/src/HotkeyDialog.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/HotkeyDialog.h b/src/gui/src/HotkeyDialog.h index bb8d983ed..a7b3c2e34 100644 --- a/src/gui/src/HotkeyDialog.h +++ b/src/gui/src/HotkeyDialog.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/Ipc.cpp b/src/gui/src/Ipc.cpp index dda27b223..6cc3e97cd 100644 --- a/src/gui/src/Ipc.cpp +++ b/src/gui/src/Ipc.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/gui/src/Ipc.h b/src/gui/src/Ipc.h index 8178d5318..8b86b069a 100644 --- a/src/gui/src/Ipc.h +++ b/src/gui/src/Ipc.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/gui/src/IpcClient.cpp b/src/gui/src/IpcClient.cpp index 728a800cd..941a79d23 100644 --- a/src/gui/src/IpcClient.cpp +++ b/src/gui/src/IpcClient.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/gui/src/IpcClient.h b/src/gui/src/IpcClient.h index b49d9e6a1..e67611762 100644 --- a/src/gui/src/IpcClient.h +++ b/src/gui/src/IpcClient.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/gui/src/IpcReader.cpp b/src/gui/src/IpcReader.cpp index dc30657e6..227fc715e 100644 --- a/src/gui/src/IpcReader.cpp +++ b/src/gui/src/IpcReader.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/gui/src/IpcReader.h b/src/gui/src/IpcReader.h index 4efd00a4b..e4f7c75cc 100644 --- a/src/gui/src/IpcReader.h +++ b/src/gui/src/IpcReader.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/gui/src/KeySequence.cpp b/src/gui/src/KeySequence.cpp index 444088753..6548a83fb 100644 --- a/src/gui/src/KeySequence.cpp +++ b/src/gui/src/KeySequence.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/KeySequence.h b/src/gui/src/KeySequence.h index b6be825f4..3797b05d7 100644 --- a/src/gui/src/KeySequence.h +++ b/src/gui/src/KeySequence.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/KeySequenceWidget.cpp b/src/gui/src/KeySequenceWidget.cpp index 5ce7c5901..b3fa517d0 100644 --- a/src/gui/src/KeySequenceWidget.cpp +++ b/src/gui/src/KeySequenceWidget.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/KeySequenceWidget.h b/src/gui/src/KeySequenceWidget.h index 6670ee6b2..f9fea8f7d 100644 --- a/src/gui/src/KeySequenceWidget.h +++ b/src/gui/src/KeySequenceWidget.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 8f7cbc5e0..332fcbd65 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index da06c2e73..87380f963 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/NewScreenWidget.cpp b/src/gui/src/NewScreenWidget.cpp index d16ebdb41..e5f39a669 100644 --- a/src/gui/src/NewScreenWidget.cpp +++ b/src/gui/src/NewScreenWidget.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/NewScreenWidget.h b/src/gui/src/NewScreenWidget.h index c5f884121..c45030adf 100644 --- a/src/gui/src/NewScreenWidget.h +++ b/src/gui/src/NewScreenWidget.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/Plugin.cpp b/src/gui/src/Plugin.cpp index 8118d9507..50079da24 100644 --- a/src/gui/src/Plugin.cpp +++ b/src/gui/src/Plugin.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/Plugin.h b/src/gui/src/Plugin.h index 431e1a806..bec6a1c22 100644 --- a/src/gui/src/Plugin.h +++ b/src/gui/src/Plugin.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/PluginManager.cpp b/src/gui/src/PluginManager.cpp index f4d16ff0a..b498751a7 100644 --- a/src/gui/src/PluginManager.cpp +++ b/src/gui/src/PluginManager.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/PluginManager.h b/src/gui/src/PluginManager.h index f49c03b01..b1450fda2 100644 --- a/src/gui/src/PluginManager.h +++ b/src/gui/src/PluginManager.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/PluginWizardPage.cpp b/src/gui/src/PluginWizardPage.cpp index ace607da6..8be207520 100644 --- a/src/gui/src/PluginWizardPage.cpp +++ b/src/gui/src/PluginWizardPage.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/PluginWizardPage.h b/src/gui/src/PluginWizardPage.h index 5c889e0bc..d43197865 100644 --- a/src/gui/src/PluginWizardPage.h +++ b/src/gui/src/PluginWizardPage.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ProcessorArch.h b/src/gui/src/ProcessorArch.h index 76f6e96a0..43bf3126a 100644 --- a/src/gui/src/ProcessorArch.h +++ b/src/gui/src/ProcessorArch.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/QSynergyApplication.cpp b/src/gui/src/QSynergyApplication.cpp index e0155c224..27705f322 100644 --- a/src/gui/src/QSynergyApplication.cpp +++ b/src/gui/src/QSynergyApplication.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/QSynergyApplication.h b/src/gui/src/QSynergyApplication.h index 8cbe898c0..091ba852e 100644 --- a/src/gui/src/QSynergyApplication.h +++ b/src/gui/src/QSynergyApplication.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp index dcc8f5eb5..a21343ec6 100644 --- a/src/gui/src/QUtility.cpp +++ b/src/gui/src/QUtility.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/gui/src/QUtility.h b/src/gui/src/QUtility.h index 01a42c038..0738d96cc 100644 --- a/src/gui/src/QUtility.h +++ b/src/gui/src/QUtility.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/gui/src/Screen.cpp b/src/gui/src/Screen.cpp index eb9de091c..ca6727d6f 100644 --- a/src/gui/src/Screen.cpp +++ b/src/gui/src/Screen.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/Screen.h b/src/gui/src/Screen.h index 397cce5c1..d5aa3edcb 100644 --- a/src/gui/src/Screen.h +++ b/src/gui/src/Screen.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ScreenSettingsDialog.cpp b/src/gui/src/ScreenSettingsDialog.cpp index 8eab272e6..05f769d13 100644 --- a/src/gui/src/ScreenSettingsDialog.cpp +++ b/src/gui/src/ScreenSettingsDialog.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ScreenSettingsDialog.h b/src/gui/src/ScreenSettingsDialog.h index 9c35919ed..aa470837d 100644 --- a/src/gui/src/ScreenSettingsDialog.h +++ b/src/gui/src/ScreenSettingsDialog.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ScreenSetupModel.cpp b/src/gui/src/ScreenSetupModel.cpp index 7c8038303..919b07707 100644 --- a/src/gui/src/ScreenSetupModel.cpp +++ b/src/gui/src/ScreenSetupModel.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ScreenSetupModel.h b/src/gui/src/ScreenSetupModel.h index d55a316a3..f8c9cabee 100644 --- a/src/gui/src/ScreenSetupModel.h +++ b/src/gui/src/ScreenSetupModel.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ScreenSetupView.cpp b/src/gui/src/ScreenSetupView.cpp index 0c0adee21..d86ab499d 100644 --- a/src/gui/src/ScreenSetupView.cpp +++ b/src/gui/src/ScreenSetupView.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ScreenSetupView.h b/src/gui/src/ScreenSetupView.h index 9ee955f44..9a515d39f 100644 --- a/src/gui/src/ScreenSetupView.h +++ b/src/gui/src/ScreenSetupView.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ServerConfig.cpp b/src/gui/src/ServerConfig.cpp index d84f91f03..b7c866f73 100644 --- a/src/gui/src/ServerConfig.cpp +++ b/src/gui/src/ServerConfig.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ServerConfig.h b/src/gui/src/ServerConfig.h index 15214a7ee..29b40fab3 100644 --- a/src/gui/src/ServerConfig.h +++ b/src/gui/src/ServerConfig.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ServerConfigDialog.cpp b/src/gui/src/ServerConfigDialog.cpp index 501c758c5..06923a7f5 100644 --- a/src/gui/src/ServerConfigDialog.cpp +++ b/src/gui/src/ServerConfigDialog.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ServerConfigDialog.h b/src/gui/src/ServerConfigDialog.h index fad42492f..b0e224db2 100644 --- a/src/gui/src/ServerConfigDialog.h +++ b/src/gui/src/ServerConfigDialog.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp index f3deebba2..5d5b53d26 100644 --- a/src/gui/src/SettingsDialog.cpp +++ b/src/gui/src/SettingsDialog.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/SettingsDialog.h b/src/gui/src/SettingsDialog.h index ea30ac32e..656323a6b 100644 --- a/src/gui/src/SettingsDialog.h +++ b/src/gui/src/SettingsDialog.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index f5a40b6c0..3d23b017f 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-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 diff --git a/src/gui/src/SetupWizard.h b/src/gui/src/SetupWizard.h index 328de0ee7..34b301a25 100644 --- a/src/gui/src/SetupWizard.h +++ b/src/gui/src/SetupWizard.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-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 diff --git a/src/gui/src/SslCertificate.cpp b/src/gui/src/SslCertificate.cpp index 47919689d..7a8403c05 100644 --- a/src/gui/src/SslCertificate.cpp +++ b/src/gui/src/SslCertificate.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/SslCertificate.h b/src/gui/src/SslCertificate.h index 04e098c79..4455f9bf1 100644 --- a/src/gui/src/SslCertificate.h +++ b/src/gui/src/SslCertificate.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/SynergyLocale.cpp b/src/gui/src/SynergyLocale.cpp index 685f4fa5e..fa980ed43 100644 --- a/src/gui/src/SynergyLocale.cpp +++ b/src/gui/src/SynergyLocale.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/gui/src/SynergyLocale.h b/src/gui/src/SynergyLocale.h index 7dfe72ea1..68bd2d7ab 100644 --- a/src/gui/src/SynergyLocale.h +++ b/src/gui/src/SynergyLocale.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/gui/src/TcpSocketReader.cpp b/src/gui/src/TcpSocketReader.cpp index 843f98db9..e9600fc74 100644 --- a/src/gui/src/TcpSocketReader.cpp +++ b/src/gui/src/TcpSocketReader.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/gui/src/TrashScreenWidget.cpp b/src/gui/src/TrashScreenWidget.cpp index 13c406b91..a632bbe90 100644 --- a/src/gui/src/TrashScreenWidget.cpp +++ b/src/gui/src/TrashScreenWidget.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/TrashScreenWidget.h b/src/gui/src/TrashScreenWidget.h index 14a4571d1..16aa3f07a 100644 --- a/src/gui/src/TrashScreenWidget.h +++ b/src/gui/src/TrashScreenWidget.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/VersionChecker.cpp b/src/gui/src/VersionChecker.cpp index a7af296c5..1f6980fc8 100644 --- a/src/gui/src/VersionChecker.cpp +++ b/src/gui/src/VersionChecker.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/gui/src/VersionChecker.h b/src/gui/src/VersionChecker.h index b7133fdb6..ae2ff33ea 100644 --- a/src/gui/src/VersionChecker.h +++ b/src/gui/src/VersionChecker.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/gui/src/WebClient.cpp b/src/gui/src/WebClient.cpp index 451bf47f9..4b4b999b4 100644 --- a/src/gui/src/WebClient.cpp +++ b/src/gui/src/WebClient.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si, Std. + * 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 diff --git a/src/gui/src/WebClient.h b/src/gui/src/WebClient.h index 84e2f7445..100b63d12 100644 --- a/src/gui/src/WebClient.h +++ b/src/gui/src/WebClient.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si, Std. + * 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 diff --git a/src/gui/src/ZeroconfBrowser.cpp b/src/gui/src/ZeroconfBrowser.cpp index 3b7df7301..e3a71afec 100644 --- a/src/gui/src/ZeroconfBrowser.cpp +++ b/src/gui/src/ZeroconfBrowser.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ZeroconfBrowser.h b/src/gui/src/ZeroconfBrowser.h index 53b66aefb..d08562832 100644 --- a/src/gui/src/ZeroconfBrowser.h +++ b/src/gui/src/ZeroconfBrowser.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ZeroconfRecord.h b/src/gui/src/ZeroconfRecord.h index 5a5281cc0..2540435a1 100644 --- a/src/gui/src/ZeroconfRecord.h +++ b/src/gui/src/ZeroconfRecord.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ZeroconfRegister.cpp b/src/gui/src/ZeroconfRegister.cpp index f1bd594dc..5b4ae34a8 100644 --- a/src/gui/src/ZeroconfRegister.cpp +++ b/src/gui/src/ZeroconfRegister.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ZeroconfRegister.h b/src/gui/src/ZeroconfRegister.h index 20c963e11..0eba89a16 100644 --- a/src/gui/src/ZeroconfRegister.h +++ b/src/gui/src/ZeroconfRegister.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ZeroconfServer.cpp b/src/gui/src/ZeroconfServer.cpp index daf1c012b..982b597a9 100644 --- a/src/gui/src/ZeroconfServer.cpp +++ b/src/gui/src/ZeroconfServer.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ZeroconfServer.h b/src/gui/src/ZeroconfServer.h index 412d8c6ab..e490b3a82 100644 --- a/src/gui/src/ZeroconfServer.h +++ b/src/gui/src/ZeroconfServer.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ZeroconfService.cpp b/src/gui/src/ZeroconfService.cpp index 165912d33..e48c2aa85 100644 --- a/src/gui/src/ZeroconfService.cpp +++ b/src/gui/src/ZeroconfService.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ZeroconfService.h b/src/gui/src/ZeroconfService.h index ae206c8a3..8f9aa6db0 100644 --- a/src/gui/src/ZeroconfService.h +++ b/src/gui/src/ZeroconfService.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ZeroconfThread.cpp b/src/gui/src/ZeroconfThread.cpp index 1965335b3..e2b7ac289 100644 --- a/src/gui/src/ZeroconfThread.cpp +++ b/src/gui/src/ZeroconfThread.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/ZeroconfThread.h b/src/gui/src/ZeroconfThread.h index 7b328a13f..ef28498b5 100644 --- a/src/gui/src/ZeroconfThread.h +++ b/src/gui/src/ZeroconfThread.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index 1aaddadc7..0ea2cbfee 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * 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 diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 37126429a..8eba5dffa 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/Arch.cpp b/src/lib/arch/Arch.cpp index 32cc91342..0c38d9562 100644 --- a/src/lib/arch/Arch.cpp +++ b/src/lib/arch/Arch.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/Arch.h b/src/lib/arch/Arch.h index 150539bd5..3c5a23ff2 100644 --- a/src/lib/arch/Arch.h +++ b/src/lib/arch/Arch.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/ArchConsoleStd.cpp b/src/lib/arch/ArchConsoleStd.cpp index 2f199fd6f..615fc7f17 100644 --- a/src/lib/arch/ArchConsoleStd.cpp +++ b/src/lib/arch/ArchConsoleStd.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/ArchConsoleStd.h b/src/lib/arch/ArchConsoleStd.h index 9d914e649..3dd5259a6 100644 --- a/src/lib/arch/ArchConsoleStd.h +++ b/src/lib/arch/ArchConsoleStd.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/ArchDaemonNone.cpp b/src/lib/arch/ArchDaemonNone.cpp index 60d0234fa..234d6d02d 100644 --- a/src/lib/arch/ArchDaemonNone.cpp +++ b/src/lib/arch/ArchDaemonNone.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/ArchDaemonNone.h b/src/lib/arch/ArchDaemonNone.h index 77d706735..aee22fb5d 100644 --- a/src/lib/arch/ArchDaemonNone.h +++ b/src/lib/arch/ArchDaemonNone.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/CMakeLists.txt b/src/lib/arch/CMakeLists.txt index 52a23fc3f..515457183 100644 --- a/src/lib/arch/CMakeLists.txt +++ b/src/lib/arch/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchConsole.h b/src/lib/arch/IArchConsole.h index b4073b78e..d1acd1926 100644 --- a/src/lib/arch/IArchConsole.h +++ b/src/lib/arch/IArchConsole.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchDaemon.h b/src/lib/arch/IArchDaemon.h index 9977db9b6..4ee1fc0d1 100644 --- a/src/lib/arch/IArchDaemon.h +++ b/src/lib/arch/IArchDaemon.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchFile.h b/src/lib/arch/IArchFile.h index a479a2c02..bcbba6e0d 100644 --- a/src/lib/arch/IArchFile.h +++ b/src/lib/arch/IArchFile.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchLog.h b/src/lib/arch/IArchLog.h index c2263f4ad..9f24b6688 100644 --- a/src/lib/arch/IArchLog.h +++ b/src/lib/arch/IArchLog.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchMultithread.h b/src/lib/arch/IArchMultithread.h index 03da73d47..f4f00a916 100644 --- a/src/lib/arch/IArchMultithread.h +++ b/src/lib/arch/IArchMultithread.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchNetwork.h b/src/lib/arch/IArchNetwork.h index 6c752b622..fa7e65ab3 100644 --- a/src/lib/arch/IArchNetwork.h +++ b/src/lib/arch/IArchNetwork.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchPlugin.h b/src/lib/arch/IArchPlugin.h index f6ae46371..e91ed65ff 100644 --- a/src/lib/arch/IArchPlugin.h +++ b/src/lib/arch/IArchPlugin.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchSleep.h b/src/lib/arch/IArchSleep.h index 3c6795b88..4b63e8f31 100644 --- a/src/lib/arch/IArchSleep.h +++ b/src/lib/arch/IArchSleep.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchString.cpp b/src/lib/arch/IArchString.cpp index e00dff373..ce0242ec3 100644 --- a/src/lib/arch/IArchString.cpp +++ b/src/lib/arch/IArchString.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchString.h b/src/lib/arch/IArchString.h index 9d04f4a84..074ec02bd 100644 --- a/src/lib/arch/IArchString.h +++ b/src/lib/arch/IArchString.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchSystem.h b/src/lib/arch/IArchSystem.h index 8b508ac59..0a05b6e65 100644 --- a/src/lib/arch/IArchSystem.h +++ b/src/lib/arch/IArchSystem.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchTaskBar.h b/src/lib/arch/IArchTaskBar.h index 4c2879969..829123802 100644 --- a/src/lib/arch/IArchTaskBar.h +++ b/src/lib/arch/IArchTaskBar.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchTaskBarReceiver.h b/src/lib/arch/IArchTaskBarReceiver.h index d3911e782..2da51fa9b 100644 --- a/src/lib/arch/IArchTaskBarReceiver.h +++ b/src/lib/arch/IArchTaskBarReceiver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/IArchTime.h b/src/lib/arch/IArchTime.h index 98304cc06..2ed170af6 100644 --- a/src/lib/arch/IArchTime.h +++ b/src/lib/arch/IArchTime.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/XArch.h b/src/lib/arch/XArch.h index 55148ac28..ef01affb0 100644 --- a/src/lib/arch/XArch.h +++ b/src/lib/arch/XArch.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/multibyte.h b/src/lib/arch/multibyte.h index f87c1e960..88f421b93 100644 --- a/src/lib/arch/multibyte.h +++ b/src/lib/arch/multibyte.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchConsoleUnix.cpp b/src/lib/arch/unix/ArchConsoleUnix.cpp index f7f22ea8f..b5e20223e 100644 --- a/src/lib/arch/unix/ArchConsoleUnix.cpp +++ b/src/lib/arch/unix/ArchConsoleUnix.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchConsoleUnix.h b/src/lib/arch/unix/ArchConsoleUnix.h index 122ec17f5..fb8683015 100644 --- a/src/lib/arch/unix/ArchConsoleUnix.h +++ b/src/lib/arch/unix/ArchConsoleUnix.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchDaemonUnix.cpp b/src/lib/arch/unix/ArchDaemonUnix.cpp index e3ec87702..91933ab4e 100644 --- a/src/lib/arch/unix/ArchDaemonUnix.cpp +++ b/src/lib/arch/unix/ArchDaemonUnix.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchDaemonUnix.h b/src/lib/arch/unix/ArchDaemonUnix.h index 07053fdd9..f315d9233 100644 --- a/src/lib/arch/unix/ArchDaemonUnix.h +++ b/src/lib/arch/unix/ArchDaemonUnix.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchFileUnix.cpp b/src/lib/arch/unix/ArchFileUnix.cpp index 71e9b5df6..9dcc0b078 100644 --- a/src/lib/arch/unix/ArchFileUnix.cpp +++ b/src/lib/arch/unix/ArchFileUnix.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchFileUnix.h b/src/lib/arch/unix/ArchFileUnix.h index f6a280283..cbf78668d 100644 --- a/src/lib/arch/unix/ArchFileUnix.h +++ b/src/lib/arch/unix/ArchFileUnix.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchInternetUnix.cpp b/src/lib/arch/unix/ArchInternetUnix.cpp index 057882292..596000c18 100644 --- a/src/lib/arch/unix/ArchInternetUnix.cpp +++ b/src/lib/arch/unix/ArchInternetUnix.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/lib/arch/unix/ArchInternetUnix.h b/src/lib/arch/unix/ArchInternetUnix.h index 6109de3b1..13581fcc9 100644 --- a/src/lib/arch/unix/ArchInternetUnix.h +++ b/src/lib/arch/unix/ArchInternetUnix.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/lib/arch/unix/ArchLogUnix.cpp b/src/lib/arch/unix/ArchLogUnix.cpp index a290463f0..f5e116ee7 100644 --- a/src/lib/arch/unix/ArchLogUnix.cpp +++ b/src/lib/arch/unix/ArchLogUnix.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchLogUnix.h b/src/lib/arch/unix/ArchLogUnix.h index 78ad57eff..d5fe1d8ef 100644 --- a/src/lib/arch/unix/ArchLogUnix.h +++ b/src/lib/arch/unix/ArchLogUnix.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchMultithreadPosix.cpp b/src/lib/arch/unix/ArchMultithreadPosix.cpp index 72f078a67..807894e23 100644 --- a/src/lib/arch/unix/ArchMultithreadPosix.cpp +++ b/src/lib/arch/unix/ArchMultithreadPosix.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchMultithreadPosix.h b/src/lib/arch/unix/ArchMultithreadPosix.h index 9633f819a..c23a0ab44 100644 --- a/src/lib/arch/unix/ArchMultithreadPosix.h +++ b/src/lib/arch/unix/ArchMultithreadPosix.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchNetworkBSD.cpp b/src/lib/arch/unix/ArchNetworkBSD.cpp index 69793de3d..c2d692fcf 100644 --- a/src/lib/arch/unix/ArchNetworkBSD.cpp +++ b/src/lib/arch/unix/ArchNetworkBSD.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchNetworkBSD.h b/src/lib/arch/unix/ArchNetworkBSD.h index 25577d836..7aab1e44f 100644 --- a/src/lib/arch/unix/ArchNetworkBSD.h +++ b/src/lib/arch/unix/ArchNetworkBSD.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchPluginUnix.cpp b/src/lib/arch/unix/ArchPluginUnix.cpp index 46f6a4765..edf53d17b 100644 --- a/src/lib/arch/unix/ArchPluginUnix.cpp +++ b/src/lib/arch/unix/ArchPluginUnix.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchPluginUnix.h b/src/lib/arch/unix/ArchPluginUnix.h index eeb14691b..84ce185ed 100644 --- a/src/lib/arch/unix/ArchPluginUnix.h +++ b/src/lib/arch/unix/ArchPluginUnix.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchSleepUnix.cpp b/src/lib/arch/unix/ArchSleepUnix.cpp index d248c4c3b..a271b96d4 100644 --- a/src/lib/arch/unix/ArchSleepUnix.cpp +++ b/src/lib/arch/unix/ArchSleepUnix.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchSleepUnix.h b/src/lib/arch/unix/ArchSleepUnix.h index 2afbe66f3..6be92e939 100644 --- a/src/lib/arch/unix/ArchSleepUnix.h +++ b/src/lib/arch/unix/ArchSleepUnix.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchStringUnix.cpp b/src/lib/arch/unix/ArchStringUnix.cpp index ee985ced3..4eecdde2b 100644 --- a/src/lib/arch/unix/ArchStringUnix.cpp +++ b/src/lib/arch/unix/ArchStringUnix.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchStringUnix.h b/src/lib/arch/unix/ArchStringUnix.h index 9a93178a6..bea1153b7 100644 --- a/src/lib/arch/unix/ArchStringUnix.h +++ b/src/lib/arch/unix/ArchStringUnix.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchSystemUnix.cpp b/src/lib/arch/unix/ArchSystemUnix.cpp index 07cbf6c40..3eb60cfa8 100644 --- a/src/lib/arch/unix/ArchSystemUnix.cpp +++ b/src/lib/arch/unix/ArchSystemUnix.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchSystemUnix.h b/src/lib/arch/unix/ArchSystemUnix.h index acacaa985..622f9ee65 100644 --- a/src/lib/arch/unix/ArchSystemUnix.h +++ b/src/lib/arch/unix/ArchSystemUnix.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchTaskBarXWindows.cpp b/src/lib/arch/unix/ArchTaskBarXWindows.cpp index 8ec0506e6..9b05d2082 100644 --- a/src/lib/arch/unix/ArchTaskBarXWindows.cpp +++ b/src/lib/arch/unix/ArchTaskBarXWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchTaskBarXWindows.h b/src/lib/arch/unix/ArchTaskBarXWindows.h index 7d6e3177c..c772c3794 100644 --- a/src/lib/arch/unix/ArchTaskBarXWindows.h +++ b/src/lib/arch/unix/ArchTaskBarXWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchTimeUnix.cpp b/src/lib/arch/unix/ArchTimeUnix.cpp index 332197f4b..74eb3d3fa 100644 --- a/src/lib/arch/unix/ArchTimeUnix.cpp +++ b/src/lib/arch/unix/ArchTimeUnix.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/ArchTimeUnix.h b/src/lib/arch/unix/ArchTimeUnix.h index 5f9ffbd83..f7fa82fce 100644 --- a/src/lib/arch/unix/ArchTimeUnix.h +++ b/src/lib/arch/unix/ArchTimeUnix.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/XArchUnix.cpp b/src/lib/arch/unix/XArchUnix.cpp index 4c9ebb88a..fee9e016c 100644 --- a/src/lib/arch/unix/XArchUnix.cpp +++ b/src/lib/arch/unix/XArchUnix.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/unix/XArchUnix.h b/src/lib/arch/unix/XArchUnix.h index d7edb130e..5bdc1056a 100644 --- a/src/lib/arch/unix/XArchUnix.h +++ b/src/lib/arch/unix/XArchUnix.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/vsnprintf.h b/src/lib/arch/vsnprintf.h index cee837e46..a3b14d968 100644 --- a/src/lib/arch/vsnprintf.h +++ b/src/lib/arch/vsnprintf.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchConsoleWindows.cpp b/src/lib/arch/win32/ArchConsoleWindows.cpp index aeaaa107c..6a1a5ff6b 100644 --- a/src/lib/arch/win32/ArchConsoleWindows.cpp +++ b/src/lib/arch/win32/ArchConsoleWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchConsoleWindows.h b/src/lib/arch/win32/ArchConsoleWindows.h index 903d7fc89..ca264c89d 100644 --- a/src/lib/arch/win32/ArchConsoleWindows.h +++ b/src/lib/arch/win32/ArchConsoleWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchDaemonWindows.cpp b/src/lib/arch/win32/ArchDaemonWindows.cpp index 7f45c7d3b..ea6d6ccaf 100644 --- a/src/lib/arch/win32/ArchDaemonWindows.cpp +++ b/src/lib/arch/win32/ArchDaemonWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchDaemonWindows.h b/src/lib/arch/win32/ArchDaemonWindows.h index e3ee11b4d..2079c8507 100644 --- a/src/lib/arch/win32/ArchDaemonWindows.h +++ b/src/lib/arch/win32/ArchDaemonWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchFileWindows.cpp b/src/lib/arch/win32/ArchFileWindows.cpp index a87a2fe01..373df72c8 100644 --- a/src/lib/arch/win32/ArchFileWindows.cpp +++ b/src/lib/arch/win32/ArchFileWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchFileWindows.h b/src/lib/arch/win32/ArchFileWindows.h index ddca7d2af..cdb8e4a23 100644 --- a/src/lib/arch/win32/ArchFileWindows.h +++ b/src/lib/arch/win32/ArchFileWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchInternetWindows.cpp b/src/lib/arch/win32/ArchInternetWindows.cpp index ec76a570a..ca649117c 100644 --- a/src/lib/arch/win32/ArchInternetWindows.cpp +++ b/src/lib/arch/win32/ArchInternetWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/lib/arch/win32/ArchInternetWindows.h b/src/lib/arch/win32/ArchInternetWindows.h index 817a8f54b..5f14b8752 100644 --- a/src/lib/arch/win32/ArchInternetWindows.h +++ b/src/lib/arch/win32/ArchInternetWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/lib/arch/win32/ArchLogWindows.cpp b/src/lib/arch/win32/ArchLogWindows.cpp index a56202b60..f6637144d 100644 --- a/src/lib/arch/win32/ArchLogWindows.cpp +++ b/src/lib/arch/win32/ArchLogWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchLogWindows.h b/src/lib/arch/win32/ArchLogWindows.h index 360a5ad3e..3104492b9 100644 --- a/src/lib/arch/win32/ArchLogWindows.h +++ b/src/lib/arch/win32/ArchLogWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchMiscWindows.cpp b/src/lib/arch/win32/ArchMiscWindows.cpp index 556ffe04b..d70861d26 100644 --- a/src/lib/arch/win32/ArchMiscWindows.cpp +++ b/src/lib/arch/win32/ArchMiscWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchMiscWindows.h b/src/lib/arch/win32/ArchMiscWindows.h index 21fc823a6..94b379ad2 100644 --- a/src/lib/arch/win32/ArchMiscWindows.h +++ b/src/lib/arch/win32/ArchMiscWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchMultithreadWindows.cpp b/src/lib/arch/win32/ArchMultithreadWindows.cpp index 288471fa0..0768492de 100644 --- a/src/lib/arch/win32/ArchMultithreadWindows.cpp +++ b/src/lib/arch/win32/ArchMultithreadWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchMultithreadWindows.h b/src/lib/arch/win32/ArchMultithreadWindows.h index ae67f11df..bc98019c9 100644 --- a/src/lib/arch/win32/ArchMultithreadWindows.h +++ b/src/lib/arch/win32/ArchMultithreadWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchNetworkWinsock.cpp b/src/lib/arch/win32/ArchNetworkWinsock.cpp index db3dbe684..712e1bdc5 100644 --- a/src/lib/arch/win32/ArchNetworkWinsock.cpp +++ b/src/lib/arch/win32/ArchNetworkWinsock.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchNetworkWinsock.h b/src/lib/arch/win32/ArchNetworkWinsock.h index 3cc6c9a40..f2c2aee03 100644 --- a/src/lib/arch/win32/ArchNetworkWinsock.h +++ b/src/lib/arch/win32/ArchNetworkWinsock.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchPluginWindows.cpp b/src/lib/arch/win32/ArchPluginWindows.cpp index 7d26520f0..7e498cb2a 100644 --- a/src/lib/arch/win32/ArchPluginWindows.cpp +++ b/src/lib/arch/win32/ArchPluginWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchPluginWindows.h b/src/lib/arch/win32/ArchPluginWindows.h index 86f5c8123..dc61ead98 100644 --- a/src/lib/arch/win32/ArchPluginWindows.h +++ b/src/lib/arch/win32/ArchPluginWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchSleepWindows.cpp b/src/lib/arch/win32/ArchSleepWindows.cpp index 1990141be..6b1c9a716 100644 --- a/src/lib/arch/win32/ArchSleepWindows.cpp +++ b/src/lib/arch/win32/ArchSleepWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchSleepWindows.h b/src/lib/arch/win32/ArchSleepWindows.h index 0cbfb299c..957e06efe 100644 --- a/src/lib/arch/win32/ArchSleepWindows.h +++ b/src/lib/arch/win32/ArchSleepWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchStringWindows.cpp b/src/lib/arch/win32/ArchStringWindows.cpp index eff39e86c..a57360700 100644 --- a/src/lib/arch/win32/ArchStringWindows.cpp +++ b/src/lib/arch/win32/ArchStringWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchStringWindows.h b/src/lib/arch/win32/ArchStringWindows.h index 33410d768..db943b952 100644 --- a/src/lib/arch/win32/ArchStringWindows.h +++ b/src/lib/arch/win32/ArchStringWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchSystemWindows.cpp b/src/lib/arch/win32/ArchSystemWindows.cpp index 5540e40c2..132691fff 100644 --- a/src/lib/arch/win32/ArchSystemWindows.cpp +++ b/src/lib/arch/win32/ArchSystemWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchSystemWindows.h b/src/lib/arch/win32/ArchSystemWindows.h index c747f28f2..ed6fcd52f 100644 --- a/src/lib/arch/win32/ArchSystemWindows.h +++ b/src/lib/arch/win32/ArchSystemWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchTaskBarWindows.cpp b/src/lib/arch/win32/ArchTaskBarWindows.cpp index 33e21a2b1..e4154ab25 100644 --- a/src/lib/arch/win32/ArchTaskBarWindows.cpp +++ b/src/lib/arch/win32/ArchTaskBarWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchTaskBarWindows.h b/src/lib/arch/win32/ArchTaskBarWindows.h index 5e26c5abc..b6de6eb24 100644 --- a/src/lib/arch/win32/ArchTaskBarWindows.h +++ b/src/lib/arch/win32/ArchTaskBarWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchTimeWindows.cpp b/src/lib/arch/win32/ArchTimeWindows.cpp index 879a7fa08..ef3f9ef83 100644 --- a/src/lib/arch/win32/ArchTimeWindows.cpp +++ b/src/lib/arch/win32/ArchTimeWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/ArchTimeWindows.h b/src/lib/arch/win32/ArchTimeWindows.h index 3ade86532..38fe73354 100644 --- a/src/lib/arch/win32/ArchTimeWindows.h +++ b/src/lib/arch/win32/ArchTimeWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/XArchWindows.cpp b/src/lib/arch/win32/XArchWindows.cpp index e60117064..6ef824701 100644 --- a/src/lib/arch/win32/XArchWindows.cpp +++ b/src/lib/arch/win32/XArchWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/arch/win32/XArchWindows.h b/src/lib/arch/win32/XArchWindows.h index b5af3ab35..bc7e3a397 100644 --- a/src/lib/arch/win32/XArchWindows.h +++ b/src/lib/arch/win32/XArchWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/CMakeLists.txt b/src/lib/base/CMakeLists.txt index c579a54cb..b17cc3dd7 100644 --- a/src/lib/base/CMakeLists.txt +++ b/src/lib/base/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/base/ELevel.h b/src/lib/base/ELevel.h index c489dffd4..f6d304e4d 100644 --- a/src/lib/base/ELevel.h +++ b/src/lib/base/ELevel.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/Event.cpp b/src/lib/base/Event.cpp index 76ae72c2b..89b6174bb 100644 --- a/src/lib/base/Event.cpp +++ b/src/lib/base/Event.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/Event.h b/src/lib/base/Event.h index 5c6d8c677..652aab62a 100644 --- a/src/lib/base/Event.h +++ b/src/lib/base/Event.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/EventQueue.cpp b/src/lib/base/EventQueue.cpp index bc3b9bc60..60a163177 100644 --- a/src/lib/base/EventQueue.cpp +++ b/src/lib/base/EventQueue.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/EventQueue.h b/src/lib/base/EventQueue.h index f741120e5..a0b155393 100644 --- a/src/lib/base/EventQueue.h +++ b/src/lib/base/EventQueue.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/EventTypes.cpp b/src/lib/base/EventTypes.cpp index afa51900b..a25bc2920 100644 --- a/src/lib/base/EventTypes.cpp +++ b/src/lib/base/EventTypes.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/base/EventTypes.h b/src/lib/base/EventTypes.h index b10fb6b8b..d22bc7296 100644 --- a/src/lib/base/EventTypes.h +++ b/src/lib/base/EventTypes.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/base/FunctionEventJob.cpp b/src/lib/base/FunctionEventJob.cpp index a73b233ec..dbd461c13 100644 --- a/src/lib/base/FunctionEventJob.cpp +++ b/src/lib/base/FunctionEventJob.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/FunctionEventJob.h b/src/lib/base/FunctionEventJob.h index df4376e43..5e7bf650a 100644 --- a/src/lib/base/FunctionEventJob.h +++ b/src/lib/base/FunctionEventJob.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/FunctionJob.cpp b/src/lib/base/FunctionJob.cpp index ad70de7f8..74b17a06d 100644 --- a/src/lib/base/FunctionJob.cpp +++ b/src/lib/base/FunctionJob.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/FunctionJob.h b/src/lib/base/FunctionJob.h index 10232aef2..2f7c3091a 100644 --- a/src/lib/base/FunctionJob.h +++ b/src/lib/base/FunctionJob.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/IEventJob.h b/src/lib/base/IEventJob.h index ebaf1c4bd..e668228a0 100644 --- a/src/lib/base/IEventJob.h +++ b/src/lib/base/IEventJob.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/IEventQueue.h b/src/lib/base/IEventQueue.h index fa04434c1..9d838de7f 100644 --- a/src/lib/base/IEventQueue.h +++ b/src/lib/base/IEventQueue.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/IEventQueueBuffer.h b/src/lib/base/IEventQueueBuffer.h index 85479a20b..5b1fc7e78 100644 --- a/src/lib/base/IEventQueueBuffer.h +++ b/src/lib/base/IEventQueueBuffer.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/IJob.h b/src/lib/base/IJob.h index 2b07eafb5..5e12ae076 100644 --- a/src/lib/base/IJob.h +++ b/src/lib/base/IJob.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/ILogOutputter.h b/src/lib/base/ILogOutputter.h index 60fd2b9a0..3719976c1 100644 --- a/src/lib/base/ILogOutputter.h +++ b/src/lib/base/ILogOutputter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/Log.cpp b/src/lib/base/Log.cpp index 42389bda5..67f7d8193 100644 --- a/src/lib/base/Log.cpp +++ b/src/lib/base/Log.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/Log.h b/src/lib/base/Log.h index ab36ce6ac..87ad1fdc3 100644 --- a/src/lib/base/Log.h +++ b/src/lib/base/Log.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/PriorityQueue.h b/src/lib/base/PriorityQueue.h index 5084cebca..239a872e2 100644 --- a/src/lib/base/PriorityQueue.h +++ b/src/lib/base/PriorityQueue.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/SimpleEventQueueBuffer.cpp b/src/lib/base/SimpleEventQueueBuffer.cpp index 95b4cbcb1..dc3cf2664 100644 --- a/src/lib/base/SimpleEventQueueBuffer.cpp +++ b/src/lib/base/SimpleEventQueueBuffer.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/SimpleEventQueueBuffer.h b/src/lib/base/SimpleEventQueueBuffer.h index 203370d4b..dbe1e19be 100644 --- a/src/lib/base/SimpleEventQueueBuffer.h +++ b/src/lib/base/SimpleEventQueueBuffer.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/Stopwatch.cpp b/src/lib/base/Stopwatch.cpp index 972f1300f..adaa74545 100644 --- a/src/lib/base/Stopwatch.cpp +++ b/src/lib/base/Stopwatch.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/Stopwatch.h b/src/lib/base/Stopwatch.h index 3d8cdad0b..71be88857 100644 --- a/src/lib/base/Stopwatch.h +++ b/src/lib/base/Stopwatch.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp index 689295b6c..ab5124db8 100644 --- a/src/lib/base/String.cpp +++ b/src/lib/base/String.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/lib/base/String.h b/src/lib/base/String.h index df4307a62..1b22df9b6 100644 --- a/src/lib/base/String.h +++ b/src/lib/base/String.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/TMethodEventJob.h b/src/lib/base/TMethodEventJob.h index 4ae5af185..3062bcb5b 100644 --- a/src/lib/base/TMethodEventJob.h +++ b/src/lib/base/TMethodEventJob.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/TMethodJob.h b/src/lib/base/TMethodJob.h index 6ff82aba8..e837e1aef 100644 --- a/src/lib/base/TMethodJob.h +++ b/src/lib/base/TMethodJob.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/Unicode.cpp b/src/lib/base/Unicode.cpp index 93e0358ec..ad7a266db 100644 --- a/src/lib/base/Unicode.cpp +++ b/src/lib/base/Unicode.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/Unicode.h b/src/lib/base/Unicode.h index df9552417..5df7016a7 100644 --- a/src/lib/base/Unicode.h +++ b/src/lib/base/Unicode.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/XBase.cpp b/src/lib/base/XBase.cpp index a3b7ee852..a20114dea 100644 --- a/src/lib/base/XBase.cpp +++ b/src/lib/base/XBase.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/XBase.h b/src/lib/base/XBase.h index a0840eb88..ffa249cf6 100644 --- a/src/lib/base/XBase.h +++ b/src/lib/base/XBase.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/log_outputters.cpp b/src/lib/base/log_outputters.cpp index 962c9d683..0dcd608c0 100644 --- a/src/lib/base/log_outputters.cpp +++ b/src/lib/base/log_outputters.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/base/log_outputters.h b/src/lib/base/log_outputters.h index 31ce58cb6..b5f6a456d 100644 --- a/src/lib/base/log_outputters.h +++ b/src/lib/base/log_outputters.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/client/CMakeLists.txt b/src/lib/client/CMakeLists.txt index 713492858..29f3759f9 100644 --- a/src/lib/client/CMakeLists.txt +++ b/src/lib/client/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 500c7d530..3894cc36c 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index 8f2f41305..27eafde57 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index 16459b439..773cdc162 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/client/ServerProxy.h b/src/lib/client/ServerProxy.h index 144fd000c..d90b9735b 100644 --- a/src/lib/client/ServerProxy.h +++ b/src/lib/client/ServerProxy.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/CMakeLists.txt b/src/lib/common/CMakeLists.txt index b092e360e..eed067278 100644 --- a/src/lib/common/CMakeLists.txt +++ b/src/lib/common/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/common/IInterface.h b/src/lib/common/IInterface.h index 3b9332754..89cd58ad8 100644 --- a/src/lib/common/IInterface.h +++ b/src/lib/common/IInterface.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/MacOSXPrecomp.h b/src/lib/common/MacOSXPrecomp.h index fe16821ae..46688bf51 100644 --- a/src/lib/common/MacOSXPrecomp.h +++ b/src/lib/common/MacOSXPrecomp.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/PluginVersion.cpp b/src/lib/common/PluginVersion.cpp index 25ebaf83c..4ccacead1 100644 --- a/src/lib/common/PluginVersion.cpp +++ b/src/lib/common/PluginVersion.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/lib/common/PluginVersion.h b/src/lib/common/PluginVersion.h index c0b2d0d7d..b2f6bae2c 100644 --- a/src/lib/common/PluginVersion.h +++ b/src/lib/common/PluginVersion.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/lib/common/Version.cpp b/src/lib/common/Version.cpp index 18724fc61..2781e7e4e 100644 --- a/src/lib/common/Version.cpp +++ b/src/lib/common/Version.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or @@ -19,7 +19,7 @@ #include "common/Version.h" const char* kApplication = "Synergy"; -const char* kCopyright = "Copyright (C) 2012-2014 Synergy Si Ltd.\n" +const char* kCopyright = "Copyright (C) 2012-2016 Symless Ltd.\n" "Copyright (C) 2008-2014 Nick Bolton\n" "Copyright (C) 2002-2014 Chris Schoeneman"; const char* kContact = "Email: nick@symless.com"; diff --git a/src/lib/common/Version.h b/src/lib/common/Version.h index d491856bc..80eaa1e70 100644 --- a/src/lib/common/Version.h +++ b/src/lib/common/Version.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/basic_types.h b/src/lib/common/basic_types.h index c5a903f99..7e9437e24 100644 --- a/src/lib/common/basic_types.h +++ b/src/lib/common/basic_types.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/common.h b/src/lib/common/common.h index bdfb5b3b3..6e8709510 100644 --- a/src/lib/common/common.h +++ b/src/lib/common/common.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/stdbitset.h b/src/lib/common/stdbitset.h index f2764646b..103a48a35 100644 --- a/src/lib/common/stdbitset.h +++ b/src/lib/common/stdbitset.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/stddeque.h b/src/lib/common/stddeque.h index 1cc7747de..1fabe6828 100644 --- a/src/lib/common/stddeque.h +++ b/src/lib/common/stddeque.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/stdexcept.h b/src/lib/common/stdexcept.h index 10003a1ba..518d46769 100644 --- a/src/lib/common/stdexcept.h +++ b/src/lib/common/stdexcept.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/lib/common/stdfstream.h b/src/lib/common/stdfstream.h index f96be64b1..459c1f4d4 100644 --- a/src/lib/common/stdfstream.h +++ b/src/lib/common/stdfstream.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/stdistream.h b/src/lib/common/stdistream.h index a55ad5137..86f29f77c 100644 --- a/src/lib/common/stdistream.h +++ b/src/lib/common/stdistream.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/stdlist.h b/src/lib/common/stdlist.h index e6def26f9..9e0b32746 100644 --- a/src/lib/common/stdlist.h +++ b/src/lib/common/stdlist.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/stdmap.h b/src/lib/common/stdmap.h index 761c9aadd..1b4c5a462 100644 --- a/src/lib/common/stdmap.h +++ b/src/lib/common/stdmap.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/stdostream.h b/src/lib/common/stdostream.h index 1fe4315a5..d51cafbdc 100644 --- a/src/lib/common/stdostream.h +++ b/src/lib/common/stdostream.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/stdpost.h b/src/lib/common/stdpost.h index 7450bf21e..80ea6c006 100644 --- a/src/lib/common/stdpost.h +++ b/src/lib/common/stdpost.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/stdpre.h b/src/lib/common/stdpre.h index 55c0ce482..fff089b99 100644 --- a/src/lib/common/stdpre.h +++ b/src/lib/common/stdpre.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/stdset.h b/src/lib/common/stdset.h index 3090fd39a..2bf2b6e2b 100644 --- a/src/lib/common/stdset.h +++ b/src/lib/common/stdset.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/stdsstream.h b/src/lib/common/stdsstream.h index 73b355b4a..7f17bed8f 100644 --- a/src/lib/common/stdsstream.h +++ b/src/lib/common/stdsstream.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or @@ -27,7 +27,7 @@ // by Magnus Fromreide of the sstream in g++ 3.0. /* This is part of libio/iostream, providing -*- C++ -*- input/output. -Copyright (C) 2012 Synergy Si Ltd. +Copyright (C) 2012-2016 Symless Ltd. Copyright (C) 2000 Free Software Foundation This file is part of the GNU IO Library. This library is free diff --git a/src/lib/common/stdstring.h b/src/lib/common/stdstring.h index 7399d3416..20ce67ef0 100644 --- a/src/lib/common/stdstring.h +++ b/src/lib/common/stdstring.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/common/stdvector.h b/src/lib/common/stdvector.h index 52b1150d3..75a332983 100644 --- a/src/lib/common/stdvector.h +++ b/src/lib/common/stdvector.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/io/CMakeLists.txt b/src/lib/io/CMakeLists.txt index d9fafa3dd..acee73567 100644 --- a/src/lib/io/CMakeLists.txt +++ b/src/lib/io/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/io/IStream.h b/src/lib/io/IStream.h index 71e768834..3ebd04e4d 100644 --- a/src/lib/io/IStream.h +++ b/src/lib/io/IStream.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/io/StreamBuffer.cpp b/src/lib/io/StreamBuffer.cpp index 1f6625fa0..050adae18 100644 --- a/src/lib/io/StreamBuffer.cpp +++ b/src/lib/io/StreamBuffer.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/io/StreamBuffer.h b/src/lib/io/StreamBuffer.h index 124806197..f7f09b645 100644 --- a/src/lib/io/StreamBuffer.h +++ b/src/lib/io/StreamBuffer.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/io/StreamFilter.cpp b/src/lib/io/StreamFilter.cpp index 5994aab09..eb66b9735 100644 --- a/src/lib/io/StreamFilter.cpp +++ b/src/lib/io/StreamFilter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/io/StreamFilter.h b/src/lib/io/StreamFilter.h index a33b93dff..626409107 100644 --- a/src/lib/io/StreamFilter.h +++ b/src/lib/io/StreamFilter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/io/XIO.cpp b/src/lib/io/XIO.cpp index 4776f005a..d51616c35 100644 --- a/src/lib/io/XIO.cpp +++ b/src/lib/io/XIO.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/io/XIO.h b/src/lib/io/XIO.h index ccc7da8ed..fe7ad2036 100644 --- a/src/lib/io/XIO.h +++ b/src/lib/io/XIO.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/CMakeLists.txt b/src/lib/ipc/CMakeLists.txt index 676f04943..23c26cb06 100644 --- a/src/lib/ipc/CMakeLists.txt +++ b/src/lib/ipc/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/Ipc.cpp b/src/lib/ipc/Ipc.cpp index e5caa1ace..6f8b8547b 100644 --- a/src/lib/ipc/Ipc.cpp +++ b/src/lib/ipc/Ipc.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/Ipc.h b/src/lib/ipc/Ipc.h index 7baf12005..a6fbb5214 100644 --- a/src/lib/ipc/Ipc.h +++ b/src/lib/ipc/Ipc.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/IpcClient.cpp b/src/lib/ipc/IpcClient.cpp index 3ebbea2fd..896df8d56 100644 --- a/src/lib/ipc/IpcClient.cpp +++ b/src/lib/ipc/IpcClient.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/IpcClient.h b/src/lib/ipc/IpcClient.h index 5eb98a747..415176bb7 100644 --- a/src/lib/ipc/IpcClient.h +++ b/src/lib/ipc/IpcClient.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/IpcClientProxy.cpp b/src/lib/ipc/IpcClientProxy.cpp index 4ad993449..8c912a837 100644 --- a/src/lib/ipc/IpcClientProxy.cpp +++ b/src/lib/ipc/IpcClientProxy.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/IpcClientProxy.h b/src/lib/ipc/IpcClientProxy.h index 6bd38bfed..cdbdcc503 100644 --- a/src/lib/ipc/IpcClientProxy.h +++ b/src/lib/ipc/IpcClientProxy.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/IpcLogOutputter.cpp b/src/lib/ipc/IpcLogOutputter.cpp index d102b7476..9cf8e65df 100644 --- a/src/lib/ipc/IpcLogOutputter.cpp +++ b/src/lib/ipc/IpcLogOutputter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/IpcLogOutputter.h b/src/lib/ipc/IpcLogOutputter.h index efe8238a0..ac5ddc65b 100644 --- a/src/lib/ipc/IpcLogOutputter.h +++ b/src/lib/ipc/IpcLogOutputter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/IpcMessage.cpp b/src/lib/ipc/IpcMessage.cpp index 1a1c5c65a..7b1901f03 100644 --- a/src/lib/ipc/IpcMessage.cpp +++ b/src/lib/ipc/IpcMessage.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/IpcMessage.h b/src/lib/ipc/IpcMessage.h index ee6750696..5a8f699ea 100644 --- a/src/lib/ipc/IpcMessage.h +++ b/src/lib/ipc/IpcMessage.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/IpcServer.cpp b/src/lib/ipc/IpcServer.cpp index ce533107d..1104857cc 100644 --- a/src/lib/ipc/IpcServer.cpp +++ b/src/lib/ipc/IpcServer.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/IpcServer.h b/src/lib/ipc/IpcServer.h index 2c442307a..ae88d4400 100644 --- a/src/lib/ipc/IpcServer.h +++ b/src/lib/ipc/IpcServer.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/IpcServerProxy.cpp b/src/lib/ipc/IpcServerProxy.cpp index 787dfe7b3..90e87478c 100644 --- a/src/lib/ipc/IpcServerProxy.cpp +++ b/src/lib/ipc/IpcServerProxy.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/ipc/IpcServerProxy.h b/src/lib/ipc/IpcServerProxy.h index be6ffcb2a..7f83dc710 100644 --- a/src/lib/ipc/IpcServerProxy.h +++ b/src/lib/ipc/IpcServerProxy.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/mt/CMakeLists.txt b/src/lib/mt/CMakeLists.txt index f204cb12d..ebbae5469 100644 --- a/src/lib/mt/CMakeLists.txt +++ b/src/lib/mt/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/mt/CondVar.cpp b/src/lib/mt/CondVar.cpp index 751114102..e7e580ab5 100644 --- a/src/lib/mt/CondVar.cpp +++ b/src/lib/mt/CondVar.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/mt/CondVar.h b/src/lib/mt/CondVar.h index 79997e051..3c7a7f114 100644 --- a/src/lib/mt/CondVar.h +++ b/src/lib/mt/CondVar.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/mt/Lock.cpp b/src/lib/mt/Lock.cpp index 866ba6817..752a1bcf0 100644 --- a/src/lib/mt/Lock.cpp +++ b/src/lib/mt/Lock.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/mt/Lock.h b/src/lib/mt/Lock.h index 425ee58f7..4b8117a01 100644 --- a/src/lib/mt/Lock.h +++ b/src/lib/mt/Lock.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/mt/Mutex.cpp b/src/lib/mt/Mutex.cpp index 78f1ae662..224ace167 100644 --- a/src/lib/mt/Mutex.cpp +++ b/src/lib/mt/Mutex.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/mt/Mutex.h b/src/lib/mt/Mutex.h index 7c940d299..6d385bbf0 100644 --- a/src/lib/mt/Mutex.h +++ b/src/lib/mt/Mutex.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/mt/Thread.cpp b/src/lib/mt/Thread.cpp index e181adf41..0c67e8a99 100644 --- a/src/lib/mt/Thread.cpp +++ b/src/lib/mt/Thread.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/mt/Thread.h b/src/lib/mt/Thread.h index db3d81e1a..dfcdfadac 100644 --- a/src/lib/mt/Thread.h +++ b/src/lib/mt/Thread.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/mt/XMT.cpp b/src/lib/mt/XMT.cpp index ba754a621..bc825afb4 100644 --- a/src/lib/mt/XMT.cpp +++ b/src/lib/mt/XMT.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/mt/XMT.h b/src/lib/mt/XMT.h index f49dbd3f9..16cef278d 100644 --- a/src/lib/mt/XMT.h +++ b/src/lib/mt/XMT.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/mt/XThread.h b/src/lib/mt/XThread.h index a5bc42a7d..f2649f6a2 100644 --- a/src/lib/mt/XThread.h +++ b/src/lib/mt/XThread.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/CMakeLists.txt b/src/lib/net/CMakeLists.txt index 732fd6373..ee50fe364 100644 --- a/src/lib/net/CMakeLists.txt +++ b/src/lib/net/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/net/IDataSocket.cpp b/src/lib/net/IDataSocket.cpp index c64cc813d..8baf1f822 100644 --- a/src/lib/net/IDataSocket.cpp +++ b/src/lib/net/IDataSocket.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/IDataSocket.h b/src/lib/net/IDataSocket.h index 08970657a..1044aa373 100644 --- a/src/lib/net/IDataSocket.h +++ b/src/lib/net/IDataSocket.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/IListenSocket.h b/src/lib/net/IListenSocket.h index edd4b9b8b..905ec831f 100644 --- a/src/lib/net/IListenSocket.h +++ b/src/lib/net/IListenSocket.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/ISocket.h b/src/lib/net/ISocket.h index 72ad67a97..efa19171e 100644 --- a/src/lib/net/ISocket.h +++ b/src/lib/net/ISocket.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/ISocketFactory.h b/src/lib/net/ISocketFactory.h index e0c4d22b5..9820536f4 100644 --- a/src/lib/net/ISocketFactory.h +++ b/src/lib/net/ISocketFactory.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/ISocketMultiplexerJob.h b/src/lib/net/ISocketMultiplexerJob.h index ce007fe19..ba7949aaa 100644 --- a/src/lib/net/ISocketMultiplexerJob.h +++ b/src/lib/net/ISocketMultiplexerJob.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/NetworkAddress.cpp b/src/lib/net/NetworkAddress.cpp index efafe0ea6..da864a4ff 100644 --- a/src/lib/net/NetworkAddress.cpp +++ b/src/lib/net/NetworkAddress.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/NetworkAddress.h b/src/lib/net/NetworkAddress.h index d962b8972..31cbb710d 100644 --- a/src/lib/net/NetworkAddress.h +++ b/src/lib/net/NetworkAddress.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/SocketMultiplexer.cpp b/src/lib/net/SocketMultiplexer.cpp index 495f262ce..9ba352a67 100644 --- a/src/lib/net/SocketMultiplexer.cpp +++ b/src/lib/net/SocketMultiplexer.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/SocketMultiplexer.h b/src/lib/net/SocketMultiplexer.h index 10477691a..bfdc8e615 100644 --- a/src/lib/net/SocketMultiplexer.h +++ b/src/lib/net/SocketMultiplexer.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/TCPListenSocket.cpp b/src/lib/net/TCPListenSocket.cpp index 7114dc1f1..94760d534 100644 --- a/src/lib/net/TCPListenSocket.cpp +++ b/src/lib/net/TCPListenSocket.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/TCPListenSocket.h b/src/lib/net/TCPListenSocket.h index d26df9be5..cf4469a0d 100644 --- a/src/lib/net/TCPListenSocket.h +++ b/src/lib/net/TCPListenSocket.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/TCPSocket.cpp b/src/lib/net/TCPSocket.cpp index ab8d189b4..326a63acc 100644 --- a/src/lib/net/TCPSocket.cpp +++ b/src/lib/net/TCPSocket.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/TCPSocket.h b/src/lib/net/TCPSocket.h index b4c833ba3..3dca4cada 100644 --- a/src/lib/net/TCPSocket.h +++ b/src/lib/net/TCPSocket.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/TCPSocketFactory.cpp b/src/lib/net/TCPSocketFactory.cpp index 0f92ffb9a..9f8dc0290 100644 --- a/src/lib/net/TCPSocketFactory.cpp +++ b/src/lib/net/TCPSocketFactory.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/TCPSocketFactory.h b/src/lib/net/TCPSocketFactory.h index 0408b6472..083f6ff2a 100644 --- a/src/lib/net/TCPSocketFactory.h +++ b/src/lib/net/TCPSocketFactory.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/TSocketMultiplexerMethodJob.h b/src/lib/net/TSocketMultiplexerMethodJob.h index 01d43eceb..f3d7f095d 100644 --- a/src/lib/net/TSocketMultiplexerMethodJob.h +++ b/src/lib/net/TSocketMultiplexerMethodJob.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/XSocket.cpp b/src/lib/net/XSocket.cpp index 5750cbafe..4b52c8a91 100644 --- a/src/lib/net/XSocket.cpp +++ b/src/lib/net/XSocket.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/net/XSocket.h b/src/lib/net/XSocket.h index 1b5b242e5..900f40777 100644 --- a/src/lib/net/XSocket.h +++ b/src/lib/net/XSocket.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index 00e5d1bcb..6c272c214 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/IMSWindowsClipboardFacade.h b/src/lib/platform/IMSWindowsClipboardFacade.h index e60c44afc..60dcaf7c3 100644 --- a/src/lib/platform/IMSWindowsClipboardFacade.h +++ b/src/lib/platform/IMSWindowsClipboardFacade.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboard.cpp b/src/lib/platform/MSWindowsClipboard.cpp index 359e43e52..4264ae4b4 100644 --- a/src/lib/platform/MSWindowsClipboard.cpp +++ b/src/lib/platform/MSWindowsClipboard.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboard.h b/src/lib/platform/MSWindowsClipboard.h index b1a30cacd..3f25fd33f 100644 --- a/src/lib/platform/MSWindowsClipboard.h +++ b/src/lib/platform/MSWindowsClipboard.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboardAnyTextConverter.cpp b/src/lib/platform/MSWindowsClipboardAnyTextConverter.cpp index 8eb696d9f..5038469a2 100644 --- a/src/lib/platform/MSWindowsClipboardAnyTextConverter.cpp +++ b/src/lib/platform/MSWindowsClipboardAnyTextConverter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboardAnyTextConverter.h b/src/lib/platform/MSWindowsClipboardAnyTextConverter.h index 2bbf16ea0..c9d0372fa 100644 --- a/src/lib/platform/MSWindowsClipboardAnyTextConverter.h +++ b/src/lib/platform/MSWindowsClipboardAnyTextConverter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp b/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp index 93b078ed9..c78bd3c49 100644 --- a/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp +++ b/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboardBitmapConverter.h b/src/lib/platform/MSWindowsClipboardBitmapConverter.h index e2014d53e..593a686d5 100644 --- a/src/lib/platform/MSWindowsClipboardBitmapConverter.h +++ b/src/lib/platform/MSWindowsClipboardBitmapConverter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboardFacade.cpp b/src/lib/platform/MSWindowsClipboardFacade.cpp index 0180a4bdc..7601df567 100644 --- a/src/lib/platform/MSWindowsClipboardFacade.cpp +++ b/src/lib/platform/MSWindowsClipboardFacade.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboardFacade.h b/src/lib/platform/MSWindowsClipboardFacade.h index 2a08f3f65..65b193e1f 100644 --- a/src/lib/platform/MSWindowsClipboardFacade.h +++ b/src/lib/platform/MSWindowsClipboardFacade.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboardHTMLConverter.cpp b/src/lib/platform/MSWindowsClipboardHTMLConverter.cpp index c082bbadd..8064cd8f5 100644 --- a/src/lib/platform/MSWindowsClipboardHTMLConverter.cpp +++ b/src/lib/platform/MSWindowsClipboardHTMLConverter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboardHTMLConverter.h b/src/lib/platform/MSWindowsClipboardHTMLConverter.h index ef5cf2cb2..cba26e13f 100644 --- a/src/lib/platform/MSWindowsClipboardHTMLConverter.h +++ b/src/lib/platform/MSWindowsClipboardHTMLConverter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboardTextConverter.cpp b/src/lib/platform/MSWindowsClipboardTextConverter.cpp index 5e08e0689..6988c43be 100644 --- a/src/lib/platform/MSWindowsClipboardTextConverter.cpp +++ b/src/lib/platform/MSWindowsClipboardTextConverter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboardTextConverter.h b/src/lib/platform/MSWindowsClipboardTextConverter.h index 574abf103..956297df5 100644 --- a/src/lib/platform/MSWindowsClipboardTextConverter.h +++ b/src/lib/platform/MSWindowsClipboardTextConverter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboardUTF16Converter.cpp b/src/lib/platform/MSWindowsClipboardUTF16Converter.cpp index 068626e8b..999afabef 100644 --- a/src/lib/platform/MSWindowsClipboardUTF16Converter.cpp +++ b/src/lib/platform/MSWindowsClipboardUTF16Converter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsClipboardUTF16Converter.h b/src/lib/platform/MSWindowsClipboardUTF16Converter.h index 48c036874..f0e4a307a 100644 --- a/src/lib/platform/MSWindowsClipboardUTF16Converter.h +++ b/src/lib/platform/MSWindowsClipboardUTF16Converter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsDebugOutputter.cpp b/src/lib/platform/MSWindowsDebugOutputter.cpp index 7614fe834..0a0571a52 100644 --- a/src/lib/platform/MSWindowsDebugOutputter.cpp +++ b/src/lib/platform/MSWindowsDebugOutputter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsDebugOutputter.h b/src/lib/platform/MSWindowsDebugOutputter.h index e6265c55f..c41aa128b 100644 --- a/src/lib/platform/MSWindowsDebugOutputter.h +++ b/src/lib/platform/MSWindowsDebugOutputter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index c88801d8d..cb1ed9e98 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsDesks.h b/src/lib/platform/MSWindowsDesks.h index 8668e0980..acc5ac7c4 100644 --- a/src/lib/platform/MSWindowsDesks.h +++ b/src/lib/platform/MSWindowsDesks.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsDropTarget.cpp b/src/lib/platform/MSWindowsDropTarget.cpp index 8cb8caa4f..fca74bdd6 100644 --- a/src/lib/platform/MSWindowsDropTarget.cpp +++ b/src/lib/platform/MSWindowsDropTarget.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/lib/platform/MSWindowsDropTarget.h b/src/lib/platform/MSWindowsDropTarget.h index 877168b92..47a9c8f9e 100644 --- a/src/lib/platform/MSWindowsDropTarget.h +++ b/src/lib/platform/MSWindowsDropTarget.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/lib/platform/MSWindowsEventQueueBuffer.cpp b/src/lib/platform/MSWindowsEventQueueBuffer.cpp index 8ea0dc5ca..e5edb7ac4 100644 --- a/src/lib/platform/MSWindowsEventQueueBuffer.cpp +++ b/src/lib/platform/MSWindowsEventQueueBuffer.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsEventQueueBuffer.h b/src/lib/platform/MSWindowsEventQueueBuffer.h index f00678c0d..1ab413e2f 100644 --- a/src/lib/platform/MSWindowsEventQueueBuffer.h +++ b/src/lib/platform/MSWindowsEventQueueBuffer.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsHook.cpp b/src/lib/platform/MSWindowsHook.cpp index 483aace83..cdabd00d9 100644 --- a/src/lib/platform/MSWindowsHook.cpp +++ b/src/lib/platform/MSWindowsHook.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsHook.h b/src/lib/platform/MSWindowsHook.h index c74cc77fd..c16492d8c 100644 --- a/src/lib/platform/MSWindowsHook.h +++ b/src/lib/platform/MSWindowsHook.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsKeyState.cpp b/src/lib/platform/MSWindowsKeyState.cpp index 2b66a048b..9c4e729a7 100644 --- a/src/lib/platform/MSWindowsKeyState.cpp +++ b/src/lib/platform/MSWindowsKeyState.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsKeyState.h b/src/lib/platform/MSWindowsKeyState.h index 52f88b64e..4313f741a 100644 --- a/src/lib/platform/MSWindowsKeyState.h +++ b/src/lib/platform/MSWindowsKeyState.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 14c525b7f..de0c7c268 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsScreen.h b/src/lib/platform/MSWindowsScreen.h index f42e7025c..7e734f85c 100644 --- a/src/lib/platform/MSWindowsScreen.h +++ b/src/lib/platform/MSWindowsScreen.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsScreenSaver.cpp b/src/lib/platform/MSWindowsScreenSaver.cpp index 81b280a91..f2ab7334f 100644 --- a/src/lib/platform/MSWindowsScreenSaver.cpp +++ b/src/lib/platform/MSWindowsScreenSaver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsScreenSaver.h b/src/lib/platform/MSWindowsScreenSaver.h index 3eab1c7fc..c6fe7bd4e 100644 --- a/src/lib/platform/MSWindowsScreenSaver.h +++ b/src/lib/platform/MSWindowsScreenSaver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsSession.cpp b/src/lib/platform/MSWindowsSession.cpp index cfbd337ad..62bdbba29 100644 --- a/src/lib/platform/MSWindowsSession.cpp +++ b/src/lib/platform/MSWindowsSession.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/platform/MSWindowsSession.h b/src/lib/platform/MSWindowsSession.h index 464cd2737..8646fa526 100644 --- a/src/lib/platform/MSWindowsSession.h +++ b/src/lib/platform/MSWindowsSession.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/platform/MSWindowsUtil.cpp b/src/lib/platform/MSWindowsUtil.cpp index 2681a131b..c5523a051 100644 --- a/src/lib/platform/MSWindowsUtil.cpp +++ b/src/lib/platform/MSWindowsUtil.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsUtil.h b/src/lib/platform/MSWindowsUtil.h index 55809f4f1..55033da06 100644 --- a/src/lib/platform/MSWindowsUtil.h +++ b/src/lib/platform/MSWindowsUtil.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index a19fad8ca..4af95b08f 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2009 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/MSWindowsWatchdog.h b/src/lib/platform/MSWindowsWatchdog.h index c50a0ad3f..b6d829d88 100644 --- a/src/lib/platform/MSWindowsWatchdog.h +++ b/src/lib/platform/MSWindowsWatchdog.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2009 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboard.cpp b/src/lib/platform/OSXClipboard.cpp index 5b7ebb098..84f7c7cec 100644 --- a/src/lib/platform/OSXClipboard.cpp +++ b/src/lib/platform/OSXClipboard.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboard.h b/src/lib/platform/OSXClipboard.h index 4aeb3cd47..d02bbb204 100644 --- a/src/lib/platform/OSXClipboard.h +++ b/src/lib/platform/OSXClipboard.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboardAnyBitmapConverter.cpp b/src/lib/platform/OSXClipboardAnyBitmapConverter.cpp index dc1764e30..862e0232f 100644 --- a/src/lib/platform/OSXClipboardAnyBitmapConverter.cpp +++ b/src/lib/platform/OSXClipboardAnyBitmapConverter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * Copyright (C) 2014-2016 Symless Ltd. * Patch by Ryan Chapman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboardAnyBitmapConverter.h b/src/lib/platform/OSXClipboardAnyBitmapConverter.h index 4f12bcbed..08b6853be 100644 --- a/src/lib/platform/OSXClipboardAnyBitmapConverter.h +++ b/src/lib/platform/OSXClipboardAnyBitmapConverter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * Copyright (C) 2014-2016 Symless Ltd. * Patch by Ryan Chapman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboardAnyTextConverter.cpp b/src/lib/platform/OSXClipboardAnyTextConverter.cpp index a8dc50964..a1830d7b1 100644 --- a/src/lib/platform/OSXClipboardAnyTextConverter.cpp +++ b/src/lib/platform/OSXClipboardAnyTextConverter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboardAnyTextConverter.h b/src/lib/platform/OSXClipboardAnyTextConverter.h index b10a9823f..70be04f94 100644 --- a/src/lib/platform/OSXClipboardAnyTextConverter.h +++ b/src/lib/platform/OSXClipboardAnyTextConverter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboardBMPConverter.cpp b/src/lib/platform/OSXClipboardBMPConverter.cpp index 4d22a58c9..faeac2912 100644 --- a/src/lib/platform/OSXClipboardBMPConverter.cpp +++ b/src/lib/platform/OSXClipboardBMPConverter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * Copyright (C) 2014-2016 Symless Ltd. * Patch by Ryan Chapman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboardBMPConverter.h b/src/lib/platform/OSXClipboardBMPConverter.h index 32c8ce693..6fbfdcba7 100644 --- a/src/lib/platform/OSXClipboardBMPConverter.h +++ b/src/lib/platform/OSXClipboardBMPConverter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * Copyright (C) 2014-2016 Symless Ltd. * Patch by Ryan Chapman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboardHTMLConverter.cpp b/src/lib/platform/OSXClipboardHTMLConverter.cpp index 088d9b631..0760bb281 100644 --- a/src/lib/platform/OSXClipboardHTMLConverter.cpp +++ b/src/lib/platform/OSXClipboardHTMLConverter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * Copyright (C) 2014-2016 Symless Ltd. * Patch by Ryan Chapman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboardHTMLConverter.h b/src/lib/platform/OSXClipboardHTMLConverter.h index 9f303742d..3b0557007 100644 --- a/src/lib/platform/OSXClipboardHTMLConverter.h +++ b/src/lib/platform/OSXClipboardHTMLConverter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * Copyright (C) 2014-2016 Symless Ltd. * Patch by Ryan Chapman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboardTextConverter.cpp b/src/lib/platform/OSXClipboardTextConverter.cpp index 748241b4b..8b6f75cd6 100644 --- a/src/lib/platform/OSXClipboardTextConverter.cpp +++ b/src/lib/platform/OSXClipboardTextConverter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboardTextConverter.h b/src/lib/platform/OSXClipboardTextConverter.h index 4becc56fe..8618276de 100644 --- a/src/lib/platform/OSXClipboardTextConverter.h +++ b/src/lib/platform/OSXClipboardTextConverter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboardUTF16Converter.cpp b/src/lib/platform/OSXClipboardUTF16Converter.cpp index 6c49e6a4f..c72983a4b 100644 --- a/src/lib/platform/OSXClipboardUTF16Converter.cpp +++ b/src/lib/platform/OSXClipboardUTF16Converter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXClipboardUTF16Converter.h b/src/lib/platform/OSXClipboardUTF16Converter.h index e2b05fb3a..8b7536249 100644 --- a/src/lib/platform/OSXClipboardUTF16Converter.h +++ b/src/lib/platform/OSXClipboardUTF16Converter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXDragSimulator.h b/src/lib/platform/OSXDragSimulator.h index 58fa02b5e..fa2abe334 100644 --- a/src/lib/platform/OSXDragSimulator.h +++ b/src/lib/platform/OSXDragSimulator.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/platform/OSXDragSimulator.m b/src/lib/platform/OSXDragSimulator.m index 32aa192a7..333abd005 100644 --- a/src/lib/platform/OSXDragSimulator.m +++ b/src/lib/platform/OSXDragSimulator.m @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/platform/OSXDragView.h b/src/lib/platform/OSXDragView.h index 99b6aadc7..2f8ce9d44 100644 --- a/src/lib/platform/OSXDragView.h +++ b/src/lib/platform/OSXDragView.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/platform/OSXDragView.m b/src/lib/platform/OSXDragView.m index 7bda39f67..4862e97a1 100644 --- a/src/lib/platform/OSXDragView.m +++ b/src/lib/platform/OSXDragView.m @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/platform/OSXEventQueueBuffer.cpp b/src/lib/platform/OSXEventQueueBuffer.cpp index a2dc81145..2ee439c8c 100644 --- a/src/lib/platform/OSXEventQueueBuffer.cpp +++ b/src/lib/platform/OSXEventQueueBuffer.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXEventQueueBuffer.h b/src/lib/platform/OSXEventQueueBuffer.h index 89da08098..abc1a39de 100644 --- a/src/lib/platform/OSXEventQueueBuffer.h +++ b/src/lib/platform/OSXEventQueueBuffer.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 1bd6d08a0..ab96282b8 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXKeyState.h b/src/lib/platform/OSXKeyState.h index 26050a284..00dbb5518 100644 --- a/src/lib/platform/OSXKeyState.h +++ b/src/lib/platform/OSXKeyState.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXPasteboardPeeker.h b/src/lib/platform/OSXPasteboardPeeker.h index b40831fe9..a41151736 100644 --- a/src/lib/platform/OSXPasteboardPeeker.h +++ b/src/lib/platform/OSXPasteboardPeeker.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/platform/OSXPasteboardPeeker.m b/src/lib/platform/OSXPasteboardPeeker.m index 586600b6d..de70a48dc 100644 --- a/src/lib/platform/OSXPasteboardPeeker.m +++ b/src/lib/platform/OSXPasteboardPeeker.m @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index 4714d2bf0..182d6e532 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h index 5fb8a4039..77a0cedc5 100644 --- a/src/lib/platform/OSXScreen.h +++ b/src/lib/platform/OSXScreen.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXScreenSaver.cpp b/src/lib/platform/OSXScreenSaver.cpp index c794a6502..91b724932 100644 --- a/src/lib/platform/OSXScreenSaver.cpp +++ b/src/lib/platform/OSXScreenSaver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXScreenSaver.h b/src/lib/platform/OSXScreenSaver.h index 010a2b2c1..0f5315c98 100644 --- a/src/lib/platform/OSXScreenSaver.h +++ b/src/lib/platform/OSXScreenSaver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXScreenSaverControl.h b/src/lib/platform/OSXScreenSaverControl.h index e25c12208..01608c676 100644 --- a/src/lib/platform/OSXScreenSaverControl.h +++ b/src/lib/platform/OSXScreenSaverControl.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2009 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/OSXScreenSaverUtil.h b/src/lib/platform/OSXScreenSaverUtil.h index a07d344fc..293b6caaf 100644 --- a/src/lib/platform/OSXScreenSaverUtil.h +++ b/src/lib/platform/OSXScreenSaverUtil.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboard.cpp b/src/lib/platform/XWindowsClipboard.cpp index 00bad52d7..6aa3a4cb8 100644 --- a/src/lib/platform/XWindowsClipboard.cpp +++ b/src/lib/platform/XWindowsClipboard.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboard.h b/src/lib/platform/XWindowsClipboard.h index baf71ba28..d091fd6af 100644 --- a/src/lib/platform/XWindowsClipboard.h +++ b/src/lib/platform/XWindowsClipboard.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp index 97f8d4825..07f89a3ab 100644 --- a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp +++ b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.h b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.h index 888a70a96..02c21c472 100644 --- a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.h +++ b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboardBMPConverter.cpp b/src/lib/platform/XWindowsClipboardBMPConverter.cpp index 1f0ce0f11..e1f35ff14 100644 --- a/src/lib/platform/XWindowsClipboardBMPConverter.cpp +++ b/src/lib/platform/XWindowsClipboardBMPConverter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboardBMPConverter.h b/src/lib/platform/XWindowsClipboardBMPConverter.h index 19bc98353..9d419b635 100644 --- a/src/lib/platform/XWindowsClipboardBMPConverter.h +++ b/src/lib/platform/XWindowsClipboardBMPConverter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboardHTMLConverter.cpp b/src/lib/platform/XWindowsClipboardHTMLConverter.cpp index 40d19f9fa..8f06cc8da 100644 --- a/src/lib/platform/XWindowsClipboardHTMLConverter.cpp +++ b/src/lib/platform/XWindowsClipboardHTMLConverter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboardHTMLConverter.h b/src/lib/platform/XWindowsClipboardHTMLConverter.h index 43f210108..f0a702881 100644 --- a/src/lib/platform/XWindowsClipboardHTMLConverter.h +++ b/src/lib/platform/XWindowsClipboardHTMLConverter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboardTextConverter.cpp b/src/lib/platform/XWindowsClipboardTextConverter.cpp index 76ddccc7e..07fb9e3dc 100644 --- a/src/lib/platform/XWindowsClipboardTextConverter.cpp +++ b/src/lib/platform/XWindowsClipboardTextConverter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboardTextConverter.h b/src/lib/platform/XWindowsClipboardTextConverter.h index d7c109ac7..d031c85eb 100644 --- a/src/lib/platform/XWindowsClipboardTextConverter.h +++ b/src/lib/platform/XWindowsClipboardTextConverter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboardUCS2Converter.cpp b/src/lib/platform/XWindowsClipboardUCS2Converter.cpp index 0f045a1e6..cc1d0207e 100644 --- a/src/lib/platform/XWindowsClipboardUCS2Converter.cpp +++ b/src/lib/platform/XWindowsClipboardUCS2Converter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboardUCS2Converter.h b/src/lib/platform/XWindowsClipboardUCS2Converter.h index dfd5a1fb8..876ec7d86 100644 --- a/src/lib/platform/XWindowsClipboardUCS2Converter.h +++ b/src/lib/platform/XWindowsClipboardUCS2Converter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboardUTF8Converter.cpp b/src/lib/platform/XWindowsClipboardUTF8Converter.cpp index 01db17970..685dd60ef 100644 --- a/src/lib/platform/XWindowsClipboardUTF8Converter.cpp +++ b/src/lib/platform/XWindowsClipboardUTF8Converter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsClipboardUTF8Converter.h b/src/lib/platform/XWindowsClipboardUTF8Converter.h index 12037931a..3a4f54053 100644 --- a/src/lib/platform/XWindowsClipboardUTF8Converter.h +++ b/src/lib/platform/XWindowsClipboardUTF8Converter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsEventQueueBuffer.cpp b/src/lib/platform/XWindowsEventQueueBuffer.cpp index 49836e230..03fd451e3 100644 --- a/src/lib/platform/XWindowsEventQueueBuffer.cpp +++ b/src/lib/platform/XWindowsEventQueueBuffer.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsEventQueueBuffer.h b/src/lib/platform/XWindowsEventQueueBuffer.h index bb3097c74..902b66274 100644 --- a/src/lib/platform/XWindowsEventQueueBuffer.h +++ b/src/lib/platform/XWindowsEventQueueBuffer.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsKeyState.cpp b/src/lib/platform/XWindowsKeyState.cpp index 149356c35..6f82d4dfb 100644 --- a/src/lib/platform/XWindowsKeyState.cpp +++ b/src/lib/platform/XWindowsKeyState.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsKeyState.h b/src/lib/platform/XWindowsKeyState.h index f3bdd7606..8c8450548 100644 --- a/src/lib/platform/XWindowsKeyState.h +++ b/src/lib/platform/XWindowsKeyState.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp index eb68666e3..b16e2e516 100644 --- a/src/lib/platform/XWindowsScreen.cpp +++ b/src/lib/platform/XWindowsScreen.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsScreen.h b/src/lib/platform/XWindowsScreen.h index 1e8eeac31..fcf93502f 100644 --- a/src/lib/platform/XWindowsScreen.h +++ b/src/lib/platform/XWindowsScreen.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsScreenSaver.cpp b/src/lib/platform/XWindowsScreenSaver.cpp index 2d6a63c7b..6e3ad9c60 100644 --- a/src/lib/platform/XWindowsScreenSaver.cpp +++ b/src/lib/platform/XWindowsScreenSaver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsScreenSaver.h b/src/lib/platform/XWindowsScreenSaver.h index 94a244c7b..297eb6f0a 100644 --- a/src/lib/platform/XWindowsScreenSaver.h +++ b/src/lib/platform/XWindowsScreenSaver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsUtil.cpp b/src/lib/platform/XWindowsUtil.cpp index aec69a070..6103cced8 100644 --- a/src/lib/platform/XWindowsUtil.cpp +++ b/src/lib/platform/XWindowsUtil.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/platform/XWindowsUtil.h b/src/lib/platform/XWindowsUtil.h index b11c612e3..e39fc17d4 100644 --- a/src/lib/platform/XWindowsUtil.h +++ b/src/lib/platform/XWindowsUtil.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/plugin/CMakeLists.txt b/src/lib/plugin/CMakeLists.txt index 81416002b..237da9896 100644 --- a/src/lib/plugin/CMakeLists.txt +++ b/src/lib/plugin/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2012 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/plugin/ns/CMakeLists.txt b/src/lib/plugin/ns/CMakeLists.txt index 3e40a4728..80b567412 100644 --- a/src/lib/plugin/ns/CMakeLists.txt +++ b/src/lib/plugin/ns/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2015 Synergy Si Ltd. +# 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 diff --git a/src/lib/plugin/ns/SecureListenSocket.cpp b/src/lib/plugin/ns/SecureListenSocket.cpp index ab4288476..0dd96f32c 100644 --- a/src/lib/plugin/ns/SecureListenSocket.cpp +++ b/src/lib/plugin/ns/SecureListenSocket.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/lib/plugin/ns/SecureListenSocket.h b/src/lib/plugin/ns/SecureListenSocket.h index 11a31e65a..ff19602c4 100644 --- a/src/lib/plugin/ns/SecureListenSocket.h +++ b/src/lib/plugin/ns/SecureListenSocket.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 80bad6dcc..0e5a03efb 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/lib/plugin/ns/SecureSocket.h b/src/lib/plugin/ns/SecureSocket.h index 871e1e4d7..ef3f63027 100644 --- a/src/lib/plugin/ns/SecureSocket.h +++ b/src/lib/plugin/ns/SecureSocket.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/lib/plugin/ns/ns.cpp b/src/lib/plugin/ns/ns.cpp index e7ae164e6..17ba322ff 100644 --- a/src/lib/plugin/ns/ns.cpp +++ b/src/lib/plugin/ns/ns.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd + * 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 diff --git a/src/lib/plugin/ns/ns.h b/src/lib/plugin/ns/ns.h index 1350f0d3c..39999110b 100644 --- a/src/lib/plugin/ns/ns.h +++ b/src/lib/plugin/ns/ns.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd + * 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 diff --git a/src/lib/plugin/winmmjoy/CMakeLists.txt b/src/lib/plugin/winmmjoy/CMakeLists.txt index cd155d45a..b07915bfc 100644 --- a/src/lib/plugin/winmmjoy/CMakeLists.txt +++ b/src/lib/plugin/winmmjoy/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2012 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/plugin/winmmjoy/winmmjoy.cpp b/src/lib/plugin/winmmjoy/winmmjoy.cpp index 40dddedef..d6306e82d 100644 --- a/src/lib/plugin/winmmjoy/winmmjoy.cpp +++ b/src/lib/plugin/winmmjoy/winmmjoy.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/plugin/winmmjoy/winmmjoy.h b/src/lib/plugin/winmmjoy/winmmjoy.h index 8b4c6620c..712bd6d99 100644 --- a/src/lib/plugin/winmmjoy/winmmjoy.h +++ b/src/lib/plugin/winmmjoy/winmmjoy.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/BaseClientProxy.cpp b/src/lib/server/BaseClientProxy.cpp index 7738dfdd6..0ffedcb31 100644 --- a/src/lib/server/BaseClientProxy.cpp +++ b/src/lib/server/BaseClientProxy.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2006 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/BaseClientProxy.h b/src/lib/server/BaseClientProxy.h index 746f47972..02ce90e6e 100644 --- a/src/lib/server/BaseClientProxy.h +++ b/src/lib/server/BaseClientProxy.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/CMakeLists.txt b/src/lib/server/CMakeLists.txt index ecafba8c9..2c34af078 100644 --- a/src/lib/server/CMakeLists.txt +++ b/src/lib/server/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index 9b103493b..078b6013c 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientListener.h b/src/lib/server/ClientListener.h index c0b35ab3e..7aefe7f47 100644 --- a/src/lib/server/ClientListener.h +++ b/src/lib/server/ClientListener.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxy.cpp b/src/lib/server/ClientProxy.cpp index 4277d4e7a..36e845e5d 100644 --- a/src/lib/server/ClientProxy.cpp +++ b/src/lib/server/ClientProxy.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxy.h b/src/lib/server/ClientProxy.h index 134324cf8..9f40d9b10 100644 --- a/src/lib/server/ClientProxy.h +++ b/src/lib/server/ClientProxy.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxy1_0.cpp b/src/lib/server/ClientProxy1_0.cpp index c124624f1..dc9b01384 100644 --- a/src/lib/server/ClientProxy1_0.cpp +++ b/src/lib/server/ClientProxy1_0.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxy1_0.h b/src/lib/server/ClientProxy1_0.h index 4c7f020bf..8cb7858c9 100644 --- a/src/lib/server/ClientProxy1_0.h +++ b/src/lib/server/ClientProxy1_0.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxy1_1.cpp b/src/lib/server/ClientProxy1_1.cpp index 7187995be..17709b708 100644 --- a/src/lib/server/ClientProxy1_1.cpp +++ b/src/lib/server/ClientProxy1_1.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxy1_1.h b/src/lib/server/ClientProxy1_1.h index c599abe72..238b3702f 100644 --- a/src/lib/server/ClientProxy1_1.h +++ b/src/lib/server/ClientProxy1_1.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxy1_2.cpp b/src/lib/server/ClientProxy1_2.cpp index 2ea9a3188..266aa62d2 100644 --- a/src/lib/server/ClientProxy1_2.cpp +++ b/src/lib/server/ClientProxy1_2.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxy1_2.h b/src/lib/server/ClientProxy1_2.h index 8645dabab..c11d1ef56 100644 --- a/src/lib/server/ClientProxy1_2.h +++ b/src/lib/server/ClientProxy1_2.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxy1_3.cpp b/src/lib/server/ClientProxy1_3.cpp index 563009ecd..6c32e10df 100644 --- a/src/lib/server/ClientProxy1_3.cpp +++ b/src/lib/server/ClientProxy1_3.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2006 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxy1_3.h b/src/lib/server/ClientProxy1_3.h index bd3379664..054b201aa 100644 --- a/src/lib/server/ClientProxy1_3.h +++ b/src/lib/server/ClientProxy1_3.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2006 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxy1_4.cpp b/src/lib/server/ClientProxy1_4.cpp index 377a046b5..205876a44 100644 --- a/src/lib/server/ClientProxy1_4.cpp +++ b/src/lib/server/ClientProxy1_4.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxy1_4.h b/src/lib/server/ClientProxy1_4.h index 4a5160f3c..14adb118e 100644 --- a/src/lib/server/ClientProxy1_4.h +++ b/src/lib/server/ClientProxy1_4.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp index 2cdceb400..7df851de6 100644 --- a/src/lib/server/ClientProxy1_5.cpp +++ b/src/lib/server/ClientProxy1_5.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/server/ClientProxy1_5.h b/src/lib/server/ClientProxy1_5.h index 3347f170a..3f1fc8e63 100644 --- a/src/lib/server/ClientProxy1_5.h +++ b/src/lib/server/ClientProxy1_5.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp index b4c33e824..054efca14 100644 --- a/src/lib/server/ClientProxy1_6.cpp +++ b/src/lib/server/ClientProxy1_6.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Inc. + * 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 diff --git a/src/lib/server/ClientProxy1_6.h b/src/lib/server/ClientProxy1_6.h index d8c69b11d..98dd8d245 100644 --- a/src/lib/server/ClientProxy1_6.h +++ b/src/lib/server/ClientProxy1_6.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Inc. + * 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 diff --git a/src/lib/server/ClientProxyUnknown.cpp b/src/lib/server/ClientProxyUnknown.cpp index a328d2138..eab156225 100644 --- a/src/lib/server/ClientProxyUnknown.cpp +++ b/src/lib/server/ClientProxyUnknown.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/ClientProxyUnknown.h b/src/lib/server/ClientProxyUnknown.h index f692b3746..e618b3a25 100644 --- a/src/lib/server/ClientProxyUnknown.h +++ b/src/lib/server/ClientProxyUnknown.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/Config.cpp b/src/lib/server/Config.cpp index d76897098..87cf0b7f6 100644 --- a/src/lib/server/Config.cpp +++ b/src/lib/server/Config.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/Config.h b/src/lib/server/Config.h index 07b17544c..12b244cd8 100644 --- a/src/lib/server/Config.h +++ b/src/lib/server/Config.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/InputFilter.cpp b/src/lib/server/InputFilter.cpp index 787cf58a5..f03786a7b 100644 --- a/src/lib/server/InputFilter.cpp +++ b/src/lib/server/InputFilter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2005 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/InputFilter.h b/src/lib/server/InputFilter.h index 0f375a672..25e2494ad 100644 --- a/src/lib/server/InputFilter.h +++ b/src/lib/server/InputFilter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2005 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/PrimaryClient.cpp b/src/lib/server/PrimaryClient.cpp index 849937a4e..2da3665cf 100644 --- a/src/lib/server/PrimaryClient.cpp +++ b/src/lib/server/PrimaryClient.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/PrimaryClient.h b/src/lib/server/PrimaryClient.h index 61f206185..1b22886e7 100644 --- a/src/lib/server/PrimaryClient.h +++ b/src/lib/server/PrimaryClient.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index f4d26bff5..6135d1be3 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index 8e3fee05b..4defc7b29 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/App.cpp b/src/lib/synergy/App.cpp index 32aa4de57..4d9aa2984 100644 --- a/src/lib/synergy/App.cpp +++ b/src/lib/synergy/App.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/App.h b/src/lib/synergy/App.h index 2ce85d2fa..8cfaea91b 100644 --- a/src/lib/synergy/App.h +++ b/src/lib/synergy/App.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/AppUtil.cpp b/src/lib/synergy/AppUtil.cpp index e9d063962..7a4426753 100644 --- a/src/lib/synergy/AppUtil.cpp +++ b/src/lib/synergy/AppUtil.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/AppUtil.h b/src/lib/synergy/AppUtil.h index 3e7218bc0..783a922cf 100644 --- a/src/lib/synergy/AppUtil.h +++ b/src/lib/synergy/AppUtil.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index 7220c583f..f4ce89840 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, inc. + * 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 diff --git a/src/lib/synergy/ArgParser.h b/src/lib/synergy/ArgParser.h index f4d6d98c6..73b1039bc 100644 --- a/src/lib/synergy/ArgParser.h +++ b/src/lib/synergy/ArgParser.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/lib/synergy/ArgsBase.cpp b/src/lib/synergy/ArgsBase.cpp index 1a064556b..a2d70f5bb 100644 --- a/src/lib/synergy/ArgsBase.cpp +++ b/src/lib/synergy/ArgsBase.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/ArgsBase.h b/src/lib/synergy/ArgsBase.h index 7dd49f73a..166e30ba2 100644 --- a/src/lib/synergy/ArgsBase.h +++ b/src/lib/synergy/ArgsBase.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/CMakeLists.txt b/src/lib/synergy/CMakeLists.txt index 1d05bfb9c..0972be8c1 100644 --- a/src/lib/synergy/CMakeLists.txt +++ b/src/lib/synergy/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/Chunk.cpp b/src/lib/synergy/Chunk.cpp index fe4aeae71..fa690ba7e 100644 --- a/src/lib/synergy/Chunk.cpp +++ b/src/lib/synergy/Chunk.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Inc. + * 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 diff --git a/src/lib/synergy/Chunk.h b/src/lib/synergy/Chunk.h index 13431950d..366788a48 100644 --- a/src/lib/synergy/Chunk.h +++ b/src/lib/synergy/Chunk.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Inc. + * 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 diff --git a/src/lib/synergy/ClientApp.cpp b/src/lib/synergy/ClientApp.cpp index 972f63c4a..6adac9b98 100644 --- a/src/lib/synergy/ClientApp.cpp +++ b/src/lib/synergy/ClientApp.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/ClientApp.h b/src/lib/synergy/ClientApp.h index 80a2b5e8b..2113318f5 100644 --- a/src/lib/synergy/ClientApp.h +++ b/src/lib/synergy/ClientApp.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/ClientArgs.cpp b/src/lib/synergy/ClientArgs.cpp index 7e44e7bf0..a7b8f733e 100644 --- a/src/lib/synergy/ClientArgs.cpp +++ b/src/lib/synergy/ClientArgs.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/lib/synergy/ClientArgs.h b/src/lib/synergy/ClientArgs.h index 3d8428cfa..004966d87 100644 --- a/src/lib/synergy/ClientArgs.h +++ b/src/lib/synergy/ClientArgs.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/lib/synergy/ClientTaskBarReceiver.cpp b/src/lib/synergy/ClientTaskBarReceiver.cpp index 7d6519f13..b84e8b999 100644 --- a/src/lib/synergy/ClientTaskBarReceiver.cpp +++ b/src/lib/synergy/ClientTaskBarReceiver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/ClientTaskBarReceiver.h b/src/lib/synergy/ClientTaskBarReceiver.h index 1eb9d44bc..7a317c4a5 100644 --- a/src/lib/synergy/ClientTaskBarReceiver.h +++ b/src/lib/synergy/ClientTaskBarReceiver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/Clipboard.cpp b/src/lib/synergy/Clipboard.cpp index fef58e8d7..870252528 100644 --- a/src/lib/synergy/Clipboard.cpp +++ b/src/lib/synergy/Clipboard.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/Clipboard.h b/src/lib/synergy/Clipboard.h index 97a5f6d14..e0195f707 100644 --- a/src/lib/synergy/Clipboard.h +++ b/src/lib/synergy/Clipboard.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/ClipboardChunk.cpp b/src/lib/synergy/ClipboardChunk.cpp index d3d6de282..f7a5673db 100644 --- a/src/lib/synergy/ClipboardChunk.cpp +++ b/src/lib/synergy/ClipboardChunk.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Inc. + * 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 diff --git a/src/lib/synergy/ClipboardChunk.h b/src/lib/synergy/ClipboardChunk.h index 459f28b28..8fc09bdad 100644 --- a/src/lib/synergy/ClipboardChunk.h +++ b/src/lib/synergy/ClipboardChunk.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Inc. + * 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 diff --git a/src/lib/synergy/DaemonApp.cpp b/src/lib/synergy/DaemonApp.cpp index bb1ef91f8..d368eaf40 100644 --- a/src/lib/synergy/DaemonApp.cpp +++ b/src/lib/synergy/DaemonApp.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/DaemonApp.h b/src/lib/synergy/DaemonApp.h index b4f01a71d..6527272e8 100644 --- a/src/lib/synergy/DaemonApp.h +++ b/src/lib/synergy/DaemonApp.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/DragInformation.cpp b/src/lib/synergy/DragInformation.cpp index aa94dd91c..b1bd4801c 100644 --- a/src/lib/synergy/DragInformation.cpp +++ b/src/lib/synergy/DragInformation.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/synergy/DragInformation.h b/src/lib/synergy/DragInformation.h index 2a171f62d..d310fa6cd 100644 --- a/src/lib/synergy/DragInformation.h +++ b/src/lib/synergy/DragInformation.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/synergy/DropHelper.cpp b/src/lib/synergy/DropHelper.cpp index a5fde5f1b..6346ce0d8 100644 --- a/src/lib/synergy/DropHelper.cpp +++ b/src/lib/synergy/DropHelper.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/lib/synergy/DropHelper.h b/src/lib/synergy/DropHelper.h index 915749ac2..0e754baf3 100644 --- a/src/lib/synergy/DropHelper.h +++ b/src/lib/synergy/DropHelper.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/lib/synergy/FileChunk.cpp b/src/lib/synergy/FileChunk.cpp index 5eae12329..7906a3a3c 100644 --- a/src/lib/synergy/FileChunk.cpp +++ b/src/lib/synergy/FileChunk.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Inc. + * 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 diff --git a/src/lib/synergy/FileChunk.h b/src/lib/synergy/FileChunk.h index 2c2e0ee58..56c3cb623 100644 --- a/src/lib/synergy/FileChunk.h +++ b/src/lib/synergy/FileChunk.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Inc. + * 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 diff --git a/src/lib/synergy/IApp.h b/src/lib/synergy/IApp.h index c041f984e..7eabf4778 100644 --- a/src/lib/synergy/IApp.h +++ b/src/lib/synergy/IApp.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/IAppUtil.h b/src/lib/synergy/IAppUtil.h index 228d40c6c..3653fc5c3 100644 --- a/src/lib/synergy/IAppUtil.h +++ b/src/lib/synergy/IAppUtil.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/IClient.h b/src/lib/synergy/IClient.h index 6296e8848..8403cbd96 100644 --- a/src/lib/synergy/IClient.h +++ b/src/lib/synergy/IClient.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/IClipboard.cpp b/src/lib/synergy/IClipboard.cpp index bd6602c18..a8019184d 100644 --- a/src/lib/synergy/IClipboard.cpp +++ b/src/lib/synergy/IClipboard.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/IClipboard.h b/src/lib/synergy/IClipboard.h index 17cfd58eb..1d8e2f218 100644 --- a/src/lib/synergy/IClipboard.h +++ b/src/lib/synergy/IClipboard.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/IKeyState.cpp b/src/lib/synergy/IKeyState.cpp index ce738ad10..2b130a4d9 100644 --- a/src/lib/synergy/IKeyState.cpp +++ b/src/lib/synergy/IKeyState.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/IKeyState.h b/src/lib/synergy/IKeyState.h index da0062c4f..26b0b2d9a 100644 --- a/src/lib/synergy/IKeyState.h +++ b/src/lib/synergy/IKeyState.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/INode.h b/src/lib/synergy/INode.h index c29bd4b47..33c551cff 100644 --- a/src/lib/synergy/INode.h +++ b/src/lib/synergy/INode.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/IPlatformScreen.h b/src/lib/synergy/IPlatformScreen.h index cd00b23ce..6a8e0fe95 100644 --- a/src/lib/synergy/IPlatformScreen.h +++ b/src/lib/synergy/IPlatformScreen.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/IPrimaryScreen.cpp b/src/lib/synergy/IPrimaryScreen.cpp index 76f4b740f..929c05904 100644 --- a/src/lib/synergy/IPrimaryScreen.cpp +++ b/src/lib/synergy/IPrimaryScreen.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/IPrimaryScreen.h b/src/lib/synergy/IPrimaryScreen.h index 7d92bee32..bc4a745d2 100644 --- a/src/lib/synergy/IPrimaryScreen.h +++ b/src/lib/synergy/IPrimaryScreen.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/IScreen.h b/src/lib/synergy/IScreen.h index 9b8d8a268..b3b348bf5 100644 --- a/src/lib/synergy/IScreen.h +++ b/src/lib/synergy/IScreen.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/IScreenSaver.h b/src/lib/synergy/IScreenSaver.h index b89bc292f..e6cd63c08 100644 --- a/src/lib/synergy/IScreenSaver.h +++ b/src/lib/synergy/IScreenSaver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/ISecondaryScreen.h b/src/lib/synergy/ISecondaryScreen.h index 929ae87e3..007aeafd2 100644 --- a/src/lib/synergy/ISecondaryScreen.h +++ b/src/lib/synergy/ISecondaryScreen.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/KeyMap.cpp b/src/lib/synergy/KeyMap.cpp index 30d1b24b7..52c5deace 100644 --- a/src/lib/synergy/KeyMap.cpp +++ b/src/lib/synergy/KeyMap.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2005 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/KeyMap.h b/src/lib/synergy/KeyMap.h index 991b32696..de869e68d 100644 --- a/src/lib/synergy/KeyMap.h +++ b/src/lib/synergy/KeyMap.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2005 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/KeyState.cpp b/src/lib/synergy/KeyState.cpp index a08caf0f6..3808b9449 100644 --- a/src/lib/synergy/KeyState.cpp +++ b/src/lib/synergy/KeyState.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/KeyState.h b/src/lib/synergy/KeyState.h index 2cebe1689..db7505365 100644 --- a/src/lib/synergy/KeyState.h +++ b/src/lib/synergy/KeyState.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/PacketStreamFilter.cpp b/src/lib/synergy/PacketStreamFilter.cpp index fc8601fe0..b7ec7f0ce 100644 --- a/src/lib/synergy/PacketStreamFilter.cpp +++ b/src/lib/synergy/PacketStreamFilter.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/PacketStreamFilter.h b/src/lib/synergy/PacketStreamFilter.h index 88f080ae5..674a6a9b9 100644 --- a/src/lib/synergy/PacketStreamFilter.h +++ b/src/lib/synergy/PacketStreamFilter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/PlatformScreen.cpp b/src/lib/synergy/PlatformScreen.cpp index 4916f8867..59bf3a1fd 100644 --- a/src/lib/synergy/PlatformScreen.cpp +++ b/src/lib/synergy/PlatformScreen.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/PlatformScreen.h b/src/lib/synergy/PlatformScreen.h index db65b848a..8afccb76f 100644 --- a/src/lib/synergy/PlatformScreen.h +++ b/src/lib/synergy/PlatformScreen.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2004 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/PortableTaskBarReceiver.cpp b/src/lib/synergy/PortableTaskBarReceiver.cpp index b29015b75..594e328c8 100644 --- a/src/lib/synergy/PortableTaskBarReceiver.cpp +++ b/src/lib/synergy/PortableTaskBarReceiver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/PortableTaskBarReceiver.h b/src/lib/synergy/PortableTaskBarReceiver.h index 7c9e5c3d1..88130c218 100644 --- a/src/lib/synergy/PortableTaskBarReceiver.h +++ b/src/lib/synergy/PortableTaskBarReceiver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/ProtocolUtil.cpp b/src/lib/synergy/ProtocolUtil.cpp index d5e0aed7d..73496c895 100644 --- a/src/lib/synergy/ProtocolUtil.cpp +++ b/src/lib/synergy/ProtocolUtil.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/ProtocolUtil.h b/src/lib/synergy/ProtocolUtil.h index e01729a33..9251e1b36 100644 --- a/src/lib/synergy/ProtocolUtil.h +++ b/src/lib/synergy/ProtocolUtil.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/Screen.cpp b/src/lib/synergy/Screen.cpp index 5fe5f5335..b69b5f1de 100644 --- a/src/lib/synergy/Screen.cpp +++ b/src/lib/synergy/Screen.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/Screen.h b/src/lib/synergy/Screen.h index 15a79602d..83d60b594 100644 --- a/src/lib/synergy/Screen.h +++ b/src/lib/synergy/Screen.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/ServerApp.cpp b/src/lib/synergy/ServerApp.cpp index 7d4a67184..0153f9a20 100644 --- a/src/lib/synergy/ServerApp.cpp +++ b/src/lib/synergy/ServerApp.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/ServerApp.h b/src/lib/synergy/ServerApp.h index f20b61212..b1f14f80d 100644 --- a/src/lib/synergy/ServerApp.h +++ b/src/lib/synergy/ServerApp.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/ServerArgs.cpp b/src/lib/synergy/ServerArgs.cpp index 9f9be718c..f56f6e8d5 100644 --- a/src/lib/synergy/ServerArgs.cpp +++ b/src/lib/synergy/ServerArgs.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/lib/synergy/ServerArgs.h b/src/lib/synergy/ServerArgs.h index 857ba00c3..93f0d3119 100644 --- a/src/lib/synergy/ServerArgs.h +++ b/src/lib/synergy/ServerArgs.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/lib/synergy/ServerTaskBarReceiver.cpp b/src/lib/synergy/ServerTaskBarReceiver.cpp index 82732c01e..230619af4 100644 --- a/src/lib/synergy/ServerTaskBarReceiver.cpp +++ b/src/lib/synergy/ServerTaskBarReceiver.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/ServerTaskBarReceiver.h b/src/lib/synergy/ServerTaskBarReceiver.h index f2aac345a..da82b56b0 100644 --- a/src/lib/synergy/ServerTaskBarReceiver.h +++ b/src/lib/synergy/ServerTaskBarReceiver.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index d54839f33..651d1a264 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/synergy/StreamChunker.h b/src/lib/synergy/StreamChunker.h index cca9442b5..7ae085f46 100644 --- a/src/lib/synergy/StreamChunker.h +++ b/src/lib/synergy/StreamChunker.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 49c8930f0..b024bfe94 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/lib/synergy/ToolApp.h b/src/lib/synergy/ToolApp.h index 7f0b6ea7b..8706c79a9 100644 --- a/src/lib/synergy/ToolApp.h +++ b/src/lib/synergy/ToolApp.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/lib/synergy/ToolArgs.cpp b/src/lib/synergy/ToolArgs.cpp index 234393177..f5d2524a6 100644 --- a/src/lib/synergy/ToolArgs.cpp +++ b/src/lib/synergy/ToolArgs.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/lib/synergy/ToolArgs.h b/src/lib/synergy/ToolArgs.h index fb0b3e783..0ebc0a4aa 100644 --- a/src/lib/synergy/ToolArgs.h +++ b/src/lib/synergy/ToolArgs.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/lib/synergy/XScreen.cpp b/src/lib/synergy/XScreen.cpp index ebe371da0..512abbb27 100644 --- a/src/lib/synergy/XScreen.cpp +++ b/src/lib/synergy/XScreen.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/XScreen.h b/src/lib/synergy/XScreen.h index dbcbfaaba..cb72225bb 100644 --- a/src/lib/synergy/XScreen.h +++ b/src/lib/synergy/XScreen.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/XSynergy.cpp b/src/lib/synergy/XSynergy.cpp index bb6c9c95a..40514edd9 100644 --- a/src/lib/synergy/XSynergy.cpp +++ b/src/lib/synergy/XSynergy.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/XSynergy.h b/src/lib/synergy/XSynergy.h index 78d4d86f9..b2575c810 100644 --- a/src/lib/synergy/XSynergy.h +++ b/src/lib/synergy/XSynergy.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/clipboard_types.h b/src/lib/synergy/clipboard_types.h index 9a5a78b23..a6cbef996 100644 --- a/src/lib/synergy/clipboard_types.h +++ b/src/lib/synergy/clipboard_types.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/key_types.cpp b/src/lib/synergy/key_types.cpp index e1e78ac6f..130b163fc 100644 --- a/src/lib/synergy/key_types.cpp +++ b/src/lib/synergy/key_types.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/key_types.h b/src/lib/synergy/key_types.h index eccfb0867..ea9387b1c 100644 --- a/src/lib/synergy/key_types.h +++ b/src/lib/synergy/key_types.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/mouse_types.h b/src/lib/synergy/mouse_types.h index 2ad79d0fb..be9f1920d 100644 --- a/src/lib/synergy/mouse_types.h +++ b/src/lib/synergy/mouse_types.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/option_types.h b/src/lib/synergy/option_types.h index 009c861f4..ca6345718 100644 --- a/src/lib/synergy/option_types.h +++ b/src/lib/synergy/option_types.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/protocol_types.cpp b/src/lib/synergy/protocol_types.cpp index 1e274b060..db5c9767b 100644 --- a/src/lib/synergy/protocol_types.cpp +++ b/src/lib/synergy/protocol_types.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/protocol_types.h b/src/lib/synergy/protocol_types.h index 198956846..089b22be9 100644 --- a/src/lib/synergy/protocol_types.h +++ b/src/lib/synergy/protocol_types.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/unix/AppUtilUnix.cpp b/src/lib/synergy/unix/AppUtilUnix.cpp index 03aa99943..e1a4f7e06 100644 --- a/src/lib/synergy/unix/AppUtilUnix.cpp +++ b/src/lib/synergy/unix/AppUtilUnix.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/unix/AppUtilUnix.h b/src/lib/synergy/unix/AppUtilUnix.h index 4059c85f6..74e0a3fc7 100644 --- a/src/lib/synergy/unix/AppUtilUnix.h +++ b/src/lib/synergy/unix/AppUtilUnix.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/win32/AppUtilWindows.cpp b/src/lib/synergy/win32/AppUtilWindows.cpp index 0a8ae0003..d2510b040 100644 --- a/src/lib/synergy/win32/AppUtilWindows.cpp +++ b/src/lib/synergy/win32/AppUtilWindows.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synergy/win32/AppUtilWindows.h b/src/lib/synergy/win32/AppUtilWindows.h index b0468c04e..e0dde6b22 100644 --- a/src/lib/synergy/win32/AppUtilWindows.h +++ b/src/lib/synergy/win32/AppUtilWindows.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synwinhk/CMakeLists.txt b/src/lib/synwinhk/CMakeLists.txt index b311e3c78..67ae5ee80 100644 --- a/src/lib/synwinhk/CMakeLists.txt +++ b/src/lib/synwinhk/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2013 Synergy Si Ltd. +# Copyright (C) 2013-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 diff --git a/src/lib/synwinhk/synwinhk.cpp b/src/lib/synwinhk/synwinhk.cpp index 7412755ed..514041b54 100644 --- a/src/lib/synwinhk/synwinhk.cpp +++ b/src/lib/synwinhk/synwinhk.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/lib/synwinhk/synwinhk.h b/src/lib/synwinhk/synwinhk.h index e9c5f4125..b369520c8 100644 --- a/src/lib/synwinhk/synwinhk.h +++ b/src/lib/synwinhk/synwinhk.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or diff --git a/src/micro/CMakeLists.txt b/src/micro/CMakeLists.txt index b243f216b..173dcf53a 100644 --- a/src/micro/CMakeLists.txt +++ b/src/micro/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2012 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/micro/uSynergy.h b/src/micro/uSynergy.h index cedc38783..44534d248 100644 --- a/src/micro/uSynergy.h +++ b/src/micro/uSynergy.h @@ -2,7 +2,7 @@ uSynergy client -- Interface for the embedded Synergy client library version 1.0.0, July 7th, 2012 -Copyright (C) 2012 Synergy Si Ltd. +Copyright (C) 2012-2016 Symless Ltd. Copyright (c) 2012 Alex Evans This software is provided 'as-is', without any express or implied diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 6d47e4541..8812150a2 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2011 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/test/global/TestEventQueue.cpp b/src/test/global/TestEventQueue.cpp index ba85ad412..a19e7a31b 100644 --- a/src/test/global/TestEventQueue.cpp +++ b/src/test/global/TestEventQueue.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/test/global/TestEventQueue.h b/src/test/global/TestEventQueue.h index 97a8d6382..4a2fdb702 100644 --- a/src/test/global/TestEventQueue.h +++ b/src/test/global/TestEventQueue.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/test/global/gmock.h b/src/test/global/gmock.h index 704015527..1c2c7491a 100644 --- a/src/test/global/gmock.h +++ b/src/test/global/gmock.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/test/global/gtest.h b/src/test/global/gtest.h index 15d91c3bd..00161c522 100644 --- a/src/test/global/gtest.h +++ b/src/test/global/gtest.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/test/guitests/src/VersionCheckerTests.cpp b/src/test/guitests/src/VersionCheckerTests.cpp index 0e46dc241..a463ac439 100644 --- a/src/test/guitests/src/VersionCheckerTests.cpp +++ b/src/test/guitests/src/VersionCheckerTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/guitests/src/VersionCheckerTests.h b/src/test/guitests/src/VersionCheckerTests.h index b6f87315b..ba288bff5 100644 --- a/src/test/guitests/src/VersionCheckerTests.h +++ b/src/test/guitests/src/VersionCheckerTests.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/guitests/src/main.cpp b/src/test/guitests/src/main.cpp index b6fe76c91..b94f5786a 100644 --- a/src/test/guitests/src/main.cpp +++ b/src/test/guitests/src/main.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/integtests/CMakeLists.txt b/src/test/integtests/CMakeLists.txt index 70620d0ac..bde35c2fa 100644 --- a/src/test/integtests/CMakeLists.txt +++ b/src/test/integtests/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/test/integtests/Main.cpp b/src/test/integtests/Main.cpp index a5d8fd399..659f6eb10 100644 --- a/src/test/integtests/Main.cpp +++ b/src/test/integtests/Main.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/integtests/arch/ArchInternetTests.cpp b/src/test/integtests/arch/ArchInternetTests.cpp index 4402578a0..e10f497d6 100644 --- a/src/test/integtests/arch/ArchInternetTests.cpp +++ b/src/test/integtests/arch/ArchInternetTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/test/integtests/ipc/IpcTests.cpp b/src/test/integtests/ipc/IpcTests.cpp index c01df60f4..8d5e3b707 100644 --- a/src/test/integtests/ipc/IpcTests.cpp +++ b/src/test/integtests/ipc/IpcTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/integtests/net/NetworkTests.cpp b/src/test/integtests/net/NetworkTests.cpp index 489ccfc33..dd7fb6b4a 100644 --- a/src/test/integtests/net/NetworkTests.cpp +++ b/src/test/integtests/net/NetworkTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/test/integtests/platform/MSWindowsClipboardTests.cpp b/src/test/integtests/platform/MSWindowsClipboardTests.cpp index 8e1b76943..ad91133fe 100644 --- a/src/test/integtests/platform/MSWindowsClipboardTests.cpp +++ b/src/test/integtests/platform/MSWindowsClipboardTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/integtests/platform/MSWindowsKeyStateTests.cpp b/src/test/integtests/platform/MSWindowsKeyStateTests.cpp index ca09c5410..c7a4b666c 100644 --- a/src/test/integtests/platform/MSWindowsKeyStateTests.cpp +++ b/src/test/integtests/platform/MSWindowsKeyStateTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/integtests/platform/OSXClipboardTests.cpp b/src/test/integtests/platform/OSXClipboardTests.cpp index 02d91bf83..e84791d12 100644 --- a/src/test/integtests/platform/OSXClipboardTests.cpp +++ b/src/test/integtests/platform/OSXClipboardTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/integtests/platform/OSXKeyStateTests.cpp b/src/test/integtests/platform/OSXKeyStateTests.cpp index 9f518234c..5a3a4b074 100644 --- a/src/test/integtests/platform/OSXKeyStateTests.cpp +++ b/src/test/integtests/platform/OSXKeyStateTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/integtests/platform/OSXScreenTests.cpp b/src/test/integtests/platform/OSXScreenTests.cpp index 9be59b65d..2118a280f 100644 --- a/src/test/integtests/platform/OSXScreenTests.cpp +++ b/src/test/integtests/platform/OSXScreenTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-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 diff --git a/src/test/integtests/platform/XWindowsClipboardTests.cpp b/src/test/integtests/platform/XWindowsClipboardTests.cpp index c44122843..36a8acad2 100644 --- a/src/test/integtests/platform/XWindowsClipboardTests.cpp +++ b/src/test/integtests/platform/XWindowsClipboardTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/integtests/platform/XWindowsKeyStateTests.cpp b/src/test/integtests/platform/XWindowsKeyStateTests.cpp index d9ae02cc1..62fdd770e 100644 --- a/src/test/integtests/platform/XWindowsKeyStateTests.cpp +++ b/src/test/integtests/platform/XWindowsKeyStateTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/integtests/platform/XWindowsScreenSaverTests.cpp b/src/test/integtests/platform/XWindowsScreenSaverTests.cpp index c6d0a0d09..8bb8d10cd 100644 --- a/src/test/integtests/platform/XWindowsScreenSaverTests.cpp +++ b/src/test/integtests/platform/XWindowsScreenSaverTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/integtests/platform/XWindowsScreenTests.cpp b/src/test/integtests/platform/XWindowsScreenTests.cpp index bf59513f0..ea503c995 100644 --- a/src/test/integtests/platform/XWindowsScreenTests.cpp +++ b/src/test/integtests/platform/XWindowsScreenTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/mock/io/MockStream.h b/src/test/mock/io/MockStream.h index 2a9efe330..74b4d13e6 100644 --- a/src/test/mock/io/MockStream.h +++ b/src/test/mock/io/MockStream.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/mock/ipc/MockIpcServer.h b/src/test/mock/ipc/MockIpcServer.h index 448e8a826..3193722c8 100644 --- a/src/test/mock/ipc/MockIpcServer.h +++ b/src/test/mock/ipc/MockIpcServer.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/test/mock/server/MockConfig.h b/src/test/mock/server/MockConfig.h index f0556bbba..5151fc253 100644 --- a/src/test/mock/server/MockConfig.h +++ b/src/test/mock/server/MockConfig.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/test/mock/server/MockInputFilter.h b/src/test/mock/server/MockInputFilter.h index ce71cc181..f8869f04d 100644 --- a/src/test/mock/server/MockInputFilter.h +++ b/src/test/mock/server/MockInputFilter.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/test/mock/server/MockPrimaryClient.h b/src/test/mock/server/MockPrimaryClient.h index a2eea872c..21c41060e 100644 --- a/src/test/mock/server/MockPrimaryClient.h +++ b/src/test/mock/server/MockPrimaryClient.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/test/mock/server/MockServer.h b/src/test/mock/server/MockServer.h index 22dbd0fca..546a59931 100644 --- a/src/test/mock/server/MockServer.h +++ b/src/test/mock/server/MockServer.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/test/mock/synergy/MockApp.h b/src/test/mock/synergy/MockApp.h index da447a63c..dfe3f9559 100644 --- a/src/test/mock/synergy/MockApp.h +++ b/src/test/mock/synergy/MockApp.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/test/mock/synergy/MockArgParser.h b/src/test/mock/synergy/MockArgParser.h index da4b8fef6..33e1849a3 100644 --- a/src/test/mock/synergy/MockArgParser.h +++ b/src/test/mock/synergy/MockArgParser.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/test/mock/synergy/MockEventQueue.h b/src/test/mock/synergy/MockEventQueue.h index 73d008aa5..fedc226cb 100644 --- a/src/test/mock/synergy/MockEventQueue.h +++ b/src/test/mock/synergy/MockEventQueue.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/mock/synergy/MockKeyMap.h b/src/test/mock/synergy/MockKeyMap.h index 67f18214a..40923121a 100644 --- a/src/test/mock/synergy/MockKeyMap.h +++ b/src/test/mock/synergy/MockKeyMap.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/mock/synergy/MockKeyState.h b/src/test/mock/synergy/MockKeyState.h index 0b05da40a..378233155 100644 --- a/src/test/mock/synergy/MockKeyState.h +++ b/src/test/mock/synergy/MockKeyState.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/mock/synergy/MockScreen.h b/src/test/mock/synergy/MockScreen.h index 81810b516..47f2917ca 100644 --- a/src/test/mock/synergy/MockScreen.h +++ b/src/test/mock/synergy/MockScreen.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2013 Synergy Si Ltd. + * Copyright (C) 2013-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 diff --git a/src/test/unittests/CMakeLists.txt b/src/test/unittests/CMakeLists.txt index 65d05d803..4cacdf936 100644 --- a/src/test/unittests/CMakeLists.txt +++ b/src/test/unittests/CMakeLists.txt @@ -1,5 +1,5 @@ # synergy -- mouse and keyboard sharing utility -# Copyright (C) 2012 Synergy Si Ltd. +# Copyright (C) 2012-2016 Symless Ltd. # Copyright (C) 2009 Nick Bolton # # This package is free software; you can redistribute it and/or diff --git a/src/test/unittests/Main.cpp b/src/test/unittests/Main.cpp index 26fb057c5..16ff80060 100644 --- a/src/test/unittests/Main.cpp +++ b/src/test/unittests/Main.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp index f34e917a8..35e9a1c04 100644 --- a/src/test/unittests/base/StringTests.cpp +++ b/src/test/unittests/base/StringTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si Ltd. + * 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 diff --git a/src/test/unittests/ipc/IpcLogOutputterTests.cpp b/src/test/unittests/ipc/IpcLogOutputterTests.cpp index 1b9133214..c512a6afc 100644 --- a/src/test/unittests/ipc/IpcLogOutputterTests.cpp +++ b/src/test/unittests/ipc/IpcLogOutputterTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Ltd. + * 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 diff --git a/src/test/unittests/platform/OSXKeyStateTests.cpp b/src/test/unittests/platform/OSXKeyStateTests.cpp index c5190e833..5892e6c32 100644 --- a/src/test/unittests/platform/OSXKeyStateTests.cpp +++ b/src/test/unittests/platform/OSXKeyStateTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/unittests/synergy/ArgParserTests.cpp b/src/test/unittests/synergy/ArgParserTests.cpp index a564e188c..25a5618f2 100644 --- a/src/test/unittests/synergy/ArgParserTests.cpp +++ b/src/test/unittests/synergy/ArgParserTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/test/unittests/synergy/ClientArgsParsingTests.cpp b/src/test/unittests/synergy/ClientArgsParsingTests.cpp index 8a0f57d85..4a215b062 100644 --- a/src/test/unittests/synergy/ClientArgsParsingTests.cpp +++ b/src/test/unittests/synergy/ClientArgsParsingTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/test/unittests/synergy/ClipboardChunkTests.cpp b/src/test/unittests/synergy/ClipboardChunkTests.cpp index 3666fe450..2460778a7 100644 --- a/src/test/unittests/synergy/ClipboardChunkTests.cpp +++ b/src/test/unittests/synergy/ClipboardChunkTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si Inc. + * 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 diff --git a/src/test/unittests/synergy/ClipboardTests.cpp b/src/test/unittests/synergy/ClipboardTests.cpp index e150f6b3d..a37a3f6a0 100644 --- a/src/test/unittests/synergy/ClipboardTests.cpp +++ b/src/test/unittests/synergy/ClipboardTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/unittests/synergy/DeprecatedArgsParsingTests.cpp b/src/test/unittests/synergy/DeprecatedArgsParsingTests.cpp index 674311e47..4fb49bff2 100644 --- a/src/test/unittests/synergy/DeprecatedArgsParsingTests.cpp +++ b/src/test/unittests/synergy/DeprecatedArgsParsingTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy Si, Inc. + * 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 diff --git a/src/test/unittests/synergy/GenericArgsParsingTests.cpp b/src/test/unittests/synergy/GenericArgsParsingTests.cpp index f41c3bf4b..8fa7d753a 100644 --- a/src/test/unittests/synergy/GenericArgsParsingTests.cpp +++ b/src/test/unittests/synergy/GenericArgsParsingTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 diff --git a/src/test/unittests/synergy/KeyStateTests.cpp b/src/test/unittests/synergy/KeyStateTests.cpp index 1987881a2..146144b8d 100644 --- a/src/test/unittests/synergy/KeyStateTests.cpp +++ b/src/test/unittests/synergy/KeyStateTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2012 Synergy Si Ltd. + * Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2011 Nick Bolton * * This package is free software; you can redistribute it and/or diff --git a/src/test/unittests/synergy/ServerArgsParsingTests.cpp b/src/test/unittests/synergy/ServerArgsParsingTests.cpp index 1414fd13a..5ae18b94b 100644 --- a/src/test/unittests/synergy/ServerArgsParsingTests.cpp +++ b/src/test/unittests/synergy/ServerArgsParsingTests.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2014 Synergy Si, Inc. + * 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 From d34a59a984872c35697292ec260e8949b9149373 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Wed, 7 Sep 2016 15:31:46 +0100 Subject: [PATCH 287/572] Update company name in win32 installer --- src/setup/win32/Include.wxi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setup/win32/Include.wxi b/src/setup/win32/Include.wxi index bd7fc0d91..62f92ac7c 100644 --- a/src/setup/win32/Include.wxi +++ b/src/setup/win32/Include.wxi @@ -2,7 +2,7 @@ - + From 11d5691bca5cf96b06a5dcfa12c59ac29e1d3688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Picard?= Date: Fri, 17 Jul 2015 23:55:36 +0200 Subject: [PATCH 288/572] Fixed memory leak in IpcReader.cpp --- src/gui/src/IpcReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/src/IpcReader.cpp b/src/gui/src/IpcReader.cpp index 227fc715e..a2a78de74 100644 --- a/src/gui/src/IpcReader.cpp +++ b/src/gui/src/IpcReader.cpp @@ -65,7 +65,7 @@ void IpcReader::read() char* data = new char[len]; readStream(data, len); QString line = QString::fromUtf8(data, len); - delete data; + delete[] data; readLogLine(line); } From 3e1a86c3c1873be030d3ebb0764fefee74077a7a Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Thu, 1 Sep 2016 11:15:58 +0100 Subject: [PATCH 289/572] #5502 Reverted disabled sending clipboard on inactive grab This reverts commit 2ad4b896f3bd98aeff6021206ce4ba22a45a542c. --- src/lib/client/Client.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 3894cc36c..9ad878eea 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -704,6 +704,14 @@ Client::handleClipboardGrabbed(const Event& event, void*) m_ownClipboard[info->m_id] = true; m_sentClipboard[info->m_id] = false; m_timeClipboard[info->m_id] = 0; + + // if we're not the active screen then send the clipboard now, + // otherwise we'll wait until we leave. + Clipboard clipboard; + if (!m_active) { + fillClipboard(info->m_id, &clipboard); + sendClipboard(info->m_id, &clipboard); + } } void From 82043ca435e7f199294aae40390f156c02afe8e6 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Thu, 1 Sep 2016 11:28:32 +0100 Subject: [PATCH 290/572] #4740 Used the same chunk size for secure and non-secure sockets --- src/lib/synergy/ArgParser.cpp | 1 - src/lib/synergy/StreamChunker.cpp | 19 +++---------------- src/lib/synergy/StreamChunker.h | 2 -- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/lib/synergy/ArgParser.cpp b/src/lib/synergy/ArgParser.cpp index f4ce89840..431a91b73 100644 --- a/src/lib/synergy/ArgParser.cpp +++ b/src/lib/synergy/ArgParser.cpp @@ -326,7 +326,6 @@ ArgParser::parseGenericArgs(int argc, const char* const* argv, int& i) } else if (isArg(i, argc, argv, NULL, "--enable-crypto")) { argsBase().m_enableCrypto = true; - StreamChunker::updateChunkSize(true); } else if (isArg(i, argc, argv, NULL, "--profile-dir", 1)) { argsBase().m_profileDirectory = argv[++i]; diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index 651d1a264..a2789f78c 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -37,10 +37,8 @@ using namespace std; -#define SOCKET_CHUNK_SIZE 512 * 1024; // 512kb -#define SECURE_SOCKET_CHUNK_SIZE 2 * 1024; // 2kb +#define CHUNK_SIZE 512 * 1024; // 512kb -size_t StreamChunker::s_chunkSize = SOCKET_CHUNK_SIZE; bool StreamChunker::s_isChunkingClipboard = false; bool StreamChunker::s_interruptClipboard = false; bool StreamChunker::s_isChunkingFile = false; @@ -73,7 +71,7 @@ StreamChunker::sendFile( // send chunk messages with a fixed chunk size size_t sentLength = 0; - size_t chunkSize = s_chunkSize; + size_t chunkSize = CHUNK_SIZE; Stopwatch sendStopwatch; sendStopwatch.start(); file.seekg (0, std::ios::beg); @@ -141,7 +139,7 @@ StreamChunker::sendClipboard( // send clipboard chunk with a fixed size size_t sentLength = 0; - size_t chunkSize = s_chunkSize; + size_t chunkSize = CHUNK_SIZE; Stopwatch sendStopwatch; sendStopwatch.start(); @@ -189,17 +187,6 @@ StreamChunker::sendClipboard( s_isChunkingClipboard = false; } -void -StreamChunker::updateChunkSize(bool useSecureSocket) -{ - if (useSecureSocket) { - s_chunkSize = SECURE_SOCKET_CHUNK_SIZE; - } - else { - s_chunkSize = SOCKET_CHUNK_SIZE; - } -} - void StreamChunker::interruptFile() { diff --git a/src/lib/synergy/StreamChunker.h b/src/lib/synergy/StreamChunker.h index 7ae085f46..c9ac5af18 100644 --- a/src/lib/synergy/StreamChunker.h +++ b/src/lib/synergy/StreamChunker.h @@ -36,12 +36,10 @@ class StreamChunker { UInt32 sequence, IEventQueue* events, void* eventTarget); - static void updateChunkSize(bool useSecureSocket); static void interruptFile(); static void setClipboardInterrupt(bool interrupt); private: - static size_t s_chunkSize; static bool s_isChunkingClipboard; static bool s_interruptClipboard; static bool s_isChunkingFile; From 4ad2c6b10d32b6f378ed34813644242f05f76d08 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Thu, 1 Sep 2016 11:46:52 +0100 Subject: [PATCH 291/572] #4740 Moved clipboard transfering back into main thread on client --- src/lib/client/Client.cpp | 114 ++++++++-------------------------------------- src/lib/client/Client.h | 7 +-- 2 files changed, 19 insertions(+), 102 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 9ad878eea..2d687ee37 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -73,11 +73,7 @@ Client::Client( m_writeToDropDirThread(NULL), m_socket(NULL), m_useSecureNetwork(false), - m_args(args), - m_sendClipboardThread(NULL), - m_mutex(NULL), - m_condData(false), - m_condVar(NULL) + m_args(args) { assert(m_socketFactory != NULL); assert(m_screen != NULL); @@ -109,8 +105,6 @@ Client::Client( LOG((CLOG_NOTE "crypto disabled because of ns plugin not available")); } } - m_mutex = new Mutex(); - m_condVar = new CondVar(m_mutex, m_condData); } Client::~Client() @@ -129,8 +123,6 @@ Client::~Client() cleanupConnecting(); cleanupConnection(); delete m_socketFactory; - delete m_condVar; - delete m_mutex; } void @@ -270,47 +262,15 @@ Client::leave() { m_active = false; - if (m_sendClipboardThread != NULL) { - StreamChunker::setClipboardInterrupt(true); - m_sendClipboardThread->wait(); - delete m_sendClipboardThread; - m_sendClipboardThread = NULL; - StreamChunker::setClipboardInterrupt(false); - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - if (m_ownClipboard[id]) { - m_sentClipboard[id] = false; - } - } - } + m_screen->leave(); - if (m_sendClipboardThread == NULL) { - m_condData = false; - m_sendClipboardThread = new Thread( - new TMethodJob( - this, - &Client::sendClipboardThread, - NULL)); - // Bug #4735 - we can't leave() until fillClipboard()s all finish - Stopwatch timer(false); - m_mutex->lock(); - while (!m_condData) { - if (!m_condVar->wait(timer, 0.5)) { - LOG((CLOG_WARN "timed out %fs waiting for clipboard fill", - (double) timer.getTime())); - break; - } - LOG((CLOG_DEBUG1 "leave %fs elapsed", (double) timer.getTime())); + // send clipboards that we own and that have changed + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_ownClipboard[id]) { + sendClipboard(id); } - m_mutex->unlock(); } - m_screen->leave(); - - if (!m_receivedFileData.empty()) { - m_receivedFileData.clear(); - LOG((CLOG_DEBUG "file transmission interrupted")); - } - return true; } @@ -410,20 +370,9 @@ Client::getName() const } void -Client::fillClipboard(ClipboardID id, Clipboard *clipboard) -{ - assert(m_screen != NULL); - assert(m_server != NULL); - - if (clipboard->open(m_timeClipboard[id])) { - clipboard->close(); - } - m_screen->getClipboard(id, clipboard); -} - -void -Client::sendClipboard(ClipboardID id, Clipboard *clipboard) +Client::sendClipboard(ClipboardID id) { + // note -- m_mutex must be locked on entry assert(m_screen != NULL); assert(m_server != NULL); @@ -431,21 +380,26 @@ Client::sendClipboard(ClipboardID id, Clipboard *clipboard) // clipboard time before getting the data from the screen // as the screen may detect an unchanged clipboard and // avoid copying the data. + Clipboard clipboard; + if (clipboard.open(m_timeClipboard[id])) { + clipboard.close(); + } + m_screen->getClipboard(id, &clipboard); // check time if (m_timeClipboard[id] == 0 || - clipboard->getTime() != m_timeClipboard[id]) { + clipboard.getTime() != m_timeClipboard[id]) { // save new time - m_timeClipboard[id] = clipboard->getTime(); + m_timeClipboard[id] = clipboard.getTime(); // marshall the data - String data = clipboard->marshall(); + String data = clipboard.marshall(); // save and send data if different or not yet sent if (!m_sentClipboard[id] || data != m_dataClipboard[id]) { m_sentClipboard[id] = true; m_dataClipboard[id] = data; - m_server->onClipboardChanged(id, clipboard); + m_server->onClipboardChanged(id, &clipboard); } } } @@ -707,10 +661,8 @@ Client::handleClipboardGrabbed(const Event& event, void*) // if we're not the active screen then send the clipboard now, // otherwise we'll wait until we leave. - Clipboard clipboard; if (!m_active) { - fillClipboard(info->m_id, &clipboard); - sendClipboard(info->m_id, &clipboard); + sendClipboard(info->m_id); } } @@ -797,36 +749,6 @@ Client::onFileRecieveCompleted() } } -void -Client::sendClipboardThread(void * data) -{ - Stopwatch timer(false); - Clipboard clipboard[kClipboardEnd]; - // fill clipboards that we own and that have changed - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - if (m_ownClipboard[id]) { - fillClipboard(id, &clipboard[id]); - } - } - LOG((CLOG_DEBUG1 "fill took %fs, signaling", (double) timer.getTime())); - - // signal that fill is done - m_mutex->lock(); - m_condData = true; - m_condVar->signal(); - m_mutex->unlock(); - - // send clipboards that we own and that have changed - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - if (m_ownClipboard[id]) { - sendClipboard(id, &clipboard[id]); - } - } - - m_sendClipboardThread = NULL; - LOG((CLOG_DEBUG1 "send took %fs", (double) timer.getTime())); -} - void Client::handleStopRetry(const Event&, void*) { diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index 27eafde57..0e4e5a66d 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -163,8 +163,7 @@ class Client : public IClient, public INode { virtual String getName() const; private: - void fillClipboard(ClipboardID, Clipboard*); - void sendClipboard(ClipboardID, Clipboard*); + void sendClipboard(ClipboardID); void sendEvent(Event::Type, void*); void sendConnectionFailedEvent(const char* msg); void sendFileChunk(const void* data); @@ -224,8 +223,4 @@ class Client : public IClient, public INode { TCPSocket* m_socket; bool m_useSecureNetwork; ClientArgs& m_args; - Thread* m_sendClipboardThread; - Mutex* m_mutex; - bool m_condData; - CondVar* m_condVar; }; From e32402b5c62eca7862b7ff9d2d065272b2826478 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Thu, 1 Sep 2016 11:51:12 +0100 Subject: [PATCH 292/572] #4740 Moved clipboard transfering back into main thread on server --- src/lib/server/Server.cpp | 35 +++++------------------------------ src/lib/server/Server.h | 5 ----- 2 files changed, 5 insertions(+), 35 deletions(-) diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 6135d1be3..cdec86f42 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -93,8 +93,7 @@ Server::Server( m_ignoreFileTransfer(false), m_enableDragDrop(enableDragDrop), m_sendDragInfoThread(NULL), - m_waitDragInfoThread(true), - m_sendClipboardThread(NULL) + m_waitDragInfoThread(true) { // must have a primary client and it must have a canonical name assert(m_primaryClient != NULL); @@ -496,18 +495,6 @@ Server::switchScreen(BaseClientProxy* dst, } } - // if already sending clipboard, we need to interupt it, otherwise - // clipboard data could be corrupted on the other side - // interrupt before switch active, as send clipboard uses active - // client proxy, which would cause race condition - if (m_sendClipboardThread != NULL) { - StreamChunker::setClipboardInterrupt(true); - m_sendClipboardThread->wait(); - delete m_sendClipboardThread; - m_sendClipboardThread = NULL; - StreamChunker::setClipboardInterrupt(false); - } - // cut over m_active = dst; @@ -518,13 +505,11 @@ Server::switchScreen(BaseClientProxy* dst, m_active->enter(x, y, m_seqNum, m_primaryClient->getToggleMask(), forScreensaver); - + // send the clipboard data to new active screen - m_sendClipboardThread = new Thread( - new TMethodJob( - this, - &Server::sendClipboardThread, - NULL)); + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_active->setClipboard(id, &m_clipboards[id].m_clipboard); + } Server::SwitchToScreenInfo* info = Server::SwitchToScreenInfo::alloc(m_active->getName()); @@ -1864,16 +1849,6 @@ Server::sendDragInfo(BaseClientProxy* newScreen) } } -void -Server::sendClipboardThread(void*) -{ - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_active->setClipboard(id, &m_clipboards[id].m_clipboard); - } - - m_sendClipboardThread = NULL; -} - void Server::onMouseMoveSecondary(SInt32 dx, SInt32 dy) { diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index 4defc7b29..610718c4c 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -367,9 +367,6 @@ class Server : public INode { // send drag info to new client screen void sendDragInfo(BaseClientProxy* newScreen); - // thread funciton for sending clipboard - void sendClipboardThread(void*); - public: bool m_mock; @@ -481,6 +478,4 @@ class Server : public INode { bool m_waitDragInfoThread; ClientListener* m_clientListener; - - Thread* m_sendClipboardThread; }; From 95464d97cf5f2c746201368272b73176b76be287 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 24 Aug 2016 15:14:12 +0100 Subject: [PATCH 293/572] Conflicts: src/lib/client/Client.cpp src/lib/net/TCPSocketFactory.cpp --- src/lib/base/EventTypes.cpp | 1 + src/lib/base/EventTypes.h | 9 +++++++++ src/lib/client/Client.cpp | 17 ++++++++++++----- src/lib/net/TCPSocket.cpp | 4 ++-- src/lib/net/TCPSocket.h | 12 +++++++----- src/lib/plugin/ns/SecureSocket.cpp | 31 +++++++++++++++++++++++++++++++ src/lib/plugin/ns/SecureSocket.h | 7 +++++++ 7 files changed, 69 insertions(+), 12 deletions(-) diff --git a/src/lib/base/EventTypes.cpp b/src/lib/base/EventTypes.cpp index a25bc2920..2bb0db1b9 100644 --- a/src/lib/base/EventTypes.cpp +++ b/src/lib/base/EventTypes.cpp @@ -82,6 +82,7 @@ REGISTER_EVENT(IpcServerProxy, messageReceived) // REGISTER_EVENT(IDataSocket, connected) +REGISTER_EVENT(IDataSocket, secureConnected) REGISTER_EVENT(IDataSocket, connectionFailed) // diff --git a/src/lib/base/EventTypes.h b/src/lib/base/EventTypes.h index d22bc7296..7be8827b9 100644 --- a/src/lib/base/EventTypes.h +++ b/src/lib/base/EventTypes.h @@ -230,6 +230,7 @@ class IDataSocketEvents : public EventTypes { public: IDataSocketEvents() : m_connected(Event::kUnknown), + m_secureConnected(Event::kUnknown), m_connectionFailed(Event::kUnknown) { } //! @name accessors @@ -241,6 +242,13 @@ class IDataSocketEvents : public EventTypes { event when a remote connection has been established. */ Event::Type connected(); + + //! Get secure connected event type + /*! + Returns the secure socket connected event type. A secure socket sends + this event when a remote connection has been established. + */ + Event::Type secureConnected(); //! Get connection failed event type /*! @@ -254,6 +262,7 @@ class IDataSocketEvents : public EventTypes { private: Event::Type m_connected; + Event::Type m_secureConnected; Event::Type m_connectionFailed; }; diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 2d687ee37..5c716b462 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -435,10 +435,19 @@ Client::setupConnecting() { assert(m_stream != NULL); - m_events->adoptHandler(m_events->forIDataSocket().connected(), - m_stream->getEventTarget(), - new TMethodEventJob(this, + if (m_args.m_enableCrypto) { + m_events->adoptHandler(m_events->forIDataSocket().secureConnected(), + m_stream->getEventTarget(), + new TMethodEventJob(this, &Client::handleConnected)); + } + else { + m_events->adoptHandler(m_events->forIDataSocket().connected(), + m_stream->getEventTarget(), + new TMethodEventJob(this, + &Client::handleConnected)); + } + m_events->adoptHandler(m_events->forIDataSocket().connectionFailed(), m_stream->getEventTarget(), new TMethodEventJob(this, @@ -589,8 +598,6 @@ Client::handleConnected(const Event&, void*) m_sentClipboard[id] = false; m_timeClipboard[id] = 0; } - - m_socket->secureConnect(); } void diff --git a/src/lib/net/TCPSocket.cpp b/src/lib/net/TCPSocket.cpp index 326a63acc..e0ec92a77 100644 --- a/src/lib/net/TCPSocket.cpp +++ b/src/lib/net/TCPSocket.cpp @@ -39,9 +39,9 @@ TCPSocket::TCPSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer) : IDataSocket(events), + m_events(events), m_mutex(), m_flushed(&m_mutex, true), - m_events(events), m_socketMultiplexer(socketMultiplexer) { try { @@ -56,10 +56,10 @@ TCPSocket::TCPSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer) TCPSocket::TCPSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, ArchSocket socket) : IDataSocket(events), + m_events(events), m_mutex(), m_socket(socket), m_flushed(&m_mutex, true), - m_events(events), m_socketMultiplexer(socketMultiplexer) { assert(m_socket != NULL); diff --git a/src/lib/net/TCPSocket.h b/src/lib/net/TCPSocket.h index 3dca4cada..a84dace05 100644 --- a/src/lib/net/TCPSocket.h +++ b/src/lib/net/TCPSocket.h @@ -58,6 +58,9 @@ class TCPSocket : public IDataSocket { // IDataSocket overrides virtual void connect(const NetworkAddress&); + + virtual ISocketMultiplexerJob* + newJob(); virtual void secureConnect() {} virtual void secureAccept() {} virtual void setFingerprintFilename(String& f) {} @@ -71,8 +74,7 @@ class TCPSocket : public IDataSocket { virtual int secureWrite(const void*, int, int& ) { return 0; } void setJob(ISocketMultiplexerJob*); - ISocketMultiplexerJob* - newJob(); + bool isReadable() { return m_readable; } bool isWritable() { return m_writable; } @@ -99,14 +101,14 @@ class TCPSocket : public IDataSocket { protected: bool m_readable; bool m_writable; - + bool m_connected; + IEventQueue* m_events; + private: Mutex m_mutex; ArchSocket m_socket; StreamBuffer m_inputBuffer; StreamBuffer m_outputBuffer; CondVar m_flushed; - bool m_connected; - IEventQueue* m_events; SocketMultiplexer* m_socketMultiplexer; }; diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 0e5a03efb..979e8616f 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -18,6 +18,7 @@ #include "SecureSocket.h" #include "net/TSocketMultiplexerMethodJob.h" +#include "base/TMethodEventJob.h" #include "net/TCPSocket.h" #include "mt/Lock.h" #include "arch/XArch.h" @@ -100,6 +101,29 @@ SecureSocket::close() TCPSocket::close(); } +void +SecureSocket::connect(const NetworkAddress& addr) +{ + m_events->adoptHandler(m_events->forIDataSocket().connected(), + getEventTarget(), + new TMethodEventJob(this, + &SecureSocket::handleTCPConnected)); + + TCPSocket::connect(addr); +} + +ISocketMultiplexerJob* +SecureSocket::newJob() +{ + // after TCP connection is established, SecureSocket will pick up + // connected event and do secureConnect + if (m_connected && !m_secureReady) { + return NULL; + } + + return TCPSocket::newJob(); +} + void SecureSocket::secureConnect() { @@ -609,6 +633,7 @@ SecureSocket::serviceConnect(ISocketMultiplexerJob* job, // If status > 0, success if (status > 0) { + sendEvent(m_events->forIDataSocket().secureConnected()); return newJob(); } @@ -714,3 +739,9 @@ SecureSocket::showSecureConnectInfo() } return; } + +void +SecureSocket::handleTCPConnected(const Event& event, void*) +{ + secureConnect(); +} diff --git a/src/lib/plugin/ns/SecureSocket.h b/src/lib/plugin/ns/SecureSocket.h index ef3f63027..5be68f7de 100644 --- a/src/lib/plugin/ns/SecureSocket.h +++ b/src/lib/plugin/ns/SecureSocket.h @@ -41,6 +41,11 @@ class SecureSocket : public TCPSocket { // ISocket overrides void close(); + // IDataSocket overrides + virtual void connect(const NetworkAddress&); + + ISocketMultiplexerJob* + newJob(); void secureConnect(); void secureAccept(); bool isReady() const { return m_secureReady; } @@ -80,6 +85,8 @@ class SecureSocket : public TCPSocket { void showSecureConnectInfo(); void showSecureLibInfo(); void showSecureCipherInfo(); + + void handleTCPConnected(const Event& event, void*); private: Ssl* m_ssl; From e1be67cfb61256ae1a8cf9acc8e61c89dcbd77b7 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 24 Aug 2016 15:17:00 +0100 Subject: [PATCH 294/572] Fixed indentations Conflicts: src/lib/client/Client.h --- src/lib/client/Client.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index 0e4e5a66d..20e44a6a1 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -57,11 +57,10 @@ class Client : public IClient, public INode { as its name and \p address as the server's address and \p factory to create the socket. \p screen is the local screen. */ - Client(IEventQueue* events, - const String& name, const NetworkAddress& address, - ISocketFactory* socketFactory, - synergy::Screen* screen, - ClientArgs& args); + Client(IEventQueue* events, const String& name, + const NetworkAddress& address, ISocketFactory* socketFactory, + synergy::Screen* screen, ClientArgs const& args); + ~Client(); //! @name manipulators @@ -94,6 +93,7 @@ class Client : public IClient, public INode { //! Send dragging file information back to server void sendDragInfo(UInt32 fileCount, String& info, size_t size); + //@} //! @name accessors From 3df75f960123368281f5016d709e747edcdcb94b Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 24 Aug 2016 16:28:29 +0100 Subject: [PATCH 295/572] Conflicts: src/lib/server/ClientListener.cpp --- src/lib/base/EventTypes.cpp | 1 + src/lib/base/EventTypes.h | 11 ++++++- src/lib/plugin/ns/SecureSocket.cpp | 1 + src/lib/server/ClientListener.cpp | 67 +++++++++++++++++++++----------------- src/lib/server/ClientListener.h | 1 + 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/lib/base/EventTypes.cpp b/src/lib/base/EventTypes.cpp index 2bb0db1b9..2fd9d56dd 100644 --- a/src/lib/base/EventTypes.cpp +++ b/src/lib/base/EventTypes.cpp @@ -108,6 +108,7 @@ REGISTER_EVENT(OSXScreen, confirmSleep) // ClientListener // +REGISTER_EVENT(ClientListener, accepted) REGISTER_EVENT(ClientListener, connected) // diff --git a/src/lib/base/EventTypes.h b/src/lib/base/EventTypes.h index 7be8827b9..294096242 100644 --- a/src/lib/base/EventTypes.h +++ b/src/lib/base/EventTypes.h @@ -337,11 +337,19 @@ class OSXScreenEvents : public EventTypes { class ClientListenerEvents : public EventTypes { public: ClientListenerEvents() : + m_accepted(Event::kUnknown), m_connected(Event::kUnknown) { } //! @name accessors //@{ - + + //! Get accepted event type + /*! + Returns the accepted event type. This is sent whenever a server + accepts a client. + */ + Event::Type accepted(); + //! Get connected event type /*! Returns the connected event type. This is sent whenever a @@ -352,6 +360,7 @@ class ClientListenerEvents : public EventTypes { //@} private: + Event::Type m_accepted; Event::Type m_connected; }; diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 979e8616f..6e0e8f662 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -662,6 +662,7 @@ SecureSocket::serviceAccept(ISocketMultiplexerJob* job, // If status > 0, success if (status > 0) { + sendEvent(m_events->forClientListener().accepted()); return newJob(); } diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index 078b6013c..11c6c8cf3 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -56,6 +56,12 @@ ClientListener::ClientListener(const NetworkAddress& address, m_listen = m_socketFactory->createListen(m_useSecureNetwork); + // setup event handler + m_events->adoptHandler(m_events->forIListenSocket().connecting(), + m_listen, + new TMethodEventJob(this, + &ClientListener::handleClientConnecting)); + // bind listen address LOG((CLOG_DEBUG1 "binding listen socket")); m_listen->bind(address); @@ -71,11 +77,6 @@ ClientListener::ClientListener(const NetworkAddress& address, throw; } LOG((CLOG_DEBUG1 "listening for clients")); - - // setup event handler - m_events->adoptHandler(m_events->forIListenSocket().connecting(), m_listen, - new TMethodEventJob(this, - &ClientListener::handleClientConnecting)); } ClientListener::~ClientListener() @@ -141,41 +142,46 @@ ClientListener::handleClientConnecting(const Event&, void*) if (socket == NULL) { return; } - - LOG((CLOG_NOTE "accepted client connection")); - - if (m_useSecureNetwork) { - LOG((CLOG_DEBUG2 "attempting sercure Connection")); - while (!socket->isReady()) { - if (socket->isFatal()) { - m_listen->deleteSocket(socket); - return; - } - LOG((CLOG_DEBUG2 "retrying sercure Connection")); - ARCH->sleep(.5f); - } + + m_events->adoptHandler(m_events->forClientListener().accepted(), + stream->getEventTarget(), + new TMethodEventJob(this, + &ClientListener::handleClientAccepted, stream)); + + // When using non SSL, server accepts clients immediately, while SSL + // has to call secure accept which may require retry + if (!m_args.m_enableCrypto) { + m_events->addEvent(Event(m_events->forClientListener().accepted(), + stream)); } +} - LOG((CLOG_DEBUG2 "sercure Connection established:%d")); +void +ClientListener::handleClientAccepted(const Event&, void* vstream) +{ + LOG((CLOG_NOTE "accepted client connection")); - synergy::IStream* stream = socket; + synergy::IStream* stream = reinterpret_cast(vstream); + IDataSocket* socket = dynamic_cast(stream); + // filter socket messages, including a packetizing filter - bool adopt = !m_useSecureNetwork; - stream = new PacketStreamFilter(m_events, stream, adopt); - + stream = new PacketStreamFilter(m_events, socket); assert(m_server != NULL); // create proxy for unknown client ClientProxyUnknown* client = new ClientProxyUnknown(stream, 30.0, m_server, m_events); + m_newClients.insert(client); // watch for events from unknown client - m_events->adoptHandler(m_events->forClientProxyUnknown().success(), client, - new TMethodEventJob(this, - &ClientListener::handleUnknownClient, client)); - m_events->adoptHandler(m_events->forClientProxyUnknown().failure(), client, - new TMethodEventJob(this, - &ClientListener::handleUnknownClient, client)); + m_events->adoptHandler(m_events->forClientProxyUnknown().success(), + client, + new TMethodEventJob(this, + &ClientListener::handleUnknownClient, client)); + m_events->adoptHandler(m_events->forClientProxyUnknown().failure(), + client, + new TMethodEventJob(this, + &ClientListener::handleUnknownClient, client)); } void @@ -193,7 +199,8 @@ ClientListener::handleUnknownClient(const Event&, void* vclient) if (client != NULL) { // handshake was successful m_waitingClients.push_back(client); - m_events->addEvent(Event(m_events->forClientListener().connected(), this)); + m_events->addEvent(Event(m_events->forClientListener().connected(), + this)); // watch for client to disconnect while it's in our queue m_events->adoptHandler(m_events->forClientProxy().disconnected(), client, diff --git a/src/lib/server/ClientListener.h b/src/lib/server/ClientListener.h index 7aefe7f47..7674386ce 100644 --- a/src/lib/server/ClientListener.h +++ b/src/lib/server/ClientListener.h @@ -69,6 +69,7 @@ class ClientListener { private: // client connection event handlers void handleClientConnecting(const Event&, void*); + void handleClientAccepted(const Event&, void*); void handleUnknownClient(const Event&, void*); void handleClientDisconnected(const Event&, void*); From 436e333f6f3b1fdf0cd0765587c3850f21c47a12 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 24 Aug 2016 16:28:41 +0100 Subject: [PATCH 296/572] Fixed indentations --- src/lib/plugin/ns/SecureListenSocket.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib/plugin/ns/SecureListenSocket.cpp b/src/lib/plugin/ns/SecureListenSocket.cpp index 0dd96f32c..b4c08646e 100644 --- a/src/lib/plugin/ns/SecureListenSocket.cpp +++ b/src/lib/plugin/ns/SecureListenSocket.cpp @@ -61,11 +61,10 @@ SecureListenSocket::accept() setListeningJob(); } - String certificateFilename = synergy::string::sprintf( - "%s/%s/%s", - ARCH->getProfileDirectory().c_str(), - s_certificateDir, - s_certificateFilename); + String certificateFilename = synergy::string::sprintf("%s/%s/%s", + ARCH->getProfileDirectory().c_str(), + s_certificateDir, + s_certificateFilename); bool loaded = socket->loadCertificates(certificateFilename); if (!loaded) { From 61b489ab3de4c7befe841c91f34c415d40262e71 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 24 Aug 2016 17:09:42 +0100 Subject: [PATCH 297/572] Refactor write and read into functions --- src/lib/net/TCPSocket.cpp | 339 ++++++++++++++++++++++++---------------------- src/lib/net/TCPSocket.h | 8 ++ 2 files changed, 185 insertions(+), 162 deletions(-) diff --git a/src/lib/net/TCPSocket.cpp b/src/lib/net/TCPSocket.cpp index e0ec92a77..cf5a81d0d 100644 --- a/src/lib/net/TCPSocket.cpp +++ b/src/lib/net/TCPSocket.cpp @@ -323,6 +323,179 @@ TCPSocket::init() } } +TCPSocket::EJobResult +TCPSocket::doRead() +{ + try { + static UInt8 buffer[4096]; + memset(buffer, 0, sizeof(buffer)); + int bytesRead = 0; + int status = 0; + + if (isSecure()) { + if (isSecureReady()) { + status = secureRead(buffer, sizeof(buffer), bytesRead); + if (status < 0) { + return kBreak; + } + else if (status == 0) { + return kNew; + } + } + else { + return kRetry; + } + } + else { + bytesRead = (int) ARCH->readSocket(m_socket, buffer, sizeof(buffer)); + } + + if (bytesRead > 0) { + bool wasEmpty = (m_inputBuffer.getSize() == 0); + + // slurp up as much as possible + do { + m_inputBuffer.write(buffer, bytesRead); + + if (isSecure() && isSecureReady()) { + status = secureRead(buffer, sizeof(buffer), bytesRead); + if (status < 0) { + return kBreak; + } + } + else { + bytesRead = (int) ARCH->readSocket(m_socket, buffer, sizeof(buffer)); + } + + } while (bytesRead > 0 || status > 0); + + // send input ready if input buffer was empty + if (wasEmpty) { + sendEvent(m_events->forIStream().inputReady()); + } + } + else { + // remote write end of stream hungup. our input side + // has therefore shutdown but don't flush our buffer + // since there's still data to be read. + sendEvent(m_events->forIStream().inputShutdown()); + if (!m_writable && m_inputBuffer.getSize() == 0) { + sendEvent(m_events->forISocket().disconnected()); + m_connected = false; + } + m_readable = false; + return kNew; + } + } + catch (XArchNetworkDisconnected&) { + // stream hungup + sendEvent(m_events->forISocket().disconnected()); + onDisconnected(); + return kNew; + } + catch (XArchNetwork& e) { + // ignore other read error + LOG((CLOG_WARN "error reading socket: %s", e.what())); + } + + return kRetry; +} + +TCPSocket::EJobResult +TCPSocket::doWrite() +{ + static bool s_retry = false; + static int s_retrySize = 0; + static void* s_staticBuffer = NULL; + + try { + // write data + int bufferSize = 0; + int bytesWrote = 0; + int status = 0; + + if (s_retry) { + bufferSize = s_retrySize; + } + else { + bufferSize = m_outputBuffer.getSize(); + s_staticBuffer = malloc(bufferSize); + memcpy(s_staticBuffer, m_outputBuffer.peek(bufferSize), bufferSize); + } + + if (bufferSize == 0) { + return kRetry; + } + + if (isSecure()) { + if (isSecureReady()) { + status = secureWrite(s_staticBuffer, bufferSize, bytesWrote); + if (status > 0) { + s_retry = false; + bufferSize = 0; + free(s_staticBuffer); + s_staticBuffer = NULL; + } + else if (status < 0) { + return kBreak; + } + else if (status == 0) { + s_retry = true; + s_retrySize = bufferSize; + return kNew; + } + } + else { + return kRetry; + } + } + else { + bytesWrote = (UInt32)ARCH->writeSocket(m_socket, s_staticBuffer, bufferSize); + bufferSize = 0; + free(s_staticBuffer); + s_staticBuffer = NULL; + } + + // discard written data + if (bytesWrote > 0) { + m_outputBuffer.pop(bytesWrote); + if (m_outputBuffer.getSize() == 0) { + sendEvent(m_events->forIStream().outputFlushed()); + m_flushed = true; + m_flushed.broadcast(); + return kNew; + } + } + } + catch (XArchNetworkShutdown&) { + // remote read end of stream hungup. our output side + // has therefore shutdown. + onOutputShutdown(); + sendEvent(m_events->forIStream().outputShutdown()); + if (!m_readable && m_inputBuffer.getSize() == 0) { + sendEvent(m_events->forISocket().disconnected()); + m_connected = false; + } + return kNew; + } + catch (XArchNetworkDisconnected&) { + // stream hungup + onDisconnected(); + sendEvent(m_events->forISocket().disconnected()); + return kNew; + } + catch (XArchNetwork& e) { + // other write error + LOG((CLOG_WARN "error writing socket: %s", e.what())); + onDisconnected(); + sendEvent(m_events->forIStream().outputError()); + sendEvent(m_events->forISocket().disconnected()); + return kNew; + } + + return kRetry; +} + void TCPSocket::setJob(ISocketMultiplexerJob* job) { @@ -468,172 +641,14 @@ TCPSocket::serviceConnected(ISocketMultiplexerJob* job, return newJob(); } - bool needNewJob = false; - - static bool s_retry = false; - static int s_retrySize = 0; - static void* s_staticBuffer = NULL; - + EJobResult result = kRetry; if (write) { - try { - // write data - int bufferSize = 0; - int bytesWrote = 0; - int status = 0; - - if (s_retry) { - bufferSize = s_retrySize; - } - else { - bufferSize = m_outputBuffer.getSize(); - s_staticBuffer = malloc(bufferSize); - memcpy(s_staticBuffer, m_outputBuffer.peek(bufferSize), bufferSize); - } - - if (bufferSize == 0) { - return job; - } - - if (isSecure()) { - if (isSecureReady()) { - status = secureWrite(s_staticBuffer, bufferSize, bytesWrote); - if (status > 0) { - s_retry = false; - bufferSize = 0; - free(s_staticBuffer); - s_staticBuffer = NULL; - } - else if (status < 0) { - return NULL; - } - else if (status == 0) { - s_retry = true; - s_retrySize = bufferSize; - return newJob(); - } - } - else { - return job; - } - } - else { - bytesWrote = (UInt32)ARCH->writeSocket(m_socket, s_staticBuffer, bufferSize); - bufferSize = 0; - free(s_staticBuffer); - s_staticBuffer = NULL; - } - - // discard written data - if (bytesWrote > 0) { - m_outputBuffer.pop(bytesWrote); - if (m_outputBuffer.getSize() == 0) { - sendEvent(m_events->forIStream().outputFlushed()); - m_flushed = true; - m_flushed.broadcast(); - needNewJob = true; - } - } - } - catch (XArchNetworkShutdown&) { - // remote read end of stream hungup. our output side - // has therefore shutdown. - onOutputShutdown(); - sendEvent(m_events->forIStream().outputShutdown()); - if (!m_readable && m_inputBuffer.getSize() == 0) { - sendEvent(m_events->forISocket().disconnected()); - m_connected = false; - } - needNewJob = true; - } - catch (XArchNetworkDisconnected&) { - // stream hungup - onDisconnected(); - sendEvent(m_events->forISocket().disconnected()); - needNewJob = true; - } - catch (XArchNetwork& e) { - // other write error - LOG((CLOG_WARN "error writing socket: %s", e.what())); - onDisconnected(); - sendEvent(m_events->forIStream().outputError()); - sendEvent(m_events->forISocket().disconnected()); - needNewJob = true; - } + result = doWrite(); } if (read && m_readable) { - try { - static UInt8 buffer[4096]; - memset(buffer, 0, sizeof(buffer)); - int bytesRead = 0; - int status = 0; - - if (isSecure()) { - if (isSecureReady()) { - status = secureRead(buffer, sizeof(buffer), bytesRead); - if (status < 0) { - return NULL; - } - else if (status == 0) { - return newJob(); - } - } - else { - return job; - } - } - else { - bytesRead = (int) ARCH->readSocket(m_socket, buffer, sizeof(buffer)); - } - - if (bytesRead > 0) { - bool wasEmpty = (m_inputBuffer.getSize() == 0); - - // slurp up as much as possible - do { - m_inputBuffer.write(buffer, bytesRead); - - if (isSecure() && isSecureReady()) { - status = secureRead(buffer, sizeof(buffer), bytesRead); - if (status < 0) { - return NULL; - } - } - else { - bytesRead = (int) ARCH->readSocket(m_socket, buffer, sizeof(buffer)); - } - - } while (bytesRead > 0 || status > 0); - - // send input ready if input buffer was empty - if (wasEmpty) { - sendEvent(m_events->forIStream().inputReady()); - } - } - else { - // remote write end of stream hungup. our input side - // has therefore shutdown but don't flush our buffer - // since there's still data to be read. - sendEvent(m_events->forIStream().inputShutdown()); - if (!m_writable && m_inputBuffer.getSize() == 0) { - sendEvent(m_events->forISocket().disconnected()); - m_connected = false; - } - m_readable = false; - needNewJob = true; - } - } - catch (XArchNetworkDisconnected&) { - // stream hungup - sendEvent(m_events->forISocket().disconnected()); - onDisconnected(); - needNewJob = true; - } - catch (XArchNetwork& e) { - // ignore other read error - LOG((CLOG_WARN "error reading socket: %s", e.what())); - } + result = doRead(); } - return needNewJob ? newJob() : job; + return result == kBreak ? NULL : result == kNew ? newJob() : job; } diff --git a/src/lib/net/TCPSocket.h b/src/lib/net/TCPSocket.h index a84dace05..e45653855 100644 --- a/src/lib/net/TCPSocket.h +++ b/src/lib/net/TCPSocket.h @@ -66,12 +66,20 @@ class TCPSocket : public IDataSocket { virtual void setFingerprintFilename(String& f) {} protected: + enum EJobResult { + kBreak = -1, //!< Break the Job chain + kRetry, //!< Retry the same job + kNew //!< Require a new job + }; + ArchSocket getSocket() { return m_socket; } IEventQueue* getEvents() { return m_events; } virtual bool isSecureReady() { return false; } virtual bool isSecure() { return false; } virtual int secureRead(void* buffer, int, int& ) { return 0; } virtual int secureWrite(const void*, int, int& ) { return 0; } + virtual EJobResult doRead(); + virtual EJobResult doWrite(); void setJob(ISocketMultiplexerJob*); From 08a73218e63210ddafe3cc845405b63ea67c848b Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 24 Aug 2016 17:52:02 +0100 Subject: [PATCH 298/572] Refactored secure read and write into SecureSocket --- src/lib/net/TCPSocket.cpp | 242 ++++++++++++++----------------------- src/lib/net/TCPSocket.h | 5 +- src/lib/plugin/ns/SecureSocket.cpp | 109 +++++++++++++++++ src/lib/plugin/ns/SecureSocket.h | 2 + 4 files changed, 203 insertions(+), 155 deletions(-) diff --git a/src/lib/net/TCPSocket.cpp b/src/lib/net/TCPSocket.cpp index cf5a81d0d..ce2cdd099 100644 --- a/src/lib/net/TCPSocket.cpp +++ b/src/lib/net/TCPSocket.cpp @@ -326,77 +326,39 @@ TCPSocket::init() TCPSocket::EJobResult TCPSocket::doRead() { - try { - static UInt8 buffer[4096]; - memset(buffer, 0, sizeof(buffer)); - int bytesRead = 0; - int status = 0; + UInt8 buffer[4096]; + memset(buffer, 0, sizeof(buffer)); + size_t bytesRead = 0; + + bytesRead = (int) ARCH->readSocket(m_socket, buffer, sizeof(buffer)); + + if (bytesRead > 0) { + bool wasEmpty = (m_inputBuffer.getSize() == 0); - if (isSecure()) { - if (isSecureReady()) { - status = secureRead(buffer, sizeof(buffer), bytesRead); - if (status < 0) { - return kBreak; - } - else if (status == 0) { - return kNew; - } - } - else { - return kRetry; - } - } - else { - bytesRead = (int) ARCH->readSocket(m_socket, buffer, sizeof(buffer)); - } + // slurp up as much as possible + do { + m_inputBuffer.write(buffer, bytesRead); + + bytesRead = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); + } while (bytesRead > 0); - if (bytesRead > 0) { - bool wasEmpty = (m_inputBuffer.getSize() == 0); - - // slurp up as much as possible - do { - m_inputBuffer.write(buffer, bytesRead); - - if (isSecure() && isSecureReady()) { - status = secureRead(buffer, sizeof(buffer), bytesRead); - if (status < 0) { - return kBreak; - } - } - else { - bytesRead = (int) ARCH->readSocket(m_socket, buffer, sizeof(buffer)); - } - - } while (bytesRead > 0 || status > 0); - - // send input ready if input buffer was empty - if (wasEmpty) { - sendEvent(m_events->forIStream().inputReady()); - } - } - else { - // remote write end of stream hungup. our input side - // has therefore shutdown but don't flush our buffer - // since there's still data to be read. - sendEvent(m_events->forIStream().inputShutdown()); - if (!m_writable && m_inputBuffer.getSize() == 0) { - sendEvent(m_events->forISocket().disconnected()); - m_connected = false; - } - m_readable = false; - return kNew; + // send input ready if input buffer was empty + if (wasEmpty) { + sendEvent(m_events->forIStream().inputReady()); } } - catch (XArchNetworkDisconnected&) { - // stream hungup - sendEvent(m_events->forISocket().disconnected()); - onDisconnected(); + else { + // remote write end of stream hungup. our input side + // has therefore shutdown but don't flush our buffer + // since there's still data to be read. + sendEvent(m_events->forIStream().inputShutdown()); + if (!m_writable && m_inputBuffer.getSize() == 0) { + sendEvent(m_events->forISocket().disconnected()); + m_connected = false; + } + m_readable = false; return kNew; } - catch (XArchNetwork& e) { - // ignore other read error - LOG((CLOG_WARN "error reading socket: %s", e.what())); - } return kRetry; } @@ -404,92 +366,16 @@ TCPSocket::doRead() TCPSocket::EJobResult TCPSocket::doWrite() { - static bool s_retry = false; - static int s_retrySize = 0; - static void* s_staticBuffer = NULL; - - try { - // write data - int bufferSize = 0; - int bytesWrote = 0; - int status = 0; - - if (s_retry) { - bufferSize = s_retrySize; - } - else { - bufferSize = m_outputBuffer.getSize(); - s_staticBuffer = malloc(bufferSize); - memcpy(s_staticBuffer, m_outputBuffer.peek(bufferSize), bufferSize); - } - - if (bufferSize == 0) { - return kRetry; - } - - if (isSecure()) { - if (isSecureReady()) { - status = secureWrite(s_staticBuffer, bufferSize, bytesWrote); - if (status > 0) { - s_retry = false; - bufferSize = 0; - free(s_staticBuffer); - s_staticBuffer = NULL; - } - else if (status < 0) { - return kBreak; - } - else if (status == 0) { - s_retry = true; - s_retrySize = bufferSize; - return kNew; - } - } - else { - return kRetry; - } - } - else { - bytesWrote = (UInt32)ARCH->writeSocket(m_socket, s_staticBuffer, bufferSize); - bufferSize = 0; - free(s_staticBuffer); - s_staticBuffer = NULL; - } - - // discard written data - if (bytesWrote > 0) { - m_outputBuffer.pop(bytesWrote); - if (m_outputBuffer.getSize() == 0) { - sendEvent(m_events->forIStream().outputFlushed()); - m_flushed = true; - m_flushed.broadcast(); - return kNew; - } - } - } - catch (XArchNetworkShutdown&) { - // remote read end of stream hungup. our output side - // has therefore shutdown. - onOutputShutdown(); - sendEvent(m_events->forIStream().outputShutdown()); - if (!m_readable && m_inputBuffer.getSize() == 0) { - sendEvent(m_events->forISocket().disconnected()); - m_connected = false; - } - return kNew; - } - catch (XArchNetworkDisconnected&) { - // stream hungup - onDisconnected(); - sendEvent(m_events->forISocket().disconnected()); - return kNew; - } - catch (XArchNetwork& e) { - // other write error - LOG((CLOG_WARN "error writing socket: %s", e.what())); - onDisconnected(); - sendEvent(m_events->forIStream().outputError()); - sendEvent(m_events->forISocket().disconnected()); + // write data + UInt32 bufferSize = 0; + int bytesWrote = 0; + + bufferSize = m_outputBuffer.getSize(); + const void* buffer = m_outputBuffer.peek(bufferSize); + bytesWrote = (UInt32)ARCH->writeSocket(m_socket, buffer, bufferSize); + + if (bytesWrote > 0) { + discardWrittenData(bytesWrote); return kNew; } @@ -550,6 +436,17 @@ TCPSocket::sendEvent(Event::Type type) m_events->addEvent(Event(type, getEventTarget(), NULL)); } +void +TCPSocket::discardWrittenData(int bytesWrote) +{ + m_outputBuffer.pop(bytesWrote); + if (m_outputBuffer.getSize() == 0) { + sendEvent(m_events->forIStream().outputFlushed()); + m_flushed = true; + m_flushed.broadcast(); + } +} + void TCPSocket::onConnected() { @@ -643,11 +540,50 @@ TCPSocket::serviceConnected(ISocketMultiplexerJob* job, EJobResult result = kRetry; if (write) { - result = doWrite(); + try { + result = doWrite(); + } + catch (XArchNetworkShutdown&) { + // remote read end of stream hungup. our output side + // has therefore shutdown. + onOutputShutdown(); + sendEvent(m_events->forIStream().outputShutdown()); + if (!m_readable && m_inputBuffer.getSize() == 0) { + sendEvent(m_events->forISocket().disconnected()); + m_connected = false; + } + result = kNew; + } + catch (XArchNetworkDisconnected&) { + // stream hungup + onDisconnected(); + sendEvent(m_events->forISocket().disconnected()); + result = kNew; + } + catch (XArchNetwork& e) { + // other write error + LOG((CLOG_WARN "error writing socket: %s", e.what())); + onDisconnected(); + sendEvent(m_events->forIStream().outputError()); + sendEvent(m_events->forISocket().disconnected()); + result = kNew; + } } if (read && m_readable) { - result = doRead(); + try { + result = doRead(); + } + catch (XArchNetworkDisconnected&) { + // stream hungup + sendEvent(m_events->forISocket().disconnected()); + onDisconnected(); + result = kNew; + } + catch (XArchNetwork& e) { + // ignore other read error + LOG((CLOG_WARN "error reading socket: %s", e.what())); + } } return result == kBreak ? NULL : result == kNew ? newJob() : job; diff --git a/src/lib/net/TCPSocket.h b/src/lib/net/TCPSocket.h index e45653855..181e7f301 100644 --- a/src/lib/net/TCPSocket.h +++ b/src/lib/net/TCPSocket.h @@ -89,6 +89,7 @@ class TCPSocket : public IDataSocket { Mutex& getMutex() { return m_mutex; } void sendEvent(Event::Type); + void discardWrittenData(int bytesWrote); private: void init(); @@ -111,12 +112,12 @@ class TCPSocket : public IDataSocket { bool m_writable; bool m_connected; IEventQueue* m_events; + StreamBuffer m_inputBuffer; + StreamBuffer m_outputBuffer; private: Mutex m_mutex; ArchSocket m_socket; - StreamBuffer m_inputBuffer; - StreamBuffer m_outputBuffer; CondVar m_flushed; SocketMultiplexer* m_socketMultiplexer; }; diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 6e0e8f662..9f4df4416 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -140,6 +140,115 @@ SecureSocket::secureAccept() getSocket(), isReadable(), isWritable())); } +TCPSocket::EJobResult +SecureSocket::doRead() +{ + static UInt8 buffer[4096]; + memset(buffer, 0, sizeof(buffer)); + int bytesRead = 0; + int status = 0; + + if (isSecureReady()) { + status = secureRead(buffer, sizeof(buffer), bytesRead); + if (status < 0) { + return kBreak; + } + else if (status == 0) { + return kNew; + } + } + else { + return kRetry; + } + + if (bytesRead > 0) { + bool wasEmpty = (m_inputBuffer.getSize() == 0); + + // slurp up as much as possible + do { + m_inputBuffer.write(buffer, bytesRead); + + status = secureRead(buffer, sizeof(buffer), bytesRead); + if (status < 0) { + return kBreak; + } + } while (bytesRead > 0 || status > 0); + + // send input ready if input buffer was empty + if (wasEmpty) { + sendEvent(m_events->forIStream().inputReady()); + } + } + else { + // remote write end of stream hungup. our input side + // has therefore shutdown but don't flush our buffer + // since there's still data to be read. + sendEvent(m_events->forIStream().inputShutdown()); + if (!m_writable && m_inputBuffer.getSize() == 0) { + sendEvent(m_events->forISocket().disconnected()); + m_connected = false; + } + m_readable = false; + return kNew; + } + + return kRetry; +} + +TCPSocket::EJobResult +SecureSocket::doWrite() +{ + static bool s_retry = false; + static int s_retrySize = 0; + static void* s_staticBuffer = NULL; + + // write data + int bufferSize = 0; + int bytesWrote = 0; + int status = 0; + + if (s_retry) { + bufferSize = s_retrySize; + } + else { + bufferSize = m_outputBuffer.getSize(); + s_staticBuffer = malloc(bufferSize); + memcpy(s_staticBuffer, m_outputBuffer.peek(bufferSize), bufferSize); + } + + if (bufferSize == 0) { + return kRetry; + } + + if (isSecureReady()) { + status = secureWrite(s_staticBuffer, bufferSize, bytesWrote); + if (status > 0) { + s_retry = false; + bufferSize = 0; + free(s_staticBuffer); + s_staticBuffer = NULL; + } + else if (status < 0) { + return kBreak; + } + else if (status == 0) { + s_retry = true; + s_retrySize = bufferSize; + return kNew; + } + } + else { + return kRetry; + } + + if (bytesWrote > 0) { + discardWrittenData(bytesWrote); + return kNew; + } + + return kRetry; +} + int SecureSocket::secureRead(void* buffer, int size, int& read) { diff --git a/src/lib/plugin/ns/SecureSocket.h b/src/lib/plugin/ns/SecureSocket.h index 5be68f7de..732ad3997 100644 --- a/src/lib/plugin/ns/SecureSocket.h +++ b/src/lib/plugin/ns/SecureSocket.h @@ -48,6 +48,8 @@ class SecureSocket : public TCPSocket { newJob(); void secureConnect(); void secureAccept(); + EJobResult doRead(); + EJobResult doWrite(); bool isReady() const { return m_secureReady; } bool isFatal() const { return m_fatal; } void isFatal(bool b) { m_fatal = b; } From 07902d623c244bf4a10b5ccc08f5b3d70955e763 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 24 Aug 2016 17:55:19 +0100 Subject: [PATCH 299/572] Removed dead code --- src/lib/net/TCPSocket.h | 4 ---- src/lib/plugin/ns/SecureSocket.h | 2 -- 2 files changed, 6 deletions(-) diff --git a/src/lib/net/TCPSocket.h b/src/lib/net/TCPSocket.h index 181e7f301..df2cc9706 100644 --- a/src/lib/net/TCPSocket.h +++ b/src/lib/net/TCPSocket.h @@ -74,10 +74,6 @@ class TCPSocket : public IDataSocket { ArchSocket getSocket() { return m_socket; } IEventQueue* getEvents() { return m_events; } - virtual bool isSecureReady() { return false; } - virtual bool isSecure() { return false; } - virtual int secureRead(void* buffer, int, int& ) { return 0; } - virtual int secureWrite(const void*, int, int& ) { return 0; } virtual EJobResult doRead(); virtual EJobResult doWrite(); diff --git a/src/lib/plugin/ns/SecureSocket.h b/src/lib/plugin/ns/SecureSocket.h index 732ad3997..3c2ed55df 100644 --- a/src/lib/plugin/ns/SecureSocket.h +++ b/src/lib/plugin/ns/SecureSocket.h @@ -50,11 +50,9 @@ class SecureSocket : public TCPSocket { void secureAccept(); EJobResult doRead(); EJobResult doWrite(); - bool isReady() const { return m_secureReady; } bool isFatal() const { return m_fatal; } void isFatal(bool b) { m_fatal = b; } bool isSecureReady(); - bool isSecure() { return true; } int secureRead(void* buffer, int size, int& read); int secureWrite(const void* buffer, int size, int& wrote); void initSsl(bool server); From e0819b86cd7f1c80913e032d051e003a3438c6f0 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 24 Aug 2016 17:59:53 +0100 Subject: [PATCH 300/572] Conflicts: src/lib/plugin/ns/SecureSocket.cpp --- src/lib/plugin/ns/SecureSocket.cpp | 10 +++++----- src/lib/plugin/ns/SecureSocket.h | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 9f4df4416..43f29023b 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -128,16 +128,16 @@ void SecureSocket::secureConnect() { setJob(new TSocketMultiplexerMethodJob( - this, &SecureSocket::serviceConnect, - getSocket(), isReadable(), isWritable())); + this, &SecureSocket::serviceConnect, + getSocket(), isReadable(), isWritable())); } void SecureSocket::secureAccept() { setJob(new TSocketMultiplexerMethodJob( - this, &SecureSocket::serviceAccept, - getSocket(), isReadable(), isWritable())); + this, &SecureSocket::serviceAccept, + getSocket(), isReadable(), isWritable())); } TCPSocket::EJobResult @@ -218,7 +218,7 @@ SecureSocket::doWrite() if (bufferSize == 0) { return kRetry; - } + } if (isSecureReady()) { status = secureWrite(s_staticBuffer, bufferSize, bytesWrote); diff --git a/src/lib/plugin/ns/SecureSocket.h b/src/lib/plugin/ns/SecureSocket.h index 3c2ed55df..1a29cdaab 100644 --- a/src/lib/plugin/ns/SecureSocket.h +++ b/src/lib/plugin/ns/SecureSocket.h @@ -46,15 +46,15 @@ class SecureSocket : public TCPSocket { ISocketMultiplexerJob* newJob(); - void secureConnect(); - void secureAccept(); - EJobResult doRead(); - EJobResult doWrite(); bool isFatal() const { return m_fatal; } void isFatal(bool b) { m_fatal = b; } bool isSecureReady(); + void secureConnect(); + void secureAccept(); int secureRead(void* buffer, int size, int& read); int secureWrite(const void* buffer, int size, int& wrote); + EJobResult doRead(); + EJobResult doWrite(); void initSsl(bool server); bool loadCertificates(String& CertFile); From 6d86a6033ff30087cad44508e3ce1bee6872db8f Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 24 Aug 2016 18:03:33 +0100 Subject: [PATCH 301/572] Removed dead code --- src/lib/net/TCPSocket.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib/net/TCPSocket.h b/src/lib/net/TCPSocket.h index df2cc9706..f3beb2f8e 100644 --- a/src/lib/net/TCPSocket.h +++ b/src/lib/net/TCPSocket.h @@ -61,9 +61,6 @@ class TCPSocket : public IDataSocket { virtual ISocketMultiplexerJob* newJob(); - virtual void secureConnect() {} - virtual void secureAccept() {} - virtual void setFingerprintFilename(String& f) {} protected: enum EJobResult { From e1be5a66b7bb8c06e76315dd8648e5be9f96d20e Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 26 Aug 2016 14:36:57 +0100 Subject: [PATCH 302/572] Used correct event target --- src/lib/server/ClientListener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index 11c6c8cf3..9632faeac 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -152,7 +152,7 @@ ClientListener::handleClientConnecting(const Event&, void*) // has to call secure accept which may require retry if (!m_args.m_enableCrypto) { m_events->addEvent(Event(m_events->forClientListener().accepted(), - stream)); + stream->getEventTarget())); } } From a12ae98e9f9c05e16bfb8d01db7ba58aa1f493da Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Thu, 1 Sep 2016 13:58:42 +0100 Subject: [PATCH 303/572] Used parent pointer for socket accept --- src/lib/server/ClientListener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index 9632faeac..a0ae30287 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -137,7 +137,7 @@ void ClientListener::handleClientConnecting(const Event&, void*) { // accept client connection - IDataSocket* socket = m_listen->accept(); + synergy::IStream* stream = m_listen->accept(); if (socket == NULL) { return; From a471bdcea7215dc2d7d0bfdb8a5aa6e678961d68 Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Thu, 1 Sep 2016 14:07:19 +0100 Subject: [PATCH 304/572] Made a own copy of ClientArgs in Client --- src/lib/client/Client.cpp | 2 +- src/lib/client/Client.h | 2 +- src/lib/server/ClientListener.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 5c716b462..b67f64bf8 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -55,7 +55,7 @@ Client::Client( const String& name, const NetworkAddress& address, ISocketFactory* socketFactory, synergy::Screen* screen, - ClientArgs& args) : + ClientArgs const& args) : m_mock(false), m_name(name), m_serverAddress(address), diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index 20e44a6a1..b36687834 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -222,5 +222,5 @@ class Client : public IClient, public INode { Thread* m_writeToDropDirThread; TCPSocket* m_socket; bool m_useSecureNetwork; - ClientArgs& m_args; + ClientArgs m_args; }; diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index a0ae30287..858fae15e 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -150,7 +150,7 @@ ClientListener::handleClientConnecting(const Event&, void*) // When using non SSL, server accepts clients immediately, while SSL // has to call secure accept which may require retry - if (!m_args.m_enableCrypto) { + if (!m_useSecureNetwork) { m_events->addEvent(Event(m_events->forClientListener().accepted(), stream->getEventTarget())); } From 16dbb7cd6a0123483d6643324529a5436b5b68cd Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 1 Sep 2016 14:17:34 +0100 Subject: [PATCH 305/572] Fixed using the wrong variable name --- src/lib/server/ClientListener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index 858fae15e..08f58278a 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -139,7 +139,7 @@ ClientListener::handleClientConnecting(const Event&, void*) // accept client connection synergy::IStream* stream = m_listen->accept(); - if (socket == NULL) { + if (stream == NULL) { return; } From f2846b1586cfe38a65d2e097b149f43483aaa34b Mon Sep 17 00:00:00 2001 From: XinyuHou Date: Thu, 1 Sep 2016 14:50:44 +0100 Subject: [PATCH 306/572] Remove disconnect on retry exceed --- src/lib/plugin/ns/SecureSocket.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 43f29023b..483c07fbe 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -605,12 +605,6 @@ SecureSocket::checkResult(int status, int& retry) break; } - // If the retry max would exceed the allowed, treat it as a fatal error - if (retry > s_maxRetry) { - LOG((CLOG_DEBUG "retry exceeded %f sec", s_maxRetry * s_retryDelay)); - isFatal(true); - } - if (isFatal()) { retry = 0; showError(); From 61c0bd191010bf63fb4d810e2c0c7d0cbcd61f44 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Thu, 1 Sep 2016 14:56:41 +0100 Subject: [PATCH 307/572] Removed unused variable --- src/lib/plugin/ns/SecureSocket.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/plugin/ns/SecureSocket.cpp b/src/lib/plugin/ns/SecureSocket.cpp index 483c07fbe..7076c764e 100644 --- a/src/lib/plugin/ns/SecureSocket.cpp +++ b/src/lib/plugin/ns/SecureSocket.cpp @@ -37,8 +37,6 @@ #define MAX_ERROR_SIZE 65535 -// maxmium retry time limit set to 10s -static const int s_maxRetry = 1000; static const float s_retryDelay = 0.01f; enum { From 07f7baf3351b3eaa497bfada47ee5d2d09d1d49b Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Mon, 12 Sep 2016 11:11:55 -0400 Subject: [PATCH 308/572] #4740 Passed IDataSocket directly --- src/lib/server/ClientListener.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index 08f58278a..78a629017 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -137,35 +137,34 @@ void ClientListener::handleClientConnecting(const Event&, void*) { // accept client connection - synergy::IStream* stream = m_listen->accept(); + IDataSocket* socket = m_listen->accept(); - if (stream == NULL) { + if (socket == NULL) { return; } m_events->adoptHandler(m_events->forClientListener().accepted(), - stream->getEventTarget(), + socket->getEventTarget(), new TMethodEventJob(this, - &ClientListener::handleClientAccepted, stream)); + &ClientListener::handleClientAccepted, socket)); // When using non SSL, server accepts clients immediately, while SSL // has to call secure accept which may require retry if (!m_useSecureNetwork) { m_events->addEvent(Event(m_events->forClientListener().accepted(), - stream->getEventTarget())); + socket->getEventTarget())); } } void -ClientListener::handleClientAccepted(const Event&, void* vstream) +ClientListener::handleClientAccepted(const Event&, void* vsocket) { LOG((CLOG_NOTE "accepted client connection")); - synergy::IStream* stream = reinterpret_cast(vstream); - IDataSocket* socket = dynamic_cast(stream); + IDataSocket* socket = reinterpret_cast(vsocket); // filter socket messages, including a packetizing filter - stream = new PacketStreamFilter(m_events, socket); + synergy::IStream* stream = new PacketStreamFilter(m_events, socket); assert(m_server != NULL); // create proxy for unknown client From 71741da721955cac2f2648f4d1bbb114c1a7c3cb Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Mon, 12 Sep 2016 11:13:07 -0400 Subject: [PATCH 309/572] #4740 Only adopted non-SSL socket --- src/lib/server/ClientListener.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index 78a629017..a34ccf686 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -164,7 +164,8 @@ ClientListener::handleClientAccepted(const Event&, void* vsocket) IDataSocket* socket = reinterpret_cast(vsocket); // filter socket messages, including a packetizing filter - synergy::IStream* stream = new PacketStreamFilter(m_events, socket); + bool adopt = !m_useSecureNetwork; + synergy::IStream* stream = new PacketStreamFilter(m_events, socket, adopt); assert(m_server != NULL); // create proxy for unknown client From ceecee0f5742567e0a11dc657a2b831dc25d90eb Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Mon, 12 Sep 2016 12:11:23 -0400 Subject: [PATCH 310/572] #4740 Used safer static cast --- src/lib/server/ClientListener.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp index a34ccf686..cf64bc307 100644 --- a/src/lib/server/ClientListener.cpp +++ b/src/lib/server/ClientListener.cpp @@ -161,7 +161,7 @@ ClientListener::handleClientAccepted(const Event&, void* vsocket) { LOG((CLOG_NOTE "accepted client connection")); - IDataSocket* socket = reinterpret_cast(vsocket); + IDataSocket* socket = static_cast(vsocket); // filter socket messages, including a packetizing filter bool adopt = !m_useSecureNetwork; From fc6ce84ee4cb4e674e3268075c0eb750731d61ab Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Tue, 13 Sep 2016 15:13:50 +0100 Subject: [PATCH 311/572] Don't pollute the log with clipboard data at DEBUG2 --- src/lib/synergy/ProtocolUtil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/synergy/ProtocolUtil.cpp b/src/lib/synergy/ProtocolUtil.cpp index 73496c895..ab361880d 100644 --- a/src/lib/synergy/ProtocolUtil.cpp +++ b/src/lib/synergy/ProtocolUtil.cpp @@ -230,7 +230,7 @@ ProtocolUtil::vreadf(synergy::IStream* stream, const char* fmt, va_list args) throw; } - LOG((CLOG_DEBUG2 "readf: read %d byte string: %.*s", len, len, sBuffer)); + LOG((CLOG_DEBUG2 "readf: read %d byte string", len)); // save the data String* dst = va_arg(args, String*); From d14d907ac199682ee496ab29fb444ee793668da8 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Tue, 13 Sep 2016 10:50:37 -0400 Subject: [PATCH 312/572] #4740 Removed clipboard interrupt code --- src/lib/synergy/StreamChunker.cpp | 40 --------------------------------------- src/lib/synergy/StreamChunker.h | 3 --- 2 files changed, 43 deletions(-) diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index a2789f78c..34ab3b466 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -39,8 +39,6 @@ using namespace std; #define CHUNK_SIZE 512 * 1024; // 512kb -bool StreamChunker::s_isChunkingClipboard = false; -bool StreamChunker::s_interruptClipboard = false; bool StreamChunker::s_isChunkingFile = false; bool StreamChunker::s_interruptFile = false; Mutex* StreamChunker::s_interruptMutex = NULL; @@ -129,8 +127,6 @@ StreamChunker::sendClipboard( IEventQueue* events, void* eventTarget) { - s_isChunkingClipboard = true; - // send first message (data size) String dataSize = synergy::string::sizeTypeToString(size); ClipboardChunk* sizeMessage = ClipboardChunk::start(id, sequence, dataSize); @@ -144,17 +140,6 @@ StreamChunker::sendClipboard( sendStopwatch.start(); while (true) { - { - if (s_interruptMutex == NULL) { - s_interruptMutex = new Mutex(); - } - Lock lock(s_interruptMutex); - if (s_interruptClipboard) { - LOG((CLOG_DEBUG "clipboard transmission interrupted")); - break; - } - } - if (sendStopwatch.getTime() > SEND_THRESHOLD) { events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); @@ -183,8 +168,6 @@ StreamChunker::sendClipboard( events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, end)); LOG((CLOG_DEBUG "sent clipboard size=%d", sentLength)); - - s_isChunkingClipboard = false; } void @@ -195,26 +178,3 @@ StreamChunker::interruptFile() LOG((CLOG_INFO "previous dragged file has become invalid")); } } - -void -StreamChunker::setClipboardInterrupt(bool interrupt) -{ - if (s_interruptMutex == NULL) { - s_interruptMutex = new Mutex(); - } - Lock lock(s_interruptMutex); - - if (interrupt) { - if (s_isChunkingClipboard) { - s_interruptClipboard = interrupt; - LOG((CLOG_INFO "previous clipboard data has become invalid")); - } - else { - LOG((CLOG_DEBUG "no clipboard to interrupt")); - } - } - else { - s_interruptClipboard = interrupt; - LOG((CLOG_DEBUG "reset clipboard interrupt")); - } -} diff --git a/src/lib/synergy/StreamChunker.h b/src/lib/synergy/StreamChunker.h index c9ac5af18..e834bf2b5 100644 --- a/src/lib/synergy/StreamChunker.h +++ b/src/lib/synergy/StreamChunker.h @@ -37,11 +37,8 @@ class StreamChunker { IEventQueue* events, void* eventTarget); static void interruptFile(); - static void setClipboardInterrupt(bool interrupt); private: - static bool s_isChunkingClipboard; - static bool s_interruptClipboard; static bool s_isChunkingFile; static bool s_interruptFile; static Mutex* s_interruptMutex; From 8ab69a22ae8a33380006b4fbdb33097fdd246ea2 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Tue, 13 Sep 2016 11:06:06 -0400 Subject: [PATCH 313/572] #4740 Forced delay on heartbeat rather than each clipboard chunk --- src/lib/synergy/StreamChunker.cpp | 79 +++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index 34ab3b466..839c4341c 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -33,10 +33,9 @@ #include -#define SEND_THRESHOLD 0.005f - using namespace std; +#define FAKE_HEARTBEAT_THRESHOLD 3 #define CHUNK_SIZE 512 * 1024; // 512kb bool StreamChunker::s_isChunkingFile = false; @@ -70,8 +69,8 @@ StreamChunker::sendFile( // send chunk messages with a fixed chunk size size_t sentLength = 0; size_t chunkSize = CHUNK_SIZE; - Stopwatch sendStopwatch; - sendStopwatch.start(); + Stopwatch fakeHeartbeatStopwatch; + fakeHeartbeatStopwatch.start(); file.seekg (0, std::ios::beg); while (true) { @@ -81,30 +80,29 @@ StreamChunker::sendFile( break; } - if (sendStopwatch.getTime() > SEND_THRESHOLD) { + if (fakeHeartbeatStopwatch.getTime() > FAKE_HEARTBEAT_THRESHOLD) { events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); + fakeHeartbeatStopwatch.reset(); + } + + // make sure we don't read too much from the mock data. + if (sentLength + chunkSize > size) { + chunkSize = size - sentLength; + } - // make sure we don't read too much from the mock data. - if (sentLength + chunkSize > size) { - chunkSize = size - sentLength; - } - - char* chunkData = new char[chunkSize]; - file.read(chunkData, chunkSize); - UInt8* data = reinterpret_cast(chunkData); - FileChunk* fileChunk = FileChunk::data(data, chunkSize); - delete[] chunkData; - - events->addEvent(Event(events->forFile().fileChunkSending(), eventTarget, fileChunk)); + char* chunkData = new char[chunkSize]; + file.read(chunkData, chunkSize); + UInt8* data = reinterpret_cast(chunkData); + FileChunk* fileChunk = FileChunk::data(data, chunkSize); + delete[] chunkData; - sentLength += chunkSize; - file.seekg (sentLength, std::ios::beg); + events->addEvent(Event(events->forFile().fileChunkSending(), eventTarget, fileChunk)); - if (sentLength == size) { - break; - } + sentLength += chunkSize; + file.seekg (sentLength, std::ios::beg); - sendStopwatch.reset(); + if (sentLength == size) { + break; } } @@ -136,29 +134,28 @@ StreamChunker::sendClipboard( // send clipboard chunk with a fixed size size_t sentLength = 0; size_t chunkSize = CHUNK_SIZE; - Stopwatch sendStopwatch; - sendStopwatch.start(); + Stopwatch fakeHeartbeatStopwatch; + fakeHeartbeatStopwatch.start(); while (true) { - if (sendStopwatch.getTime() > SEND_THRESHOLD) { + if (fakeHeartbeatStopwatch.getTime() > FAKE_HEARTBEAT_THRESHOLD) { events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); + fakeHeartbeatStopwatch.reset(); + } + + // make sure we don't read too much from the mock data. + if (sentLength + chunkSize > size) { + chunkSize = size - sentLength; + } - // make sure we don't read too much from the mock data. - if (sentLength + chunkSize > size) { - chunkSize = size - sentLength; - } - - String chunk(data.substr(sentLength, chunkSize).c_str(), chunkSize); - ClipboardChunk* dataChunk = ClipboardChunk::data(id, sequence, chunk); - - events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, dataChunk)); - - sentLength += chunkSize; - if (sentLength == size) { - break; - } + String chunk(data.substr(sentLength, chunkSize).c_str(), chunkSize); + ClipboardChunk* dataChunk = ClipboardChunk::data(id, sequence, chunk); + + events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, dataChunk)); - sendStopwatch.reset(); + sentLength += chunkSize; + if (sentLength == size) { + break; } } From d8ace7fb4a53bd2b1aed5f8671383bdd0db59cfe Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Tue, 13 Sep 2016 11:09:47 -0400 Subject: [PATCH 314/572] #4740 Used global const rather than macro --- src/lib/synergy/StreamChunker.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index 839c4341c..eba98572a 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -35,8 +35,9 @@ using namespace std; -#define FAKE_HEARTBEAT_THRESHOLD 3 -#define CHUNK_SIZE 512 * 1024; // 512kb + +static const double g_fakeHeartbeatThreshold = 3; +static const size_t g_chunkSize = 512 * 1024; //512kb bool StreamChunker::s_isChunkingFile = false; bool StreamChunker::s_interruptFile = false; @@ -68,7 +69,7 @@ StreamChunker::sendFile( // send chunk messages with a fixed chunk size size_t sentLength = 0; - size_t chunkSize = CHUNK_SIZE; + size_t chunkSize = g_chunkSize; Stopwatch fakeHeartbeatStopwatch; fakeHeartbeatStopwatch.start(); file.seekg (0, std::ios::beg); @@ -80,7 +81,7 @@ StreamChunker::sendFile( break; } - if (fakeHeartbeatStopwatch.getTime() > FAKE_HEARTBEAT_THRESHOLD) { + if (fakeHeartbeatStopwatch.getTime() > g_fakeHeartbeatThreshold) { events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); fakeHeartbeatStopwatch.reset(); } @@ -133,12 +134,12 @@ StreamChunker::sendClipboard( // send clipboard chunk with a fixed size size_t sentLength = 0; - size_t chunkSize = CHUNK_SIZE; + size_t chunkSize = g_chunkSize; Stopwatch fakeHeartbeatStopwatch; fakeHeartbeatStopwatch.start(); while (true) { - if (fakeHeartbeatStopwatch.getTime() > FAKE_HEARTBEAT_THRESHOLD) { + if (fakeHeartbeatStopwatch.getTime() > g_fakeHeartbeatThreshold) { events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); fakeHeartbeatStopwatch.reset(); } From dc80ab4023753278c1ea3f6a6bb794d63a6f0730 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Tue, 13 Sep 2016 12:19:47 -0400 Subject: [PATCH 315/572] #4740 Sent heartbeat message between each chunk message --- src/lib/synergy/StreamChunker.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/lib/synergy/StreamChunker.cpp b/src/lib/synergy/StreamChunker.cpp index eba98572a..f53c39bc7 100644 --- a/src/lib/synergy/StreamChunker.cpp +++ b/src/lib/synergy/StreamChunker.cpp @@ -35,8 +35,6 @@ using namespace std; - -static const double g_fakeHeartbeatThreshold = 3; static const size_t g_chunkSize = 512 * 1024; //512kb bool StreamChunker::s_isChunkingFile = false; @@ -70,8 +68,6 @@ StreamChunker::sendFile( // send chunk messages with a fixed chunk size size_t sentLength = 0; size_t chunkSize = g_chunkSize; - Stopwatch fakeHeartbeatStopwatch; - fakeHeartbeatStopwatch.start(); file.seekg (0, std::ios::beg); while (true) { @@ -81,10 +77,7 @@ StreamChunker::sendFile( break; } - if (fakeHeartbeatStopwatch.getTime() > g_fakeHeartbeatThreshold) { - events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); - fakeHeartbeatStopwatch.reset(); - } + events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); // make sure we don't read too much from the mock data. if (sentLength + chunkSize > size) { @@ -135,14 +128,9 @@ StreamChunker::sendClipboard( // send clipboard chunk with a fixed size size_t sentLength = 0; size_t chunkSize = g_chunkSize; - Stopwatch fakeHeartbeatStopwatch; - fakeHeartbeatStopwatch.start(); while (true) { - if (fakeHeartbeatStopwatch.getTime() > g_fakeHeartbeatThreshold) { - events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); - fakeHeartbeatStopwatch.reset(); - } + events->addEvent(Event(events->forFile().keepAlive(), eventTarget)); // make sure we don't read too much from the mock data. if (sentLength + chunkSize > size) { From d78758ab7d60a21be36bd368d6d9e2e4b7cc0d44 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Thu, 15 Sep 2016 12:22:00 -0400 Subject: [PATCH 316/572] Fixed indentation --- src/lib/server/Config.h | 68 +++++++++++++++++++++++--------------------- src/lib/synergy/ServerArgs.h | 2 +- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/src/lib/server/Config.h b/src/lib/server/Config.h index 12b244cd8..03422e0de 100644 --- a/src/lib/server/Config.h +++ b/src/lib/server/Config.h @@ -105,7 +105,7 @@ class Config { bool operator==(const String& name) const; private: - Config* m_config; + Config* m_config; String m_name; }; @@ -391,7 +391,8 @@ class Config { link_const_iterator endNeighbor(const String&) const; //! Get the server address - const NetworkAddress& getSynergyAddress() const; + const NetworkAddress& + getSynergyAddress() const; //! Get the screen options /*! @@ -399,14 +400,15 @@ class Config { if the screen is unknown and an empty collection if there are no options. */ - const ScreenOptions* getOptions(const String& name) const; + const ScreenOptions* + getOptions(const String& name) const; //! Check for lock to screen action /*! Returns \c true if this configuration has a lock to screen action. This is for backwards compatible support of ScrollLock locking. */ - bool hasLockToScreenAction() const; + bool hasLockToScreenAction() const; //! Compare configurations bool operator==(const Config&) const; @@ -418,19 +420,21 @@ class Config { Reads a configuration from a context. Throws XConfigRead on error and context is unchanged. */ - void read(ConfigReadContext& context); + void read(ConfigReadContext& context); //! Read configuration /*! Reads a configuration from a stream. Throws XConfigRead on error. */ - friend std::istream& operator>>(std::istream&, Config&); + friend std::istream& + operator>>(std::istream&, Config&); //! Write configuration /*! Writes a configuration to a stream. */ - friend std::ostream& operator<<(std::ostream&, const Config&); + friend std::ostream& + operator<<(std::ostream&, const Config&); //! Get direction name /*! @@ -468,11 +472,11 @@ class Config { static String getOptionValue(OptionID, OptionValue); private: - CellMap m_map; - NameMap m_nameToCanonicalName; + CellMap m_map; + NameMap m_nameToCanonicalName; NetworkAddress m_synergyAddress; ScreenOptions m_globalOptions; - InputFilter m_inputFilter; + InputFilter m_inputFilter; bool m_hasLockToScreenAction; IEventQueue* m_events; }; @@ -488,41 +492,41 @@ class ConfigReadContext { ConfigReadContext(std::istream&, SInt32 firstLine = 1); ~ConfigReadContext(); - bool readLine(String&); - UInt32 getLineNumber() const; + bool readLine(String&); + UInt32 getLineNumber() const; - bool operator!() const; + bool operator!() const; - OptionValue parseBoolean(const String&) const; - OptionValue parseInt(const String&) const; - OptionValue parseModifierKey(const String&) const; - OptionValue parseCorner(const String&) const; - OptionValue parseCorners(const String&) const; + OptionValue parseBoolean(const String&) const; + OptionValue parseInt(const String&) const; + OptionValue parseModifierKey(const String&) const; + OptionValue parseCorner(const String&) const; + OptionValue parseCorners(const String&) const; Config::Interval - parseInterval(const ArgList& args) const; - void parseNameWithArgs( - const String& type, const String& line, - const String& delim, String::size_type& index, - String& name, ArgList& args) const; + parseInterval(const ArgList& args) const; + void parseNameWithArgs( + const String& type, const String& line, + const String& delim, String::size_type& index, + String& name, ArgList& args) const; IPlatformScreen::KeyInfo* - parseKeystroke(const String& keystroke) const; + parseKeystroke(const String& keystroke) const; IPlatformScreen::KeyInfo* - parseKeystroke(const String& keystroke, - const std::set& screens) const; + parseKeystroke(const String& keystroke, + const std::set& screens) const; IPlatformScreen::ButtonInfo* - parseMouse(const String& mouse) const; - KeyModifierMask parseModifier(const String& modifiers) const; - std::istream& getStream() const { return m_stream; }; + parseMouse(const String& mouse) const; + KeyModifierMask parseModifier(const String& modifiers) const; + std::istream& getStream() const { return m_stream; }; private: // not implemented ConfigReadContext& operator=(const ConfigReadContext&); - static String concatArgs(const ArgList& args); + static String concatArgs(const ArgList& args); private: - std::istream& m_stream; - SInt32 m_line; + std::istream& m_stream; + SInt32 m_line; }; //! Configuration stream read exception diff --git a/src/lib/synergy/ServerArgs.h b/src/lib/synergy/ServerArgs.h index 93f0d3119..54310f8e7 100644 --- a/src/lib/synergy/ServerArgs.h +++ b/src/lib/synergy/ServerArgs.h @@ -28,5 +28,5 @@ class ServerArgs : public ArgsBase { public: String m_configFile; - Config* m_config; + Config* m_config; }; From 130458f77a7a3fb2be6fc67a604491ebe912ec5e Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Thu, 15 Sep 2016 12:42:58 -0400 Subject: [PATCH 317/572] #123 Added enable clipboard option in GUI --- src/gui/res/ServerConfigDialogBase.ui | 102 +++++++++++++++++++--------------- src/gui/src/ServerConfig.cpp | 2 + src/gui/src/ServerConfig.h | 3 + src/gui/src/ServerConfigDialog.cpp | 3 + 4 files changed, 65 insertions(+), 45 deletions(-) diff --git a/src/gui/res/ServerConfigDialogBase.ui b/src/gui/res/ServerConfigDialogBase.ui index cc44f66ed..1cc4d2b53 100644 --- a/src/gui/res/ServerConfigDialogBase.ui +++ b/src/gui/res/ServerConfigDialogBase.ui @@ -6,8 +6,8 @@ 0 0 - 740 - 514 + 795 + 534 @@ -17,7 +17,7 @@ - 0 + 2 @@ -44,7 +44,7 @@ - :/res/icons/64x64/user-trash.png + :/res/icons/64x64/user-trash.png @@ -82,7 +82,7 @@ - :/res/icons/64x64/video-display.png + :/res/icons/64x64/video-display.png @@ -408,6 +408,36 @@ Double click on a screen to edit its settings. &Options + + + + true + + + Don't take &foreground window on Windows servers + + + + + + + true + + + Use &relative mouse moves + + + + + + + true + + + S&ynchronize screen savers + + + @@ -461,33 +491,23 @@ Double click on a screen to edit its settings. - - - - true - - - Use &relative mouse moves - - - - - - - true + + + + Qt::Vertical - - S&ynchronize screen savers + + + 20 + 16 + - + - - - - true - + + - Don't take &foreground window on Windows servers + Enable drag and drop file transfers @@ -498,25 +518,15 @@ Double click on a screen to edit its settings. - - - - Enable drag and drop file transfers - - - - - - Qt::Vertical + + + Enable clipboard sharing - - - 20 - 16 - + + true - + @@ -651,7 +661,9 @@ Double click on a screen to edit its settings.

TrashScreenWidget.h
- + + + m_pButtonBox diff --git a/src/gui/src/ServerConfig.cpp b/src/gui/src/ServerConfig.cpp index b7c866f73..73c73dd8f 100644 --- a/src/gui/src/ServerConfig.cpp +++ b/src/gui/src/ServerConfig.cpp @@ -51,6 +51,7 @@ ServerConfig::ServerConfig(QSettings* settings, int numColumns, int numRows , m_ServerName(serverName), m_IgnoreAutoConfigClient(false), m_EnableDragAndDrop(false), + m_EnableClipboard(true), m_pMainWindow(mainWindow) { Q_ASSERT(m_pSettings); @@ -246,6 +247,7 @@ QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config) outStream << "\t" << "relativeMouseMoves = " << (config.relativeMouseMoves() ? "true" : "false") << endl; outStream << "\t" << "screenSaverSync = " << (config.screenSaverSync() ? "true" : "false") << endl; outStream << "\t" << "win32KeepForeground = " << (config.win32KeepForeground() ? "true" : "false") << endl; + outStream << "\t" << "enableClipboard = " << (config.enableClipboard() ? "true" : "false") << endl; if (config.hasSwitchDelay()) outStream << "\t" << "switchDelay = " << config.switchDelay() << endl; diff --git a/src/gui/src/ServerConfig.h b/src/gui/src/ServerConfig.h index 29b40fab3..8132b8e86 100644 --- a/src/gui/src/ServerConfig.h +++ b/src/gui/src/ServerConfig.h @@ -62,6 +62,7 @@ class ServerConfig : public BaseConfig const HotkeyList& hotkeys() const { return m_Hotkeys; } bool ignoreAutoConfigClient() const { return m_IgnoreAutoConfigClient; } bool enableDragAndDrop() const { return m_EnableDragAndDrop; } + bool enableClipboard() const { return m_EnableClipboard; } void saveSettings(); void loadSettings(); @@ -90,6 +91,7 @@ class ServerConfig : public BaseConfig void setSwitchCornerSize(int val) { m_SwitchCornerSize = val; } void setIgnoreAutoConfigClient(bool on) { m_IgnoreAutoConfigClient = on; } void setEnableDragAndDrop(bool on) { m_EnableDragAndDrop = on; } + void setEnableClipboard(bool on) { m_EnableClipboard = on; } QList& switchCorners() { return m_SwitchCorners; } HotkeyList& hotkeys() { return m_Hotkeys; } @@ -122,6 +124,7 @@ class ServerConfig : public BaseConfig QString m_ServerName; bool m_IgnoreAutoConfigClient; bool m_EnableDragAndDrop; + bool m_EnableClipboard; MainWindow* m_pMainWindow; }; diff --git a/src/gui/src/ServerConfigDialog.cpp b/src/gui/src/ServerConfigDialog.cpp index 06923a7f5..07d6796f1 100644 --- a/src/gui/src/ServerConfigDialog.cpp +++ b/src/gui/src/ServerConfigDialog.cpp @@ -58,6 +58,8 @@ ServerConfigDialog::ServerConfigDialog(QWidget* parent, ServerConfig& config, co m_pCheckBoxEnableDragAndDrop->setChecked(serverConfig().enableDragAndDrop()); + m_pCheckBoxEnableClipboard->setChecked(serverConfig().enableClipboard()); + foreach(const Hotkey& hotkey, serverConfig().hotkeys()) m_pListHotkeys->addItem(hotkey.text()); @@ -100,6 +102,7 @@ void ServerConfigDialog::accept() serverConfig().setSwitchCornerSize(m_pSpinBoxSwitchCornerSize->value()); serverConfig().setIgnoreAutoConfigClient(m_pCheckBoxIgnoreAutoConfigClient->isChecked()); serverConfig().setEnableDragAndDrop(m_pCheckBoxEnableDragAndDrop->isChecked()); + serverConfig().setEnableClipboard(m_pCheckBoxEnableClipboard->isChecked()); // now that the dialog has been accepted, copy the new server config to the original one, // which is a reference to the one in MainWindow. From 3c94310c9e0f33758c9a25e25aba28e1021ea59d Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Thu, 15 Sep 2016 15:19:17 -0400 Subject: [PATCH 318/572] #123 Read clipboard sharing option and pass it to all clients --- src/lib/client/ServerProxy.cpp | 3 +++ src/lib/server/Config.cpp | 4 ++++ src/lib/synergy/option_types.h | 1 + 3 files changed, 8 insertions(+) diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index 773cdc162..fc84fd21d 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -833,6 +833,9 @@ ServerProxy::setOptions() // update keep alive setKeepAliveRate(1.0e-3 * static_cast(options[i + 1])); } + else if (options[i] == kOptionClipboardSharing) { + LOG((CLOG_DEBUG "disable clipboard sharing")); + } if (id != kKeyModifierIDNull) { m_modifierTranslationTable[id] = static_cast(options[i + 1]); diff --git a/src/lib/server/Config.cpp b/src/lib/server/Config.cpp index 87cf0b7f6..4b87764f2 100644 --- a/src/lib/server/Config.cpp +++ b/src/lib/server/Config.cpp @@ -765,6 +765,10 @@ Config::readSectionOptions(ConfigReadContext& s) else if (name == "win32KeepForeground") { addOption("", kOptionWin32KeepForeground, s.parseBoolean(value)); } + else if (name == "enableClipboard") { + addOption("", kOptionClipboardSharing, s.parseBoolean(value)); + } + else { handled = false; } diff --git a/src/lib/synergy/option_types.h b/src/lib/synergy/option_types.h index ca6345718..3e4328b8e 100644 --- a/src/lib/synergy/option_types.h +++ b/src/lib/synergy/option_types.h @@ -67,6 +67,7 @@ static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU"); static const OptionID kOptionScreenPreserveFocus = OPTION_CODE("SFOC"); static const OptionID kOptionRelativeMouseMoves = OPTION_CODE("MDLT"); static const OptionID kOptionWin32KeepForeground = OPTION_CODE("_KFW"); +static const OptionID kOptionClipboardSharing = OPTION_CODE("CLPS"); //@} //! @name Screen switch corner enumeration From 9c0bac7c7d6f4970c34e33452a5a19e0d328f442 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Thu, 15 Sep 2016 15:21:13 -0400 Subject: [PATCH 319/572] Fixed indentations --- src/lib/synergy/option_types.h | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/lib/synergy/option_types.h b/src/lib/synergy/option_types.h index 3e4328b8e..22f7292b0 100644 --- a/src/lib/synergy/option_types.h +++ b/src/lib/synergy/option_types.h @@ -45,28 +45,28 @@ typedef std::vector OptionsList; //! @name Option identifiers //@{ -static const OptionID kOptionHalfDuplexCapsLock = OPTION_CODE("HDCL"); -static const OptionID kOptionHalfDuplexNumLock = OPTION_CODE("HDNL"); -static const OptionID kOptionHalfDuplexScrollLock = OPTION_CODE("HDSL"); -static const OptionID kOptionModifierMapForShift = OPTION_CODE("MMFS"); -static const OptionID kOptionModifierMapForControl = OPTION_CODE("MMFC"); -static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA"); -static const OptionID kOptionModifierMapForAltGr = OPTION_CODE("MMFG"); -static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM"); -static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR"); -static const OptionID kOptionHeartbeat = OPTION_CODE("HART"); -static const OptionID kOptionScreenSwitchCorners = OPTION_CODE("SSCM"); -static const OptionID kOptionScreenSwitchCornerSize = OPTION_CODE("SSCS"); -static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); -static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT"); +static const OptionID kOptionHalfDuplexCapsLock = OPTION_CODE("HDCL"); +static const OptionID kOptionHalfDuplexNumLock = OPTION_CODE("HDNL"); +static const OptionID kOptionHalfDuplexScrollLock = OPTION_CODE("HDSL"); +static const OptionID kOptionModifierMapForShift = OPTION_CODE("MMFS"); +static const OptionID kOptionModifierMapForControl = OPTION_CODE("MMFC"); +static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA"); +static const OptionID kOptionModifierMapForAltGr = OPTION_CODE("MMFG"); +static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM"); +static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR"); +static const OptionID kOptionHeartbeat = OPTION_CODE("HART"); +static const OptionID kOptionScreenSwitchCorners = OPTION_CODE("SSCM"); +static const OptionID kOptionScreenSwitchCornerSize = OPTION_CODE("SSCS"); +static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT"); +static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT"); static const OptionID kOptionScreenSwitchNeedsShift = OPTION_CODE("SSNS"); static const OptionID kOptionScreenSwitchNeedsControl = OPTION_CODE("SSNC"); static const OptionID kOptionScreenSwitchNeedsAlt = OPTION_CODE("SSNA"); -static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR"); -static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU"); -static const OptionID kOptionScreenPreserveFocus = OPTION_CODE("SFOC"); -static const OptionID kOptionRelativeMouseMoves = OPTION_CODE("MDLT"); -static const OptionID kOptionWin32KeepForeground = OPTION_CODE("_KFW"); +static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR"); +static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU"); +static const OptionID kOptionScreenPreserveFocus = OPTION_CODE("SFOC"); +static const OptionID kOptionRelativeMouseMoves = OPTION_CODE("MDLT"); +static const OptionID kOptionWin32KeepForeground = OPTION_CODE("_KFW"); static const OptionID kOptionClipboardSharing = OPTION_CODE("CLPS"); //@} From 673829f511d976384ed1967a7d754af535f27b2c Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Thu, 15 Sep 2016 16:19:10 -0400 Subject: [PATCH 320/572] #123 Made disable clipboard take effect on server --- src/lib/server/Config.cpp | 8 ++++++-- src/lib/server/Server.cpp | 14 ++++++++++---- src/lib/server/Server.h | 1 + 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/lib/server/Config.cpp b/src/lib/server/Config.cpp index 4b87764f2..dac8f58ea 100644 --- a/src/lib/server/Config.cpp +++ b/src/lib/server/Config.cpp @@ -765,7 +765,7 @@ Config::readSectionOptions(ConfigReadContext& s) else if (name == "win32KeepForeground") { addOption("", kOptionWin32KeepForeground, s.parseBoolean(value)); } - else if (name == "enableClipboard") { + else if (name == "clipboardSharing") { addOption("", kOptionClipboardSharing, s.parseBoolean(value)); } @@ -1380,6 +1380,9 @@ Config::getOptionName(OptionID id) if (id == kOptionScreenPreserveFocus) { return "preserveFocus"; } + if (id == kOptionClipboardSharing) { + return "clipboardSharing"; + } return NULL; } @@ -1396,7 +1399,8 @@ Config::getOptionValue(OptionID id, OptionValue value) id == kOptionXTestXineramaUnaware || id == kOptionRelativeMouseMoves || id == kOptionWin32KeepForeground || - id == kOptionScreenPreserveFocus) { + id == kOptionScreenPreserveFocus || + id == kOptionClipboardSharing) { return (value != 0) ? "true" : "false"; } if (id == kOptionModifierMapForShift || diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index cdec86f42..4e1a728a4 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -92,6 +92,7 @@ Server::Server( m_writeToDropDirThread(NULL), m_ignoreFileTransfer(false), m_enableDragDrop(enableDragDrop), + m_enableClipboard(true), m_sendDragInfoThread(NULL), m_waitDragInfoThread(true) { @@ -485,7 +486,7 @@ Server::switchScreen(BaseClientProxy* dst, // update the primary client's clipboards if we're leaving the // primary screen. - if (m_active == m_primaryClient) { + if (m_active == m_primaryClient && m_enableClipboard) { for (ClipboardID id = 0; id < kClipboardEnd; ++id) { ClipboardInfo& clipboard = m_clipboards[id]; if (clipboard.m_clipboardOwner == getName(m_primaryClient)) { @@ -506,9 +507,11 @@ Server::switchScreen(BaseClientProxy* dst, m_primaryClient->getToggleMask(), forScreensaver); - // send the clipboard data to new active screen - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_active->setClipboard(id, &m_clipboards[id].m_clipboard); + if (m_enableClipboard) { + // send the clipboard data to new active screen + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_active->setClipboard(id, &m_clipboards[id].m_clipboard); + } } Server::SwitchToScreenInfo* info = @@ -1166,6 +1169,9 @@ Server::processOptions() else if (id == kOptionRelativeMouseMoves) { newRelativeMoves = (value != 0); } + else if (id == kOptionClipboardSharing) { + m_enableClipboard = (value != 0); + } } if (m_relativeMoves && !newRelativeMoves) { stopRelativeMoves(); diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index 610718c4c..7681487a5 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -473,6 +473,7 @@ class Server : public INode { String m_dragFileExt; bool m_ignoreFileTransfer; bool m_enableDragDrop; + bool m_enableClipboard; Thread* m_sendDragInfoThread; bool m_waitDragInfoThread; From 59e31c3f05232618bd6d0748cb7de4275e471353 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Thu, 15 Sep 2016 16:23:08 -0400 Subject: [PATCH 321/572] #123 Made clipboard name convention consistent --- src/gui/src/ServerConfig.cpp | 4 ++-- src/gui/src/ServerConfig.h | 6 +++--- src/gui/src/ServerConfigDialog.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gui/src/ServerConfig.cpp b/src/gui/src/ServerConfig.cpp index 73c73dd8f..0f2267a3c 100644 --- a/src/gui/src/ServerConfig.cpp +++ b/src/gui/src/ServerConfig.cpp @@ -51,7 +51,7 @@ ServerConfig::ServerConfig(QSettings* settings, int numColumns, int numRows , m_ServerName(serverName), m_IgnoreAutoConfigClient(false), m_EnableDragAndDrop(false), - m_EnableClipboard(true), + m_ClipboardSharing(true), m_pMainWindow(mainWindow) { Q_ASSERT(m_pSettings); @@ -247,7 +247,7 @@ QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config) outStream << "\t" << "relativeMouseMoves = " << (config.relativeMouseMoves() ? "true" : "false") << endl; outStream << "\t" << "screenSaverSync = " << (config.screenSaverSync() ? "true" : "false") << endl; outStream << "\t" << "win32KeepForeground = " << (config.win32KeepForeground() ? "true" : "false") << endl; - outStream << "\t" << "enableClipboard = " << (config.enableClipboard() ? "true" : "false") << endl; + outStream << "\t" << "clipboardSharing = " << (config.clipboardSharing() ? "true" : "false") << endl; if (config.hasSwitchDelay()) outStream << "\t" << "switchDelay = " << config.switchDelay() << endl; diff --git a/src/gui/src/ServerConfig.h b/src/gui/src/ServerConfig.h index 8132b8e86..136c5f2b0 100644 --- a/src/gui/src/ServerConfig.h +++ b/src/gui/src/ServerConfig.h @@ -62,7 +62,7 @@ class ServerConfig : public BaseConfig const HotkeyList& hotkeys() const { return m_Hotkeys; } bool ignoreAutoConfigClient() const { return m_IgnoreAutoConfigClient; } bool enableDragAndDrop() const { return m_EnableDragAndDrop; } - bool enableClipboard() const { return m_EnableClipboard; } + bool clipboardSharing() const { return m_ClipboardSharing; } void saveSettings(); void loadSettings(); @@ -91,7 +91,7 @@ class ServerConfig : public BaseConfig void setSwitchCornerSize(int val) { m_SwitchCornerSize = val; } void setIgnoreAutoConfigClient(bool on) { m_IgnoreAutoConfigClient = on; } void setEnableDragAndDrop(bool on) { m_EnableDragAndDrop = on; } - void setEnableClipboard(bool on) { m_EnableClipboard = on; } + void setClipboardSharing(bool on) { m_ClipboardSharing = on; } QList& switchCorners() { return m_SwitchCorners; } HotkeyList& hotkeys() { return m_Hotkeys; } @@ -124,7 +124,7 @@ class ServerConfig : public BaseConfig QString m_ServerName; bool m_IgnoreAutoConfigClient; bool m_EnableDragAndDrop; - bool m_EnableClipboard; + bool m_ClipboardSharing; MainWindow* m_pMainWindow; }; diff --git a/src/gui/src/ServerConfigDialog.cpp b/src/gui/src/ServerConfigDialog.cpp index 07d6796f1..c6691286f 100644 --- a/src/gui/src/ServerConfigDialog.cpp +++ b/src/gui/src/ServerConfigDialog.cpp @@ -58,7 +58,7 @@ ServerConfigDialog::ServerConfigDialog(QWidget* parent, ServerConfig& config, co m_pCheckBoxEnableDragAndDrop->setChecked(serverConfig().enableDragAndDrop()); - m_pCheckBoxEnableClipboard->setChecked(serverConfig().enableClipboard()); + m_pCheckBoxEnableClipboard->setChecked(serverConfig().clipboardSharing()); foreach(const Hotkey& hotkey, serverConfig().hotkeys()) m_pListHotkeys->addItem(hotkey.text()); @@ -102,7 +102,7 @@ void ServerConfigDialog::accept() serverConfig().setSwitchCornerSize(m_pSpinBoxSwitchCornerSize->value()); serverConfig().setIgnoreAutoConfigClient(m_pCheckBoxIgnoreAutoConfigClient->isChecked()); serverConfig().setEnableDragAndDrop(m_pCheckBoxEnableDragAndDrop->isChecked()); - serverConfig().setEnableClipboard(m_pCheckBoxEnableClipboard->isChecked()); + serverConfig().setClipboardSharing(m_pCheckBoxEnableClipboard->isChecked()); // now that the dialog has been accepted, copy the new server config to the original one, // which is a reference to the one in MainWindow. From 90673e8b877efb26cb10d6b606189533ff6574b7 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Thu, 15 Sep 2016 16:26:13 -0400 Subject: [PATCH 322/572] #123 Added note about disabling clipboard --- src/lib/server/Server.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 4e1a728a4..af9588892 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -1171,6 +1171,10 @@ Server::processOptions() } else if (id == kOptionClipboardSharing) { m_enableClipboard = (value != 0); + + if (m_enableClipboard == false) { + LOG((CLOG_NOTE "clipboard sharing is disabled")); + } } } if (m_relativeMoves && !newRelativeMoves) { From eafc548b9725211cdd23a71afbba5dc35e16dda0 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Thu, 15 Sep 2016 16:45:36 -0400 Subject: [PATCH 323/572] #123 Made disable clipboard take effect on client --- src/lib/client/Client.cpp | 27 ++++++++++++++++++++++----- src/lib/client/Client.h | 1 + src/lib/client/ServerProxy.cpp | 4 +--- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index b67f64bf8..f01044ea3 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -73,7 +73,8 @@ Client::Client( m_writeToDropDirThread(NULL), m_socket(NULL), m_useSecureNetwork(false), - m_args(args) + m_args(args), + m_enableClipboard(true) { assert(m_socketFactory != NULL); assert(m_screen != NULL); @@ -264,10 +265,12 @@ Client::leave() m_screen->leave(); - // send clipboards that we own and that have changed - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - if (m_ownClipboard[id]) { - sendClipboard(id); + if (m_enableClipboard) { + // send clipboards that we own and that have changed + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_ownClipboard[id]) { + sendClipboard(id); + } } } @@ -360,6 +363,20 @@ Client::resetOptions() void Client::setOptions(const OptionsList& options) { + for (OptionsList::const_iterator index = options.cbegin(); + index != options.cend(); ++index) { + const OptionID id = *index; + if (id == kOptionClipboardSharing) { + index++; + if (*index == static_cast(false)) { + LOG((CLOG_NOTE "clipboard sharing is disabled")); + } + m_enableClipboard = *index; + + break; + } + } + m_screen->setOptions(options); } diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h index b36687834..22740be06 100644 --- a/src/lib/client/Client.h +++ b/src/lib/client/Client.h @@ -223,4 +223,5 @@ class Client : public IClient, public INode { TCPSocket* m_socket; bool m_useSecureNetwork; ClientArgs m_args; + bool m_enableClipboard; }; diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp index fc84fd21d..bf11dd38c 100644 --- a/src/lib/client/ServerProxy.cpp +++ b/src/lib/client/ServerProxy.cpp @@ -833,9 +833,7 @@ ServerProxy::setOptions() // update keep alive setKeepAliveRate(1.0e-3 * static_cast(options[i + 1])); } - else if (options[i] == kOptionClipboardSharing) { - LOG((CLOG_DEBUG "disable clipboard sharing")); - } + if (id != kKeyModifierIDNull) { m_modifierTranslationTable[id] = static_cast(options[i + 1]); From ce4effa2c8618f69af75b411dac84700ce7c7675 Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Thu, 15 Sep 2016 16:51:56 -0400 Subject: [PATCH 324/572] #123 Fixed warning on Linux --- src/lib/client/Client.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index f01044ea3..591b4cab1 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -363,8 +363,8 @@ Client::resetOptions() void Client::setOptions(const OptionsList& options) { - for (OptionsList::const_iterator index = options.cbegin(); - index != options.cend(); ++index) { + for (OptionsList::const_iterator index = options.begin(); + index != options.end(); ++index) { const OptionID id = *index; if (id == kOptionClipboardSharing) { index++; From a17779e7fdce5a4f7d81d0e1b397ab361167327f Mon Sep 17 00:00:00 2001 From: Xinyu Hou Date: Fri, 16 Sep 2016 09:24:46 -0400 Subject: [PATCH 325/572] #123 Disabled clipboard grab mechanism when clipboard is disabled --- src/lib/client/Client.cpp | 4 ++++ src/lib/server/Server.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp index 591b4cab1..748c8cb7c 100644 --- a/src/lib/client/Client.cpp +++ b/src/lib/client/Client.cpp @@ -672,6 +672,10 @@ Client::handleShapeChanged(const Event&, void*) void Client::handleClipboardGrabbed(const Event& event, void*) { + if (!m_enableClipboard) { + return; + } + const IScreen::ClipboardInfo* info = reinterpret_cast(event.getData()); diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index af9588892..b0ff56fc6 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -1219,6 +1219,10 @@ Server::handleShapeChanged(const Event&, void* vclient) void Server::handleClipboardGrabbed(const Event& event, void* vclient) { + if (!m_enableClipboard) { + return; + } + // ignore events from unknown clients BaseClientProxy* grabber = reinterpret_cast(vclient); if (m_clientSet.count(grabber) == 0) { From 6d5b2707c8658355bd006f5741bd32c527be37c5 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 15 Sep 2016 15:46:42 +0100 Subject: [PATCH 326/572] #3305 Add logging for unknown macOS events --- src/lib/platform/OSXScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index 182d6e532..2a4c5c4af 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -1941,7 +1941,7 @@ OSXScreen::handleCGInputEvent(CGEventTapProxy proxy, case NX_NULLEVENT: break; case NX_SYSDEFINED: - // Unknown, forward it + LOG((CLOG_DEBUG2 "unknown system defined event")); return event; break; case NX_NUMPROCS: From 6294679fcd210f8171bf220841f29c26679d6a20 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Thu, 15 Sep 2016 16:39:10 +0100 Subject: [PATCH 327/572] #3305 Detect media keys on macOS server --- src/lib/platform/OSXMediaKeySimulator.h | 3 + src/lib/platform/OSXMediaKeySimulator.m | 108 +++++++++++++++++++++++++------- src/lib/platform/OSXScreen.cpp | 27 +++++++- src/lib/platform/OSXScreen.h | 2 + 4 files changed, 115 insertions(+), 25 deletions(-) diff --git a/src/lib/platform/OSXMediaKeySimulator.h b/src/lib/platform/OSXMediaKeySimulator.h index 277778df0..c62a19bc6 100644 --- a/src/lib/platform/OSXMediaKeySimulator.h +++ b/src/lib/platform/OSXMediaKeySimulator.h @@ -18,6 +18,7 @@ #pragma once #import +#import #include "synergy/key_types.h" @@ -25,6 +26,8 @@ extern "C" { #endif bool fakeNativeMediaKey(KeyID id); +bool isMediaKeyEvent(CGEventRef event); +bool getMediaKeyEventInfo(CGEventRef event, KeyID* keyId, bool* down, bool* isRepeat); #if defined(__cplusplus) } #endif diff --git a/src/lib/platform/OSXMediaKeySimulator.m b/src/lib/platform/OSXMediaKeySimulator.m index 646807e31..1106a2ea1 100644 --- a/src/lib/platform/OSXMediaKeySimulator.m +++ b/src/lib/platform/OSXMediaKeySimulator.m @@ -13,52 +13,40 @@ */ #import "platform/OSXMediaKeySimulator.h" - #import +#import int convertKeyIDToNXKeyType(KeyID id) { - // hidsystem/ev_keymap.h - // NX_KEYTYPE_SOUND_UP 0 - // NX_KEYTYPE_SOUND_DOWN 1 - // NX_KEYTYPE_BRIGHTNESS_UP 2 - // NX_KEYTYPE_BRIGHTNESS_DOWN 3 - // NX_KEYTYPE_MUTE 7 - // NX_KEYTYPE_EJECT 14 - // NX_KEYTYPE_PLAY 16 - // NX_KEYTYPE_NEXT 17 - // NX_KEYTYPE_PREVIOUS 18 - // NX_KEYTYPE_FAST 19 - // NX_KEYTYPE_REWIND 20 - int type = -1; + switch (id) { case kKeyAudioUp: - type = 0; + type = NX_KEYTYPE_SOUND_UP; break; case kKeyAudioDown: - type = 1; + type = NX_KEYTYPE_SOUND_DOWN; break; case kKeyBrightnessUp: - type = 2; + type = NX_KEYTYPE_BRIGHTNESS_UP; break; case kKeyBrightnessDown: - type = 3; + type = NX_KEYTYPE_BRIGHTNESS_DOWN; break; case kKeyAudioMute: - type = 7; + type = NX_KEYTYPE_MUTE; break; case kKeyEject: - type = 14; + type = NX_KEYTYPE_EJECT; break; case kKeyAudioPlay: - type = 16; + type = NX_KEYTYPE_PLAY; break; case kKeyAudioNext: - type = 17; + type = NX_KEYTYPE_NEXT; break; case kKeyAudioPrev: - type = 18; + type = NX_KEYTYPE_PREVIOUS; break; default: break; @@ -67,6 +55,80 @@ int convertKeyIDToNXKeyType(KeyID id) return type; } +static KeyID +convertNXKeyTypeToKeyID(uint32_t const type) +{ + KeyID id = 0; + + switch (type) { + case NX_KEYTYPE_SOUND_UP: + id = kKeyAudioUp; + break; + case NX_KEYTYPE_SOUND_DOWN: + id = kKeyAudioDown; + break; + case NX_KEYTYPE_MUTE: + id = kKeyAudioMute; + break; + case NX_KEYTYPE_EJECT: + id = kKeyEject; + break; + case NX_KEYTYPE_PLAY: + id = kKeyAudioPlay; + break; + case NX_KEYTYPE_FAST: + case NX_KEYTYPE_NEXT: + id = kKeyAudioNext; + break; + case NX_KEYTYPE_REWIND: + case NX_KEYTYPE_PREVIOUS: + id = kKeyAudioPrev; + break; + default: + break; + } + + return id; +} + +bool +isMediaKeyEvent(CGEventRef event) { + NSEvent* nsEvent = nil; + @try { + nsEvent = [NSEvent eventWithCGEvent: event]; + if ([nsEvent subtype] != 8) { + return false; + } + uint32_t const nxKeyId = ([nsEvent data1] & 0xFFFF0000) >> 16; + if (convertNXKeyTypeToKeyID (nxKeyId)) { + return true; + } + } @catch (NSException* e) { + } + return false; +} + +bool +getMediaKeyEventInfo(CGEventRef event, KeyID* const keyId, + bool* const down, bool* const isRepeat) { + NSEvent* nsEvent = nil; + @try { + nsEvent = [NSEvent eventWithCGEvent: event]; + } @catch (NSException* e) { + return false; + } + if (keyId) { + *keyId = convertNXKeyTypeToKeyID (([nsEvent data1] & 0xFFFF0000) >> 16); + } + if (down) { + *down = !([nsEvent data1] & 0x100); + } + if (isRepeat) { + *isRepeat = [nsEvent data1] & 0x1; + } + return true; +} + bool fakeNativeMediaKey(KeyID id) { diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index 2a4c5c4af..da18190e3 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -25,6 +25,7 @@ #include "platform/OSXKeyState.h" #include "platform/OSXScreenSaver.h" #include "platform/OSXDragSimulator.h" +#include "platform/OSXMediaKeySimulator.h" #include "platform/OSXPasteboardPeeker.h" #include "synergy/Clipboard.h" #include "synergy/KeyMap.h" @@ -1333,6 +1334,23 @@ OSXScreen::onKey(CGEventRef event) return true; } +void +OSXScreen::onMediaKey(CGEventRef event) +{ + KeyID keyID; + bool down; + bool isRepeat; + + if (!getMediaKeyEventInfo (event, &keyID, &down, &isRepeat)) { + LOG ((CLOG_ERR "Failed to decode media key event")); + return; + } + + LOG ((CLOG_DEBUG2 "Media key event: keyID=0x%02x, %s, repeat=%s", + keyID, (down ? "down": "up"), + (isRepeat ? "yes" : "no"))); +} + bool OSXScreen::onHotKey(EventRef event) const { @@ -1941,8 +1959,13 @@ OSXScreen::handleCGInputEvent(CGEventTapProxy proxy, case NX_NULLEVENT: break; case NX_SYSDEFINED: - LOG((CLOG_DEBUG2 "unknown system defined event")); - return event; + if (isMediaKeyEvent (event)) { + LOG((CLOG_DEBUG2 "detected media key event")); + screen->onMediaKey (event); + } else { + LOG((CLOG_DEBUG2 "ignoring unknown system defined event")); + return event; + } break; case NX_NUMPROCS: break; diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h index 77a0cedc5..dce5ac088 100644 --- a/src/lib/platform/OSXScreen.h +++ b/src/lib/platform/OSXScreen.h @@ -128,6 +128,8 @@ class OSXScreen : public PlatformScreen { void constructMouseButtonEventMap(); bool onKey(CGEventRef event); + + void onMediaKey(CGEventRef event); bool onHotKey(EventRef event) const; From 1b0d4308e26f419a05164b46220e1b8987742853 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 16 Sep 2016 16:30:49 +0100 Subject: [PATCH 328/572] #3305 Map brightness keys correctly on macOS servers --- src/lib/platform/OSXKeyState.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index ab96282b8..69a689c6e 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -35,6 +35,8 @@ static const UInt32 s_superVK = kVK_Command; static const UInt32 s_capsLockVK = kVK_CapsLock; static const UInt32 s_numLockVK = kVK_ANSI_KeypadClear; // 71 +static const UInt32 s_brightnessUp = 144; +static const UInt32 s_brightnessDown = 145; static const UInt32 s_missionControlVK = 160; static const UInt32 s_launchpadVK = 131; @@ -117,7 +119,9 @@ static const KeyEntry s_controlKeys[] = { { kKeyCapsLock, s_capsLockVK }, { kKeyMissionControl, s_missionControlVK }, - { kKeyLaunchpad, s_launchpadVK } + { kKeyLaunchpad, s_launchpadVK }, + { kKeyBrightnessUp, s_brightnessUp }, + { kKeyBrightnessDown, s_brightnessDown } }; From e76b4a774747bd16886ca62f2e8bab866722bb43 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 16 Sep 2016 17:11:22 +0100 Subject: [PATCH 329/572] #3305 Send macOS media key events to clients --- src/lib/platform/OSXScreen.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index da18190e3..e4b80e375 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -1349,6 +1349,10 @@ OSXScreen::onMediaKey(CGEventRef event) LOG ((CLOG_DEBUG2 "Media key event: keyID=0x%02x, %s, repeat=%s", keyID, (down ? "down": "up"), (isRepeat ? "yes" : "no"))); + + KeyButton button = 0; + KeyModifierMask mask = m_keyState->getActiveModifiers(); + m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat, keyID, mask, 1, button); } bool From c418d66f60e382dd45e51281347798bd832d3afa Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Fri, 16 Sep 2016 17:20:31 +0100 Subject: [PATCH 330/572] #3305 Rename macOS MediaKeySimulator to MediaKeySupport --- src/lib/platform/OSXKeyState.cpp | 2 +- src/lib/platform/{OSXMediaKeySimulator.h => OSXMediaKeySupport.h} | 0 src/lib/platform/{OSXMediaKeySimulator.m => OSXMediaKeySupport.m} | 2 +- src/lib/platform/OSXScreen.cpp | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/lib/platform/{OSXMediaKeySimulator.h => OSXMediaKeySupport.h} (100%) rename src/lib/platform/{OSXMediaKeySimulator.m => OSXMediaKeySupport.m} (98%) diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 69a689c6e..2071621bf 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -18,7 +18,7 @@ #include "platform/OSXKeyState.h" #include "platform/OSXUchrKeyResource.h" -#include "platform/OSXMediaKeySimulator.h" +#include "platform/OSXMediaKeySupport.h" #include "arch/Arch.h" #include "base/Log.h" diff --git a/src/lib/platform/OSXMediaKeySimulator.h b/src/lib/platform/OSXMediaKeySupport.h similarity index 100% rename from src/lib/platform/OSXMediaKeySimulator.h rename to src/lib/platform/OSXMediaKeySupport.h diff --git a/src/lib/platform/OSXMediaKeySimulator.m b/src/lib/platform/OSXMediaKeySupport.m similarity index 98% rename from src/lib/platform/OSXMediaKeySimulator.m rename to src/lib/platform/OSXMediaKeySupport.m index 1106a2ea1..514f0daf2 100644 --- a/src/lib/platform/OSXMediaKeySimulator.m +++ b/src/lib/platform/OSXMediaKeySupport.m @@ -12,7 +12,7 @@ * GNU General Public License for more details. */ -#import "platform/OSXMediaKeySimulator.h" +#import "platform/OSXMediaKeySupport.h" #import #import diff --git a/src/lib/platform/OSXScreen.cpp b/src/lib/platform/OSXScreen.cpp index e4b80e375..7ca761385 100644 --- a/src/lib/platform/OSXScreen.cpp +++ b/src/lib/platform/OSXScreen.cpp @@ -25,7 +25,7 @@ #include "platform/OSXKeyState.h" #include "platform/OSXScreenSaver.h" #include "platform/OSXDragSimulator.h" -#include "platform/OSXMediaKeySimulator.h" +#include "platform/OSXMediaKeySupport.h" #include "platform/OSXPasteboardPeeker.h" #include "synergy/Clipboard.h" #include "synergy/KeyMap.h" From d77f35b952a28eb077392c243ea9ea9c15b8ba85 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 19 Sep 2016 15:41:45 +0100 Subject: [PATCH 331/572] Update Changelog --- ChangeLog | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4cefb2049..270668f52 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,14 @@ v1.8.3-stable ============= -Bug #2765 - A letter is typed when spacebar is pressed on Mac client +Bug #2765 - A letter appears on macOS clients when the spacebar is pressed +Bug #3241 - Windows UAC disconnects clients when elevated +Bug #4740 - Linux client crashes with "Assertion '!m_open' failed" +Bug #4879 - Memory leak caused by IpcReader Bug #5373 - Tab behaves like shift tab on client -Bug #3241 - UAC causes discconnection while in elevate mode -Enhancement #4323 - Make auto-elevate optional -Enhancement #3305 - Media key support for Mac clients +Bug #5502 - Copy and paste from server to client doesn't work +Enhancement #123 - Option to disable clipboard sharing +Enhancement #3305 - Media key support on macOS +Enhancement #4323 - Make automatic elevation on Windows optional v1.8.2-stable ============= From 91c01d3a45f836817513454fd8affbf21172a191 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 19 Sep 2016 16:20:49 +0100 Subject: [PATCH 332/572] Version 1.8.3-rc2 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71708bbf9..40a6a647c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 3) -set(VERSION_STAGE rc1) +set(VERSION_STAGE rc2) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From db9181bd11a8dfdbefcb0740333c9052072f33f5 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Fri, 23 Sep 2016 10:20:19 -0400 Subject: [PATCH 333/572] Versioned to 1.8.3-stable --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 40a6a647c..963d84e36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ set(VERSION_MAJOR 1) set(VERSION_MINOR 8) set(VERSION_REV 3) -set(VERSION_STAGE rc2) +set(VERSION_STAGE stable) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REV}") cmake_minimum_required(VERSION 2.6) From f58e95c96f4c88e968f4b6ce8306034d34787963 Mon Sep 17 00:00:00 2001 From: Andrew Nelless Date: Mon, 26 Sep 2016 15:46:57 +0100 Subject: [PATCH 334/572] 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 335/572] #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 336/572] #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 337/572] #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 338/572] #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 339/572] #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 340/572] #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 341/572] #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 342/572] #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 343/572] #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 344/572] #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 345/572] #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 346/572] #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 347/572] #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 348/572] #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 349/572] #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 350/572] #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 351/572] #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 352/572] #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 353/572] #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 354/572] #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 355/572] #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 356/572] #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 357/572] #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 358/572] #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 359/572] #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 360/572] #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 361/572] #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 362/572] #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 363/572] #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 364/572] #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 365/572] #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 366/572] #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 367/572] #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 368/572] #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 369/572] 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 370/572] 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 371/572] #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 372/572] #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 373/572] #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 374/572] #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 375/572] #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 376/572] #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 377/572] #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 378/572] #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 379/572] #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 380/572] #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 381/572] #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 382/572] #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 383/572] #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 384/572] #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 385/572] #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 386/572] #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 387/572] #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 388/572] #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 389/572] #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 390/572] #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 391/572] #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 392/572] 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 393/572] #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 394/572] 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 395/572] 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 396/572] 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 397/572] 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 398/572] 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 399/572] 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 400/572] 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 401/572] 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 402/572] 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 403/572] 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 404/572] 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 405/572] 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 406/572] 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 407/572] 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 408/572] 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 409/572] 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 410/572] 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 411/572] 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 412/572] 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 413/572] 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 414/572] 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 415/572] 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 416/572] 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 417/572] 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 418/572] 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 419/572] 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 420/572] 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 421/572] 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 422/572] #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 423/572] #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 424/572] 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 425/572] #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 zcmeFa2XK^Uwk_)W>b!b&ea=1Ou`wA$HqJK20b`pS3?|u_Xbc8S2Ahl!2!SL}4k+iG zQ>(kxYDpcWR?a!+074=_| zJo5iOBL4l4M1=P`(bCjp-cVO(^@E_is!o!M>Uvbw zHln7k-dad~eZ5s=D2s53L5Nlrm*Xc&CFyx`#Egp(Jnu>)YDec2rhZyP5LxkfK&0Br*ano*p=V=^`wS9>ej2hj3uu zKJ4Cm*QPhouGb&fi?s*$V*6QheCBWw=l#9m5)lfI=m_{k-Ic(YC`6|wBSoV^qAD3t zaghj%4nk~PD57Hn5gG1-zyN2syIsKfbNqjf?!jjVckut(hP`_}reF97yXaT;AJ~Hv zCyz?;f}6WD0s?)JkQk55tPJ{sV${{wT8o%K_5-pXxREdS;I?VecsK*8RlnBW9MfFu>R0q%-y>kuQE4ZV0RX~v8U2jBF ze+^o$)}WU^d#$?&*IU!kUmuODb$;lsbwpR~C3H7jL~oNFuGG7szs?8OYeI3WJ_6Ue zqHv=x4uhRZ=xSSc) z8&O-!+@QC2(XZuN%72IKmwv7e+_`mgSbuA?d0Dmrd0ASdCC0(q#RW$W9mJN+oABX= zb@+(3bIR2LZW&7WRpi6Hp%jkoRj}!AN+Jtf%LS&B{FrFg%q9Pf6O zVR83enaAhm^p;>A-?xH(U@v{c#jEv<|65ToP>;r|)o9{#T|H&!YtN&Nr=q(u6kQb_ zXsfu0)`|;gt+GN}^?7tu*rTJ=1wF++xKieix-xgP*Zbp2ODwK7B}?|-UZp`}aXQMf zH87?nBPKKmrx?F{xNa>rZP|={hY#WGrStFz@`1>0Dk>`6YHF+PJG(kI{@m|QexGb- z526m-{`$*DMIG!Kd-V;}`}8z5f}_G=X@3zLKHG;kHm$|t;|H-L#0%TwgRwg+1xIT0 zab}4c|XP*0K(O!2DtqnWS(zr#E zw)$;otJ{Ij+TDE5Av9GVLvy7CT52uPQE!W`CP(x&dttCE5_hht(BGj#XM>vlEfk4S zml5LQ0E^?FW5-7uvG3DeIDh^;LPJB5lB_^kX?aw0Yn!>i8Qr?`g^Vqd@dUprJjIP~ zz8uzj?dqcXhC1YCWg$L19QLf?A3c2%n?Bu+9jA|DhwV8Wj|_xQZ85?bpLv72%ccVoClM`wwKDK-hodAkTm14_#La(bKL$ zcYO%Ds-4hTa|Ug-2hdW#9ZmI{C26Vs7|qq&&|0|z?G^iI_a{(Wd>rKkhf!U03bmz{ zs4KSRbIzzK@j^p+xMcrV+H#m%RM2-6A|Wai7c5TU6nhs34jjPFojY;H;w%FE0}&ga zfReIubaZuZ6qv&Y(Z|cbvmBfJr4DrVU)@;Q*x;6xYeHmV0-W4kag6!!@`JlE=fpw0 zed==@%1nX7Kr5{7bYkCNGuASXU(!>KH+xF)Dr55(uax73s}*>far~s-Voc~R#N;a# znACUotQY6~B%bv{GL=9340C{&=__91XUyX3i~Gv3y{8gquGGMfHNh;_{j0lMP}5tF zcIN(FEotbf4rcsshrTLvT&dZQp1P0FRsW$R-Sz7k7i^?|*n;k=Pta1n6>XKDpuK9l zByH8atzo=Svzs{sB!@7EUR+ZP+`D+Y%cm)OG zl+`)xJ#+wDwzF5}=77t^+3>F~hg(}U&a+2%sG}TPJ1g)W>+JK|3h{bp5oYqNe~$6} zw2lHyWR5<*GhecQVfzz%%P@_v3zjc|q>)X&#yOZbqUdjI3s*j_VaY0?t1ytudqRMoc^#j%l z>eA3wn2L&0J#r0cNKOpLr=NU;O&iwZjJXAzUESFKNQTM8K4f#J`ORBj%J^bAA9$eh zfv>I&PQTL9cBrPX7%53fuyVA=I%0B`9omCE?hZJWmVhI51vt(+{{Eg?Y-lTGY+sId zI*KrhXZ=fk%;RbELY|`CPU|Yd)4Y9#kDuk){vt)#^-JxAn9*7!$uq5m5Hh_b7f&^t z@KReIX7K*Y?Kya@-Gn)vIauG3hl4%EaAiEM>Zw6NTNR4vYie8dXsJs4Y>w;i21d(dUti{A2`=qcZZuF{XuQL+gg2FHUK!~R|%uk-guEU>U zx8-S=v+f;`r$=OWBdmA^AMC2c?v6@qY%jy=-b%dLl82eX)_KlP?JSgRU-$rF|4)g> zlxginnBH1|XWH`dLTf%=YA(QwP5GGKoR6oP@-VF_2U8l1c(&1q=j#o;&BDt~dVaPJ z?>8H*v!G4li3e|Py7#snMbKNug-_PeV$vQDs(HoqOMVMOhtOPj z42_i5LMt?u2eC#Fi>AtWTwi2(i>hlBm#K#RM@-lFivG(cyM(kuCej#J^ zx0suY82=U8^>dvCc)F_qPf?~Z#uvBG@;M>Tx98(Ip64&LnDBga4xVeu#|bU4$RjcEFVtY!@i z%}NwCM5Ch8hjF?!Y7B=_o3#VAx{avKT#IV$YDvWXx{UQ`NZ*8d%|_H2K14(IdNk*5 zLTmmwTK>F_V$2e`c?V;7=Jr3ee_{Jm z8LNvpV0v3Fo^8#=)3keWra#kQz|-}5OsvesyGju6+57z^fIwV@#t;Q5?hdl40RM-9IZKbZ~(`T zTfoCR0Gjk1&KtH7FEA*_0)Mtt#7c(GiJlv+Z%gM9LHMD#H@2?eLEuw z$JoEyeWMBM25KcN&a1TR=R0zl+w+X?W}Hu%(v|z8Ozz5IoS%cKtvbf}Iy}{qfoaWJ zOl?ZX`EA7 z-~BvZw} zP7+(}?&gWKbR#OOo81KG!NZho{08WZrpLn;EoVmMPo)5>) zTAXB^Z5``!OPQ;`)m4bs7~8+l$#eY&`xmx9xzmJ6%z-DiXTz+GXM1xNCN^p@g(7Tz zBC)k*HK`a^m4cCskB9SYf3hMPBg&#Msw4s}wVwm_`Zt^1Jr7T8a>QWS^u0W}370Of}pi2FrBz4-2k}s&& zZ9-%ACN$-3L~}lK1?C4rgb!#h+KQG!_6~~nqNVr=6_f4=@dtUhS)+O_Fb#PKPJz}a+?klTQV`B z@vgm3V%|QbmN|S)A|_SxjHkVevwdVq2u2hKV?w?!rsewJDU&yC-AlTCI?op`n*#7Q zZGTmMIMx?MV@pL6b}=vbyeR{=ZP^Im?~%%WU^eH4Dmxlb%f4eP^O3eD9eaDptkDId zCD#KL85d~N=EzPs2z}HJ+Wr>k!q!6@^Z_ygRzerPR1!nPyU31Oj$FnH`SFYk65dB? z(pr=ze}Hnuhp14mLzQL&YFIm{%c4&(Zl*u@2(2PNU~bTu_o3t$I?J~+9@xeIpMA$N zb15(Ata2y5I2t`o3K+BjaI~RsJ@zRs*`7swd^`#Z3-#UPIlE0h!-rK5<-hwM?*p$~ zxiVYu=qZwuanaEMyJ`D-ZO+3^t3)7c`!>YXd`isTD)#f{uy*$n&->?i_KUoJVw*{l zX>A6^_HQmjq zXCI=S^8;P<4ObXz^wq?nzbX`+B_6ElIik*Zp7lU;+V4^3|NCg;+hB@ZFWG)(=wkW< z#s-1&krBKQdisRisP|A1y9)WSD^M7}3dJdFP?}0#z}i7oI%@}6^aJ{hs5gBe`GDqv z^=Qo9z!-ozL%|L-=I`SDURV$hnQPQNlk^jhX+1kT)N5iB0e{&iLI%{o~yMIPIo?gcCWUY@G^Vx zFS33oYIx7Eem|YhO>Gi&yiC^dGBAa8I8no!$oSrj@%?z(?u4>fOehY=MCR?2^8+wB z#}`wxJ%11n-g;nWjyK*k`C<-VpI;P=MdcBAw>k#z*Ct{cV~5Xq<{zOyIo_B7bNU%8 z#vsnb`9#vz(}*$3XI@gnencG~H@6j_y;+Y={w`gN0lMgSn)!dXV`H zFt5LeTJ>2}B_Bm$%r4}Ie+)z5I%N2*M27FX$O>2reaKQ6!E2z@0MVan>#`_ykmoMyp9a`uIn)BC5_nY%Jqak-YpJR`p;4EtLFA-A| zj4t*nN^^q|>}SK8;Bi=4Ss@}Kf_TA_sE&?~jkj*ylH&lmE_mPSf}+P)Q&VFtX4f2@ zopF>pc*x!s&ZZ2wGoC)tS%r@{D=%{Ww`uRMG-u=0_5!KDH=RAZr&zy#s)haeMgykT zY4KETIwn>#x3A=x&p3TdSpr6v@VqaI#Mt~`j57sde6}~t^q!cccb8lI*{(yB3X{ zCu(OtAkP0T;s84NdvugWGcFHiZ_iJ%|EBEAXfisZS$_#l%nO<{7HCXAfx6U#D2>|z zQ}}uqf>uiQpAoPS>Hc$&89bNqz+7ZU&xa{?Ax!a$k(;mt1@r}?UQnE|3MHAVQL0^}t>g_W)ujcLOjAdt!{dk=VCS*8aip~*F8C~#fo;z!Lo|vB>iRA^+SX~r{4@(lTxik)& zN@B5r{^Ns^NUWw$SXCOv+fb~ljKC+=;n-UhfzRt>VcnR7zy=jk*fUf!UNDtMp}4@0 zu{mdVG*+lqoknHq2~;SKvln;@RoXMCNI!?lbZb;=tWlxlZK@s060K2^Xu+J|F!T|h zKohbS%Aog<;g2YlBW&VE8VQe*w$WP1!}&KyEWYygUC zixu@fEmniyd}($+^g#a1htI!U`}XVU9XAFyR<*Z6la+};cMsT}K7+l1o;X^R3H!c! z;`Yn1vMw8QiO~`D{izLEnA*bpo-&2C_Q{Qztmmh}jJ>z?{4vddSB%Yc!o*DHySATu87~%jVMeJh=H*3Ti76aQv%|2Q zvL+`4tFwc#!sw4B24Bq8UB=rPu6R@9fVa}@u~>5ntJ58@k@3SJ<_#CKgW#hJL_nqo zVw4U@h_!$+;2=`Hwj#xS0~D9nBiZdkBzbLw(*I+qgFa;)@GuG!%~6zi76s9FMc99- z;w0nzeaMdc7@1LPp$T3l*}u|fE`(_42ekeRU<_nV5X3%W$TGfn0df=BKS*I8k$J(8 z9}su~fi0@fS%(G_YX!L<@&0zk0(;SvLmY9@SzHrXfpQ&cOLRz$_ajfzNu0fCj-c3J z{=bx5Vt#< zy|kBGnb)(AC(ipREwp>ix=m@+5QC?}E0scus4L zXDHLO7cf5E3Zv32@muv7NrtH{F(&;yrfY5RdZr!bFoyV$`MhPWJG>1p@X5M_VC7lv z062i?fUStVyc+S&OA+fh7crOKLag0eNV0!hk~HV}FkW7Y?90oM<@bS<`xi!SMR~%f zD2?5T;^^%tj@XX8;4Lutu1A&^eZl4TpmtvZwI^c%;RgcWk#Yk=*ka^FzAM!UixO6& zBkW84i*>pJ9V%H0Lpm zwmUiHG$tgQW4z)tMky^Yobq_uSqx*$J|@c+la2OxImekX!(}Wj^1+tk09+!6Pe`E; z!t&h_r?*3j>J*eQ`=JWjj5Oa5kmR-qNwoJQ=Xp>#%|*hc*+{gR1%>r2BwD_XxJ&bp zV7~xLw`I_JuSTZN2Rz?5pdfUsB>5p9!xXp~I^Xx9@pu=?%X6Ugd>d-tH<2FjHZp_e zLl?48suh}|mi_1lN)@aTs#i+*!s;yc42UhPHEv>!U@K}3#3dVdqh5a)Wm(o}Vx6Ru zJ&uM7?mvk0gQegeb##Q-Pte%hYPQ1$_GJ@6OT1AANB4pkNw=@TL%v4cInjZQW= zRhWSjoT2@Yv$sn*OZNtGG|$y)@icKe(R0Vd=@@e5ewkC7P;OVCCT&IglwM;(0Q$a`to8Z zT<0M9@@y#R2Q;2@k?y&Weq#yr{>%r0mm@EH75fS+Q5?SlWvQzq{IS3p3qMdRasm+x zm^Slv3vYL$A?F0K0?wQ-j6*{SdCdJU;{x}Yc)GbF+n7Z@;|9w+cLvSmGyCB>v+u?A z{$gNodJ}i-#D_=Vy!mN-ZhHYv#&ld}&HO-9KHlSO?R;YMXEhn{d|d{6dKuE0KdwPT zY_1w(Y5U`=l3-RAjR`#O$FrV4hS;2uMsEyfU2hCy`cXXNM`T{YlUiF0)1Jo&#_1!p zR*cuJFrJSmF`l2SxqvBZD@<0L!<57`n3`}_lK-VSg~u{1FiLlh{eknCqPNAf1_!*9 z=Yls1y)m~k81Gd_V?#qC_B5+;qAde<-Pv%xQiyPJh^WXNl;2x`;+_J1RcH2%M&b`z zI3Lhf$}_bv2=$CPD%Iyvlysc^{=LvMH_-WPhS7ulKIhd?y3A!xFq?6~EW|PB-cJkn~^FczpWZlEvN%-fy3J&Fc_5nvqEUY&-r zoDBH7xxt1!3;upyk`L(ZZI^2Sat!b{#Q-<251Lg~SGsA^(_rajhh28(aZ0bi`Mw(L zAlJs679(C{ZGS53dLqsjx&C<8_eL`o9b1=-3H6NkD`PN`b^Xc2>rE*L#H3vI@z}Q? z%Q$~DfpW;cKLyFcUC`0nun117G>!GgJGXMH&1Ih~($j%B! zrqTLChVjb2X!e$X7t=~qZ%^QfcnknTAsrK(ckEx>UxluqwJ< z+H&RutE66Gtx?1S>=~MfDbCx=nt%mo2Yfh3s7A3a8bO{8xODzBB0>XERG8a#o&4(e z;!Kb~#3^*te@iPwFiD4aQdD8@+fzKHdGez1Kv#`jJ<;~g-HXZ_eTD~u$bX0+-IMyt(X zmUf!LJf1zb(TZajqd0|8LTLLV6&4tuW+T-DpQayqmA(JD1>smzDb9B-F7y?^|5gK% zzUhSi+kO;&H>j`s!&g@A_;$xt{9(p*{L$=h)!*NLb9KhIUv_T%_D-|;?ZImMf$qGL zR?Zw&M=bvi=KAk)R&O@3G%qw~u(nU^j?~jrVqC3)Gxv!+^NHCli-cK; zsO|e=f;jV;=bN#1H#W;2qcbjHBbF7!0|1lxy*sqeLgZhh$Z%0gzUhjFoi8ge)Mt_ zFcv5whEU8QS80hE(!PiKtW^>RK~44!RORePRUT)F%l*hpu0UFfFYGT`;~v|24O z&Yf}|Am;)9Z60vv+Q9VsD#rb(3Y@uQgU|ikaW+?jQ#|*#5<54aoEx) zGGzPWR@lDq0TbA}doJT5USrNM$K-<*ob}t%qQc?6Tx8w4ippZ=N_cw=q|LyHZzrV#DpM5pf-TY2!ii1#)aUSaEok;Lqg*evx;@Lw?U~Mqz z;#-paC!L)Q6@7sE!dxWV&-yb_*#F4~Xk3{Sco0MAJs(;A>=A_07cdscja!7Gly^Co z^e*R$mZCa+xl|i0&-xf8S)Zai}iw!ap-T5 zZ-4!<{NH+wyIAj6rl-Ni%N?7Y$vt6G;Q)Da7qX}SYL5v|v$ppv<*6Daar><86K6Yt zc0Z{+hIxJ%CUbUfA~CZQ+0z?OyzB(MBgV15H-_~+5$}&kJBKG}_aoV#8?y1=q#Va% ziWB%v>Pg<7#xN!Gebog_Wsd)r#tm!qe%ND*f_0e^QRMB)y3>Z1@2(&ETmQEE`Tnnd zcin8@X3L?D_FUwfBA`yNMIz&YIImrZb>4(H`;~~gFduQ}XCeLq`v&J`BA&TH0{e-H zmtJT8fIWmuuOiv*HT=mB&deqaUbgX|G#84r+$ zq#|oADs)>=olE|r0z2}T$B>&m386u*xXe8&v2jr-D=${u_~M#e3y|Z0|050}`-S~atiBuXk7Ip*Jm>Sw3W8up z?Cv-t&wZ)JXOFQw^GB!KU}V|_#`l(ZQgs$jh%;Ydjwe%&VHoFVgzZ1BWbB`2!MK3^ zdiDS(vwt^(vBQeOFzl{Pfi-ynB5u?m|NDWGT6}GN4gd8or^DZaF~J|YSKS;ejp}LU zZXeDj=B8M2K5!p<|HKkHE{D=#K2nG&iaY-rbAs0pM_&+c`>Nyv5-+l5X!{BhZC^u* z-7Kg?O@OsQ4SNQ{2W0v!fPr`c!9O5;Kz`yf6pGs55B-5s?HZJjbD%hDFG}?1P|CUJ z%AypQjH#R%bm1OCNA4v~M{`HL)vd4Op5XmF4<5A7{(|%4b&6Nu&cQ(r$6lW6;sdG1f3St8bRCt}^!B7(huh>P?6Lyn1FG>r8gu$A^Ha@r+M@P3y2|NAAxfPLztoLsFj>ATumRq-e4;6LaLRhP9s(z zVjx3A9`J%Q)pxo^gEe+={_xaOKidCL-aj(+v}FI&I8!rQ>xR_@5!hFugd2U2;_F`2 z;2Z0!_~Qe)H~ahB-K(y*7w8-F!eC6WfJ)$tJy#&cVG$C}za!aywB_AcK-hlFg&E8X zW=i&-#M)uID2W{sm?3SzTnRg<4_^*L#CsAaSy>9}g31+=52(zHnpUMocbkj}kbTNspCWq0wbj-3Vev6IPTT)n5ebXt zBJ60*#bR=2&!YXmP|diXbMe#I-y2gNPY#|K&ginnpC3p}j}L2no~-Y?^2~R@Sj~{_ z-#zz-^V}bqdiUHH{k*ZsCnb4;Gc)3~sjLNx8J#s*J~%=wo-eUQVmD9wcVGW%>eXLf zYp`tQjB|n31&XMni1OUbTwpa)h!cuC_YUH$W+V2(OvIdjg|Wch96;0u5`_<7z94*n zg8c%OJMqIlZxMU&4$_0(MMelQgOQveBwnyKX$9&S3)CvtNIWC?s!gacl25GA5zV#n zFzF-V?QD-o?{E|uN^p(*O|lK#-*fdr_wYw3_^M}s;Cx3|f zMchA{asQD04`VDaMtcq~WjkRx`vtqI65-Uvc>mj8wBZjst^p6^ng7?X`Rzyl_;rKj zjSe+2dalsM9D?3!GtwPbBH3mE5-z-rc$?QG`;W1DS&9K-tX`G!f*~J}#2#UaGv^OH z-hk4Jy#n8bQ2Q^2F@!UO#0yr$E=5)RGE^p$i%_{5!v6Ep4shS%ITZ0aRhOwaFB}Qa ziw{4tfW<>VYk&h0tjadPJl=WOi=wH@pF){OHl@hI*4KhjR(H_Bsp zQgIX$Qcl1uiSd6DeE{+MQ&Q~lj>d<${7_itry`a6IQqW4@vG;yZ)|F?)MjvAC(;_h z9$OLOvNVa$tQD zWO>e!a6_6i&#hbMN#sQf@-LPmD@J3}2)1&76jyU%f=P@#cvHuVKJ%Qo*P0CS8hVi+vd~I^74Q8jgVw;{j zz)Q7=>8nE7?*=h|?;fgrw6Cket;7(Hv?vSm1?@lr=K|tw-eN8=3o(|jQaDp|PT&XM zAa;QFIaiQGAECB?0~*ISIae?nX{-mNdCiB`cMr+$shdp>XQ8P4_>^~b)|2WGLyW0e!lRD42OGZ zHcq#2FHdb6o~tCbhqiB48Y9{M1aj!#t@ZhlqsLR?<{70IwojWUp6>~s`H$1~hb5oG z@FdRPr<{`d`$P8sn85V12QX260nd@kXGu-~j#hDJ0QYd}{_6^w@jLSy_`^eW4*l={ z>Ys7FGq&K{x1T-F8Y2gpTij3Cz__Y)K)5GN#N2NK`s{J?G$kqe|7YJotodxjThJ$1a@LqL1Og)@u!1QLuW^3h4XH06dV|6Tr2BAgAcz>DaN>lC z5t4-5Lb>sqkgqsE&X5b7S@1$$mOmmReG$YxEfoS2I4I+R{$6`}kbZukzkfzfLH-Sw zkU+R6#lVR*|6{dAtSDFGIdb$oQ;{I?agOJ?FWElp$D_!%J>1}d;pE~RPCh*`uP^X= zW68fWnzMOB_WvYpej88g&ah&0s zs=kD`bzb;1Hy)Ska*%tqtxdirf4U~O2Ch%9uP)H*l%9z9-;X%X5XL#oK@#)+lrzKw zTg*ZdCDwwq04w5#$vq_efWQnV+7Tll=7@B4!7(MQ_D2d{j=y66 zqmmB8jPd_O`h`iv{Xfr~;a&C)j@4u!s=p4+-+yWTYyOUZ$=`P8M)#_kqIhUy&5;(e zg?s|!2)XnQQZ0!ErtK%5ego0wuOY&cI3e$Nf-=pmU2>b-JhQKrEA zB!!a)Ps9B`v<1yu7*8UG;IOBdX-#0$Z8ZQ$Uv`)|dVp%%09X05=Ik@PT z*vG^0Gku+18;kN{kQQ?iNr4|QreB2AOK&3a0`2|mYls&!16FS#%JNOl56zH7-~^L? zf)m!z2V~F(=(vAC?>7%QA&VrQ;T+BjJf7F948L&6eg2zdlqy4ZH2M$X)8p@oxZ5szyIAsTkrch zy4TfaL^*w~Hs)j2^T-|U@HSFx$t@^yf7*Zax!Hea|6+D%2qP$P!fJAgrMr`7#ETdq zpE=|xpAW$`ZV>YVk(?Jw+Q?ny`%sYOg`(^@M1?pZDK7YK9q{HC4}Cr6=Uk%)^*z^a z-k4rgQLYG%jKDEvC=QgV@iFuMS)A<=wSKX4YpmG6!<>I~wmZfe$-~cj(P;ATjwKhT znCTft-k!&a=bw;791nM4{733B>HPoCRMz?^Bb3K6mam&BtuT{&JhtXUz@ za&wQM?F-H>Vf!PQ%ZvH_-xA9&Y=3;hsk^a1}C6RhhGOFW5@BG;!p$$G!Y z`^V5fOd(JIJ9=;IVZGnK+JvgUPWyZDH#{t#@w+c>3~Q<~!ld#*BKraH?jMk2c!3lH zB(VOMBzgeE1&OmiUY!5DpGY4d`UT0X2d0XC0P%rJ(G&C{F2I-E0o+3<_K)RrR-iay z4KV@xhzYugI`XupL^&fqIza3Z#=wm)9x5E>!=h6IU)-6VTUvs!#CSxhQgNmx6CaY- z<899KPUB8#v3GMU`~2fs>lfVo!uFpacbDMg7jyi=_Q$0%_9s`5;OrT){}Hr%VgJJR zN3iBUo}As!a2{YG_xT;>%vnrFCECBZA)oz!4@9rNo1T2#F59WugfFbHrn8>AG zQxJ^wBrnPSv$MGax%Zl!1N@EG>Ot@!JvXl}$|%f-UrZDtG%D<>(_$%ib3Ioai77ec z=jTpL!QnAZ;^ue8rksj+0Q{|qLu&u1oZ{p9QsIsgCU{Qm!!d>oVGk6=my zu|7$(efou`m@B+YjPUl{Sa??FqNcag`a$@9{?&d*v3IelxKOW4^g?RDVI;V|k67}G zCtrAxIKbzK4SEgHXZ~dW3FH%#gmc6q1{lHx4dH{e-sBJS^ zayFT?{zyp-l6Zm}nma6S-jQ)x|7!a1_xwJCUw&cM)_-M(CeH+)s0c)+tFfK4{qxHc zF+HFBUEG-~_HB>luB={*(JThVeWXJN1O^j~BRp+P~=c{nY-aCbHL0 zUoerm!LzIvzDBI@KF*nhv{j(})}Va$|HAX+N<*DhaR&E}M4ske;tfc0oWtE?#Qj)3 zFWG*S1>*qL1f(3`2m6mFHXw;R$r1#oC^-X?nFk1LU>fnkI`4(ZrVq&RTf!Zr+)qj# zf+Fr>$j=IbG9?r$l?qk0O}KIU*FT5(_t?{e__OWJP3Cc#8rWtf<3NcLa~qVHSrUzB z7^hD&lB-`w&R*`{9HZkLKl}W{d5#a`Y>%*iF}ou${L)+x@x8-{?|p*y{#e2hJVvbF zZ;1DKnzQ{cvgZFvx(gO@4~0563$>k{82Ik@({KL~a?kG<_<)<&yH}NP-=Z?k3yIuG z7~`~-J4MJ3a^WT70AE7n*%{;sm?8B6L_I)=s0E5Tpx_P@wLr0BK;^=|;APeWz3$?J zb%6_r4I)QC)F&uXevbNlZ|Jo?+#wo(((-y-x$?!Yo`>Nc(esA1BZK(cIH9oIL%)_Mad(*AwLHev+~OPwih| z{GVj}|2SiQf$tacJh6Z?`Ezd?ys&~?VaAFAwD$KOy7~Pdhuwi}|G(tgef{ON>5X*- zdP5rblX6Zt*<&;JjK0R5LNizke3>`^_5&%R7cf*06t_|@fZXDe4-mUWI3FPP4W@CY zfYz6L1vn$1^W%I_*cO-)_o0&5Ze6-JqN9D0Z7M-q+ci06`vv~`mt5n2#Ls-we|?70 zlnoa}6fE<|>0h6TH!EWCVr~$o>%A~F^D-uIuE$L4C~mNy+ z-x%WgheY^*$C>vJV-6{N!859Jc$>Mya_;y%%-P`!6zAf|1{GEE#*VANPl&3^S zl06iDyU77IPr~~M4sp@{7d=4<10c?R(GMU_NX!XJy#Ub3t&%B;t&;k zMVJc|gnWpcxKAW3KrVMAC&c+6BP$C{ja~9g@ZWje{txH-&}G7zcyD3< zyJ?@Vl|L0@Qk4z?S7wvs?;_)9vY)A79&v&9 z9oi%E*X&>TfT4b%s0AoEACT(wCi8%|q&z_Q05K3*^pmao&sipXdQf_+Vl8L-GS2AcY*l zsiF?xz};iS1*u)%=1vjj0G`AJvlf^cx)vtRY!>O=$qnugr79k^m36pv{cE%Tn@^IT zf3Ri+JDQr!6FKK?tBsfJ|GnZkJY5usiNxlQ6*WKF|8UyxFz)Jkf_eXAjOBl$w!m+R zD>Rp{sksL$^lphP)PGZclQg57(nC! z!v2M1gnWRk*!{@moK|L9Ad*v}QC3ur8v|d;IG}rU{r;K$@-}z-)N+@Uo`-ZW;_XK(T1jc7b1m5q7 zxT6yH$HX*Syh={M4|BudRh*8B&ITFle-Cx1um8&Ia(=IwDXvHjIz+zE52U>SiJbk3 z5%qtHhyf%l5N{Ld7X(HynKJ`P*5nW1{E*lwCUy&|SO-wk2V@c#BsfI%p{tjZU&H`H5(FMl@&UvF&<6zkYvd;?j*E1(VE#yw@{BwjGBA_9fn1=W9bP_EtH!)y4@vj2fwH>c+n z7r|eb3aerjmJ##wCTnz08OYyHjE@;-dc~Z+8DsdnTwbRoj&8B5L&%uqV|OKq*nY~m z#JeJV!0^Nqob$89E8PDvKRXb+@)D6=hLJ!6^=J+^q#V&O!$30BWzr zPz8O6Jnmx3)m}zcY6uF9I&^jRZv1EIto;ApyZVBd`OPgVlO4}scarOC zA+bQ@0~7H-?foz9Ki-PFM1>FdZ|q;oI)KV^5tM;zk;58q4)?8RDuR)x&qPOiw~PU} zhd%sX>cijiSyu)JX6F=|ezgA;ocEbYevgR;CyXOMhuF_ON@I2souLRgMBe{p>;t?l@c{~MfXH*NN)p4E|E|3Hryb+;6M{2PaD@r3K*1HJq-dNM zayR)BsQuOI{>%fkXm9S2Ioj^=HT+xb_FlcWr~m3AL!J?SSqg~r|2^{W z&LHQ!u>bMI`i&90dW8?*oZkq}`H8)K((XRSgVOFk_V&fz-k*xF{jsbOOyPT9ARp*r z;t1B~M&n|h26c_~GLO`K(3_sVj*X?nZfFwRkQREBIKCB7IL_nFaB=}qgbxrB#~H!9 z!hQhlK7sXr!4LFPp1@Rc1gXduD(qk7u~f2u1F_tBN@wU&16T)4M{|9Pd>!w@b-Y)0 z{c}I3r>}33!K6oEW)f^lR9IaWi#Isy_bl!IDW3mRi1!gPnf2d^?E8y3e`(gA_APik zgp6Y!P|W`RRD|t6&%Xbg+-bYs5Q1Yy?(Q{Zp{21|#*W&N}*me%$FV3a?&m&Lh0?rUE;%qRv zMID#W2P}o^@(QT@)L|%wH(LYhimzBZTDWhZ^-_GIQL^) zs>b`=>oYq)fL#18ct-Ci`2fMyGmU$^rmzP%nQ?%SN!%ABWFq^4k}x)y_!F6yW{H=G z8+(Vj|7Py-JCTzJzg!(!n_A?!@jmFx)q(yQWd#|~B>TXSXbofVM#**)9Ty|sexbB8 zB%ZjSWbO_S_@G3)`I5aSJ1k_pzeo}V?Ox%sl)M1%vi`qZl63Di&;)Lh>|ezH22~KU zIUibI-GE!Szmo0$K3vOtVb?$RGy3}aX6Kj;2-l^+sXQI)IQO%hoG)vL{aK-N!}3fQ zEaP6DWyArzOB}#b#`8j!kmq}`=HidCkX$j6Xf9zPWmeWjyk)S*D((W_Pt2bq_Xs3% z#z4*+?_KTor`NcDU|>dhp$-|T{wScG=8(T5({B@f0C|F4S91r*N+@}&c3aKf0Pj;& zuB)WS;<+^UHFxFm`_OoNz**pR#Q&@%Kj0>0hV4L3;wj>Q-B|++r~Ru?Tiqzn0YBij zFZ4&)zbV&<2)zO>l^NJrpNf^G5m=Y+hYyJVU2XKZE9CoJNzT6&<#vfc89GqTJq(_!2 z5M>#jC{8*EQ`mN7`EQ2SX9KxD*Kr>(XZn5CLrc5P@cIy$v~ltH&%}2FXa6=LH)soT z0ye`G{t2>U_9Bmb&ZYd^yo@NAGSX!G{}tH2u)U71Ya0#OMTpa7AgNr3psIK{@%%qW zzAp=-H_l}H;B1bsbpJGGcZHbiJ#mVB076b?UH+?L#n&w=@Y{)G-y z)!99;(U?<=1Va{5Dh&v&O@dolB&@mDQ}_TO=eg(0lH7koBK(2y3;&&5^&>Z zO%fezKtuQc@>aYyBq`(!Q8NBFaF#HqSP6Yz8g+-W<4q0n`F|hmU&Q|=y#YzdixMRd9D& z0au6R|A)9Z-hJJf?{RTn4L_H)@OAzGo*wJqO@8s9=tGD}xr8Kg0O<5;G|PS5M}Dr2 z{VTuk_TUY(rovLxWvbC@N=9$C7kZTrl8yI9orRFTXiIcQSV+g~R2c+Atxo74pzkT?fc+C4lBAy=-@!lc%L*VE5gZ~NqG0`4hrUjrw9R?$Jik24V zU~q8I>>vGi{@VY))|R$IDzy@R;dTg)wSA*iu>aPKMBLJb;13EL{4VUMWYf33cK!eCy?1n7)wMo)?s$LP-(!sX?(Zga zuw@Jw(-RU12@sRyLPByW38C1y_bOYKWlJt9maS^b>b>{gd+)tVwtDZq_wv1OZAtEw z1TGNR9%FhRowfHl`>Z{`x#pS`gO+tZ0(8nvNLtS9e?oU9~tc6Q3S|AZ6l z1A5@t^Vp~SEDj!i<~r>^xE=>?qWuS+g7n^@r?H>s9XRqVjvjdqM-Dv$Wz`p;a^_{6 zGvwZZeMUS2X%pi5-`LzD=l_0#&+xlipG4oU%F0SqCkJDIHRM#Bqv3nv8`p}!1SbC z|LYs#pMky+rJ}-O_y>B!!dVkn91cNC|4pcbm^>#+aCb2z2<4ldapVShMtWaOrxlQpBiE1s3V_nY%O z_Kl4yUAuM-wW*;J{ucw!FX7*83k0SYE^m}bV1Ci~Q^4pGKpdAzyeCMUC(d7y1B+L- z02X@yM>UN4=%a&isj1%dDI|uYrm-F}{=d0r*g+podRhiNyj);nr$JqP2Tq@V4M$X- zkubkw&uVPny$U<`Dq+XY)e>#ru?jcSwq1`(=ZNzJ?bx*nyY?ty*P$n{PxV;||CjBR z;Ys~JyC5Cia{aGw+=laA$hE%7ai#KxI#kg=*A?r8i2!}T=?K?yvDSZrMWc_1^^b{r zk|AH;jl`HZhv(xmWdmvc3a_=IE?|EI^Pbum4z)oE>&GX@2B73x1vGShI*vKB&oF!W6HFR@DA5dMfS_5*gB6-vd5=26%%zW|^Jh&y zg`f%MkWJWpj0O9>fEzWbt5XT-73V0Bj;x+f>(8nRz={U7WbP%0@bw)gXP zgM+gH%&5C*T>2x_PrrZzhm^2q@1xkci);PPhb1{6t?^v{SLA@ey+q4+7uXlHojSk{ zaSd$&`&3@QY5n(M<*JTQ#{XAVSI|iG_2riQ8Q+gN6urNjS(me&&vr2265y(a1&b}1 zBaXie|FgvY9M^s!E5!LL_@88s*`)0!OZay=3pS@i7h_Xny=)N@8iXSf?4%7)4Qx)0(MV4uI$OZk$AdWr>8NaN<`;dx=Hg%Je!6=)UdzvA?zWE)>}^e9 zX{!ZY^G|V{wtp41rzQOF-cS91FZKA{)aRG+zjfy$cgTSiymf8?k|Y8{{>U}4yZpYSni??ka~oND%bzB7!ET=TY@tp zy-k^?=EqpzQq!USiF^N*F!z1B*SdxV<7_+I%{=XVpKZEM=wNO4n z{O)}ad-gm)U*1F5xtn@D^#DQF@xHv@7kC%6gSvpAE!_Wqwwt=cf#-2d{T&!xIf}qA zN0eR7L;paJjQ<}H{sm7D_B6lWUBtK!+UJM;OfciLpE%xvd9k(|kiH22!vA+W{uw{C zXtkR%fO-ULNx=6sM#3+lBgqv}K4!3YFhf>)CfZwiWnJ>0;H93vzV(I0#rDBrA+WQt zrtM!3b}q-DW4r;U&b=Vv|H$D-v2Wi)k_^~Q9bg6jTX!zm_HUK}EBN2Obx8(%CiVk+ z9%cN`&!KkiJ(yXaL{z*N_x@#uBcp?|-}aw)X5Xi^lORKMDV0zyBc?4gZE&!@pvBnIxa!P556l*@$_r|FhU0N3CS`v52l=U~y=`M`QX=zwtl{8l3@1+wSUK&0 zw$ZO};`DkPQ+W)Bi2wcjAC_dm3jPK5Z^r+1U;py@zaj@VZs&Oi*5KgrS8(S1C$M$Z zK^kL3+BzHLxc`6fS>2Ac@8jRIQ$zPmG*#$!W<+5)#2$-Y$7uuDgM~}f=~3}N&f+!5;r(PL|_<6p>tJ+uRg z^{z1%KW*om*|Mma6M8y2O;Rl!_{)HZ}pij9Vt^dLXz?h#a z8>QHRd8@5hu-=Aw8?OJXC%5Q+6w|(1Sd6j4M2a`cLfv3*bOFAMTdAmR(CrwMeSu%) z846vgp}AvM5^McAxI5sI%{h3o-nWhWZWx&U0UGC@f~xu|992=mp+l>1;J_o24)8_z z7qVc*{=ae*dw&u4cl^xnp=YuO{$bX*R#jv_IyNNhYX5+&{wmM(`*O}yU&m{0rMc*i z@xfvcP4kTRp1VT)iuL~{{LdM@e=Gh)44{bny9xgi_Js^!{TZ;foWTFM=Naw+OfjAA zhr0Lx*cvcb&EAearEFBUv}+7a&&j;?CA=kcrIPY0xO#dq$JYdA%=dHi(}uazHs~7v z7HXQ0L*?Wn68;Y#UVR<^LjT{fi|hM}e{XsHU(o|ra4-1)g)YFjzr8BYLS5%iFta;` z*c4B+w#l{IzAsq)-?)!)%J$~ktJ-zxv6y79r$v`jm@waq1tad|83O==54hITTxaku z-=iKNVuQu{zl{HR;(x}JaX{wG1ro9U*32Pt*ntJsn1ma13O!B&0gNwBaKT85AClcH zSYO5vo_;>aDKAB9-=N$V_)GAAwYph5IxY<+mKL~VsSB3?9XR$ z)l&~%$N#>Ak6`bChndfJ1OGSg|5xJwHqq`U#Q`ubK==o@9c13miC3V-xDQvK3&<&n zMc+V|yifa*=kvdT%Wv~DJDcm2Tk}#d%UXVLKaDBN{mk#<9^fCu{{m~vfwkn|NE;&S z%}V(9Ql}q47Yh+K7)uL44Q-8Prh2$yX^t572C1lTlH*FhfWNx>`(Db-Dam(s^TQ=` z#`@dp!zaR!ak)Ej!Te2V>OYTDXCB1~^@pzGfB&IJCHxEj-}aqLu|2or|FbO*UN_-%jZ(CF|z&Gl89gQ>ol z3u5gqr&F}$6T2d|cLo2q=>N<37rBC8hJR=J6ulWb9%O)7)&-o*Wj)Z$5I9;Jv&P&7 z1O)}*YH@|>o6+{iI)ALKX6JrDvis>6y@<0q z>m=-}oqQN8_!s*B?tKqR_}{TpCQk| zIEsVp;9z_PPG+LNbOf4f>*U&!H}P6?bMyQ8B}K4vvxd6XF*te{AUMj1wSJGn((XOz zU3wYXdQah;<{F$*r~a?{2#(PIcTmLt9e9ZN=i0yLe&+Msihq#7Z{f}z=nRfr9@C&y=RYShl4~4ho-|^+~_WdKn_YPNP)DI$s?~Z@tzhB4yqWR`q@z4Ge68^o8NwL7Q!51)_Xp6BdZ**mcBg%{Gnht$g zb`I<}k|gJtEaShWx&8g}@^alk#`$UJoMcT;*5C{>M@Y0W99(w8(Buy|Z}4-R(|rPG z&aQ^K#;PyHzo`8o^#2w7eivSY?_Y}}YA-_l{D-h`IE#c#KXmlT`F=k(xzX8B zsokE~__}sKq|U0&tCIE&0VulmWM||H1}9{NoD8%@`-h8i0$I+W_JJbKHYD z^6ISnUd9!$9tiiW2xCrgoH3AO0~Cj$Bf%FwMmn%CxBz!&C*6IGy%uNAtis9DkKp);hjCct z(WUkO;Dguo|J(QfLI#NSU*Lb^_Ei$?IQSHfY0&O}aWC9_EKzhdGk;-Sex<&zV$&aUt?+A=6}rhu zDeT8_h57s{aPlxkOqvY>BTmD?>r?swX`eTK7WxM3p{4cME%-ll>=7Jb{O_Iv%>5Cz zznk=bvHpv-e+B=W#oE7PHDmr*FNC#%)pdSL|AH!_D3cpI*x9P_~E==-0gEnuEL!FjR&7x{sr4uHTvd&j`^Z&)y){%=PAALIW;4S*S^ zotX64i*bMU5MXa=gt0e35PbpBtTz(x02D-Fs+9d}eVy5d;v9^OjNrq(*22O<`_|Uh zA5~m{i0`eZ{uLP&2y=6DXlQ7_kUo-_B<4=0>cP?XW0*%IdeCpyt@Pko&|q!-u+VxGGHKZ1Wz17O;5 z$1V6@2sr_SYq2gELOD+!A!@L6eGf*qFVGM0Gz^W_vbWE}-20QL|Ep;{ zjAO?i!~tdI{n7TfYu|m?A+W#eUe@#xdHwfc3;q2!EPh zYV<>Mr)-D)v0}d;P-+~R+&EBEiQ&u$j0c-xmbE1(%(r8XwZ28oFR2#j^_oC`lX8J( zsUJ+UZ`|VL&u9x^+@Se3#sM$}i2dg0U6@nsbpZ2zj0FriPW}HB<|8$+7;DIQ0Bazd z{fCN!fzkx{*_%QCv^p&GFCyGO0Hyf_UM&r+Kj_@E-p>A)DoW~1QxbAuWoe5Or<9?k zdk{g9`bf#qLvY+K#`^u9`~Fv9V*V@)Ez?>cT|4w_E2fQD1v;ocs9>r|X3Cx6@!CaIcX5-C(RQdyReYqEi zMp=3sylkzZapVvTbW zOAQAzgIL4c3`5T9m^R%*9{^+fY4elve;NNT>Hvve0iyo*yf}*fAnXq$`~boZDDr@p zbbx(W@H~V?|6|<$t70*f_>Vl#cpxLpCRQW*R!1!R@V+U~gyrz^d zoRE@=s=9j3zTpw29|G?U4UFB}(%kxfenC!>e~>5iO>~%Zw-=h{_fY58;ks{**z~jT z2-*Z&&o^Q2_;VPWt)o5Qaa_<}15NEUID6)CsHv}oiYjydk22@)2y=Z8vW~}r`>~C6 zzqW5>Kab5zw0#R}0dC>@JAaBT+!K7dllz0i>!7UhJNo=SVU5oV-1nz&YJd2AE&s-^ z_>B9SHZD{oa*cMtgr_DJtPax7Prd#w_+JpUfJI)A6c5B&0PG1i&srd}!gfdu!AKoXsF?o>Ty`v*&{eS z0(r$Hq4h1TyLtu({$*=Xb++}tb+w{KH!U?CZnSxb^ssum1IEd z?}M#7`|mmZ8pc-7z`*2j+WjAcuAUOkX({2%>9yP|JjQ+h8VURR4zboBvA=!)eb~xc zUn}_E{@J70wwboS&G$+8-%7i{*8TJooM3(Li|^u+^&y0_K0{m2HBA{CUlAKW5;@t^ zQLEgQ7maTA@fr6xk7@J$v;ne50I@Fm0IVD(KY*weCSm~?4=DNwEwUcSiVgq=>JH9~ zC-$Tbz>l#&!Hg{mIgRP?bC`*~h`HoTSWLIYT!t%95&>LG#&Ag@f*J3wt9}gXCyqkj z$N-)JzDUc?K~;T&W?M(+TZ6+R{}Q>wBa;L7cK3I^R9D}uTvA$T8XuPgTN?+)?H=LU zy_vCmY6y(HgsdVxq~xj~D0VYk{NAG8|4ZiKJ_TcQB^a8ng8n5X?)}$DF@L8u9*645 z$Dn*viS;@e@5`E>qUNvY`yuN1Z=;=m8~6QNHw&Bpa(xd`?}s_QI}bmN!>2ajg2`^U z2biItJS$(uzKs3bd0=w1>!pdR%>4004=e;SZpWV3HD!%YuJ=OU7cxN53jP;`FHqoL z)B|EYuoWF(fjWSc59o1-xj@SNELF@0Ye@K?j=F%E#HD?}Oqw(1@`5nM9HH@wRJ619 zOr)P1G*7G1ueG0XuxD92zyaYgvB=FYz_scc&7RKg?`?kS}HM7j>1HAN4sFy`#k1s4@hu9DNpxi8@$J7P$deXbYgtvB(os z`LqePJMQwO_xFbFx4(x$z-`Lc)v9Etv>HGA|21f_( zZRu)#zq0l!QZus<5E2S28(W-FKaG96x8bz-895e+h*v>a!e8MN^d{^2 zy$mboXNdi^%=3E;CKhYB?|%sA^;U81U(L86=KCLi42O@cmU8`e9U!i`_DivT%YFWY zzkj8!hp6i%XbXFS?mqGYj-B}f49pHNp4b7`>dOp=M&(+J-zVPrzTL}oQ;F#$ZGltF z4TO^l>wGiDN9q;IwO*0|^b1f1EU-ox7$+e10irkXJpF-l%q3rBJ@5q^y7A}>kaB{& z4okMc`H+)Xh&+wiSndPj&SO4_dO(sH7IK`iP#TP({7_8Q=VGun4<+nB??d1C*^?^R zw`(hoA3F*?`UzcK+>n@*2BBBCwRXPM`yKbF9ULO==|ACmubYyU%C$WZS1c^)qd(1h z9Q%mO{yX+@-_JUKQmzm2ztZDR;9u1G6xiRq>tW{jJcT1C ze+5m}aC7n1Mt)f|`l;i6-}p_wpYPXRWO%6Kwc*-a^rg6Ci1j=t*xy&w2?sGQNWwa6 zfr}b}QjWl7`UXV20PBbf86cg@m;i7eAjtrC+5&uN3!py`p{mpY7!w$;N&IVLAx#ep z8CS5FWs7<0#3%^?s*`|*e9Tm5AuGTEP6q6Eed-WS9NLMKs>--*W(+q^>I2+Y=jIe7 zRaMt(wzhS>)!8%fQvcA{z262ub#?Z>RM}8!SX`CwC2;RaJ>SyGg>lV>*t_d6l=mNj z`pLtvv(-dOiW!Pa%#fY0j=1E{5EAnig2LW}tJh1c?fV?_`_?h%_i@UNwJWd%pV}X!U-jE~-an@An4TKA7kxDv^Q94(jkCa`wRSwI>9sa56&1-CftmF8|DQ$?4}MtJ;00g0RzZ>j0swd(qKFga|4of zuH%0`!x~eWj+n~!#!O)trf3hGtIfe^buM};DF{o0R0UHxIGG0*4dG3NRc|H9`d>U@cq zo-I3Q*I(Y}3;b{1ejnF;uK#=3`)lti>^b^8RL=euhL$@K6m5dqrp)|_xn89o6CcaJ z_3P;sL!ITi{jA^78DNZ2M|I3t9m4dLt(X#f0gG*v2dw=g^$-0B6Z)TE%8)w1-@~5G8=-OR2uuz2;AU?Fe{U~DMn$mRcsgZ4 z0g5ZiQC@XTx2CZ{qp_`ZV;ki{XK&w2y@Nwaq95Y$P@j^Z{((NFzW!b%5tBbOG;~i- zU;ldHZ)k39d%w26LHQc{@0FI88s-<|At{-DiXcDkLmXgiYz9?T4N1@cF+x!{C(8>575^qdVGi)A0o$Rn~3$L&tK}_M}1$s{&XAt{5zQE zw|5P;?SC5kj=h1?7dFDxn>Gj5^&636{1vi1_y^^|H0#0CXXekapU0x_1+L|+5n{fB zwZgdei{7DPeWz_;j{3kf>&OV2y~LcLD~tyc`vp-SP{;s#$_i)32fMRAfG_s}L8`0` za+2{tv;_zmkj&3cqrV}|jB>zcIX;MS!dJtvSQd)uvM5Z~q++I}2!oA9$Vm%^vlVmW z&987zupgg%@GiD}`VmghMxb+En|Lyl_6Obp{#=36_?O&nRyw}3CC#X+r~N`{{!F9*Wls*b9neX!+To? zXSc`M%kL?eSw005vnODD=?PrWTMJG4`cG@DroC_}*6*nDD)#t&P{O~+@e?sUTN%@{ zb1(M+)c<$V|0iCHwO`cp7BxK&9RCGl{NINKd%P!Qc%r4Nl!lyt@Y&1f%kqLeFfrWq zQs1?lq>cnnbaE{bb%GWwcC-FJ*K}Lf{I%K38UTARYf8DmwSS870yDG?2wF6wy}+Eh zf(>JYB^f{+K-38KCjR}@0Oq&~e*ofjSR;TkAccBBx)JeDT_D#I^Vw!tETBG6=7#xF zAIwyQ0`;jtXED%SfsTR%L^_(n{IoKR8B?Qw>L672ZNb6qA47TXHrfpL;xz4tdb*l0 zH@OUZ*41*hw}Y#r1KgY(C7*(imnXcuJsF$h3TGE5I5^tF!p59_I%DYS>);IQaj4Sv za7bl4_uU^;_x_M}wNL1a-;UF#_v5l5eRv^P*fY-!)r~gDuTV!?!44$ny^qNB-y$sW zRRl!*jP?7UhKtvF>ikc^*5N7G*sW)7FMWMio`iw%6U^@uG5%|r2dE^)_zHXfA?o`F z53;`(_x$_z)0QuMeqz0+&2Ja`eC|8&xYX}+`)>C9rLBJ(bNhB5c^PVF8AoJ$3bE|t z-Pk0@`N*>GYs&uKI_25YI7~7(ZQA1u@yxg&;vLrX@e%*xZ&|VRBv_$M$Z<-mRbD0A;}; z+6#^x+=HWs4npO~VW_DbgNE7(oK-)C6USAc#&|>36US*|Q(-@?qf5m2BNeq{w72cW zomxqN2Sr5*s3=LIzVF32zDr0vsQ}nJ+J$i6z8L^zFM;PsT^Z( z58_|+_7Lm7u=R`D{$kDF&-y)w4n2v3jOi8qes>W2JJ`cptlFa{KlmBI1NHNe=t2|m9jqwOe?&oO_v}C_83+DP*5&O2>*V}Pjw_^_=kq5|F zV1a#+M=TL*1+k&NoXpFM|Dm-^5Oy! zAI$ZgeMn8tpM|>eJ{;P%1$#Dqj9s66g3X(#zkm8EKH=+4eEr$xPnk2fk$B&VeMfdm zb-2vz_23_2$C?}-h|O?EOr|s9b6t>8syTrhP3lvU|zrWDrlTtLtp=D=J(Ow ze^g1T?Xj0}eo{?u#`Fp7D<63psw(XLOWJ$j3FiGW-*4ZO)Ja~1ipCo_Z}b^^Ef}Jx zJPG}Sa$dhI`@R|3w>aIWL<5``(mhxwK$H9V6Sv~uk#;>%``d|m1FRtcOWFWL9Rbz~ zpXYJbVl!r~w`10BA7&kuG3`tl;HivRU)mIc85a=2zCrW}&QJzSr0ZfbgS7+mjjrQ= zA>Wp|fGZYry%;axi^alFOq9f6tTYi5%v%^^{NO}wHfEYjG1XR%;ihu*))t}VD&qzV z(@~HThqRbbB!mPZJ|uw05JZH9At)d~szc=O@6Y%gFYX20nUCjzz;GXg#`q#C*&oR{ zLDcCQ zpIG;I?_s~MW9yj*@Cr0ey@F%P&olOy>ptPIKY%elqr~(S@xS22b=-k5ehxf3a&K>=Onu-0 z#+Vy8M15dP=m9pg2ik61BK8b}2YrH+0iYg$P+m(p0LBL)L5sP<7wHpVoM0|(gZXBd zFS5d1DfbwTWAOP}g9CtJMamxOx_4l`1GL-;dnF z<b+0d0G4Q~%$9grwggI`(yhhQETq;FqNJ-pB6+#sWMCchBeH?)@U|d|dM#o{{YQ z=9aYen>|BY-;>bie*c{IGdOkn3HI`0JrA{qaQMW1%Icu$=l=-a-NT+9$EgFHd=;nkSjXE_kFkfb=;-czP3Bn{`~NFE zFfnw`RKqpRzN9GD_90fi+4GM&y%ZxP@bAp`oR-O%n0BU)&prQ??RHF9(nmm&`~sqP zh{Ha~9stS_@i_?KzCy@=7}^8{{*!en11?}LQy+8Lmob%Vipc_VOcq;Wy3`u8#der0 za>7ih3+Bo^F;ni1x$*$aRs>Q$gkqs85_8otn5#*^Y;6Xn8}cwsTft;g6~>$EFx1?D z{)SrgHPMzx|6&8rzgFpq;!-Q-8(cst*Z1VqeMm~(g2d#%BPQX`h>ia}qT^me7}xq0 z-23{!056|s;pzRXwC1~dtcQ!+Gq7`7M_b?e>vsN2CQs0Ru!jEqN1?5?O2Yn0=JlLl zJzpUM4j%t0_4xm!4eux1e^3ulS;u_epVKGsJJtaDAKD1trfu+L_WF2S!vANglnuHs zufqh^FkQ4_FfG=JXq|zQBa_ZcK~)0OiA?`w_+nGT%tZ0M@?} zwF4zNz&!zy7$ZQt05WMC%%yFx(3CZVgiV09h$36s1n4s?aiCqmk>rYnau@0b9+)RB zl>1=eY5?Z1Q5MvQn4)Yf)E80?lwz=@0)0I8v}B^ADI7I5cBs615&5Mm$Sl~5_>A|M zqw_n2C%lgE*q2$O=LLy^BVK@i$TRQ_e3tq9Ps7vqDf;!+v7X*j^y{yOquV;zJ3qy^ zAlmtC)-j%c9c%bLDb@1O)nh#mUFP(&mZ!=o*78}-=NCPEgpL0o{dX#&C+EqR=@0lV zbT9m#Ho`wM7x*38^?!|>2iDQg_ZW5_T94f)U&9%LO|Wo1%^KeR=%Afe#=gAne=9t| z{8rSZ_Jg+XNk8`bu{%Y5pL#rf&I14N)xbo+8P)2H$4pf`V~R5|UspiiLCG!nZ*L64wQJVQE6_$} z{vq}&{yX>n@31D%>j;c`1wr)T`Gr2ixcz4tQ}7)8f}e&@z%#et-&OMKuY;ZQ_c3~AD~Z>Yd&N8MO^Q}Bdeiud_B&deFYaUZh(=& zA92O>J?nV)U1~}!VOboM*VDMSuUmN3$9h&f^P2ZpU z_yA(pmofjITA1@Vfob{yMjQ__uHXQMxE~mEp-q8%0df62Ylkh;N4OLxuxt~ceNgNP zgg&sCL;GMpYY7xl7L=NE&9_*hN*)DWwPp+v?G)EsxEJuDuON(Te**0RX|xLzVzj9m zgLReYXMAB-T>z>pFQd5VIP>#9MndWagvbA!dHT;1^XuUovQEOf(Di-&e?fi!XYk;< z@6L7K#pg*lx-*{7^$A$oGmf9J{Fg1BkZO2bG+fI(9796*{XDqVYyKXF=RbfMW2h~ePc6vCdJ{}c-eb?N*O|ln9P4tovU1jf0apY%L}$q?rGg|Oa7h%sgY z>B|%N_rJt?Af!;f4kea@sNYjZnDag<#RN>cD@)gpQWqF!-rywb2TTVXr;mWO$s>hr zfcis%7WV`?m`=NZ=`4NBj+(vYHug>b1X@}fSm*m0>^kz46w`l5 z{Ws8M?=@%YS6R%H=;?2e_xrLQ@}27;y{%WZ+t@?0HP#B9zFHV}cr^F$(R@FK#}$e;!gPo(#(Ym>&_fL)UMDc>t;WA8Qy4Ee9mqbV zA>2nqP-aAFGdGDj#0eLeLqOX=nm%&~=_|;gji7*WgoQj7Q^piAmQdIVgngileuBzS z`Uql3=@_gjL*KPx^w2KQQRR*5QX`ZW979g_r$|ov1HvO;qFw(v_%L?Q(}(eUZj9xj zFVDf@McCLp3k&bUfs{JcGrvS z+r$37+|wIzKW}0AF)SUoz?MBzM2{~=)=qY?W9?toPO`V%%6Q^Wpri8^ZGgXI%;B@t z`G3Vc{&#SmHGDkT7cjRn8a=(8FUjlPceL)wGUryAGu5j!*ID2-T^xY1s7uuC8P{*W zECa-vA7Meio;Bv;tT7Q|hLH&F1LFDK3MJ5%mrR@rN+f3EsV{_SV?I<1Gr^}Z8E^^{ zekU;Lza$4{Sx;y(N)uDjT9}Gu+(10*3no)0r0LREpo@iE%7lE%gM4Ew=9^%lz=m>w zzCw{xR24-^m9XDco{x^Jv1lrDMOmIMax?Z5`|l$<>NN@b-oCW!F@Dd*{RucYJqZVU z+V$<8hm{rMc&ygqGX42Rrb^H=UJV`A>(D&E3a2$0!+(~&y0o8#rq0iC(eMq%`Tq}` z+&0liK>U*2J@(L7zmGA2`z8Df+`GFTWDdb0*jRmri@NXNF#B`wKk_r~7hb_htv^Da zb=kexQ?;-m90N15txuMH-#^(m)Yts}NNE~|Vm#Rk2475bVGe-vGSQx&YEPNK-XW9|GcnW~!VNJJ%6`Q`I+zVy zqNxyVOmios%qxTiOw4^RK{8seR~I_IBeJ?^;)xWJFt2Sk;%MQl-4lN&lT)3aXY{qG;| z%g+RgMb^NB@I%$+Bq4$NcAyDnU1_`Xq7N^EdI0e+YJVcj0rM%e%cas^M-s2+Q=Eu< zSB|++kM|&nBPn;8@;Js?LCB#lFY1ORaPJUDU4gwt7T8}v;D0)Ta)3I4pt)Eb%yW-0 z8_SsDxXYMhAHu~{>Hz6Mj3W#~Q%N}L3qw#<$hDvG1!)P#5fi?JF@b+%zTYdbW2~;g zzB$)Vm~)7Y z)<$H6Ho`)**yrdR0t3&&Kj!XKJ`T=HW3q-=wTD-uX zprQ`=bh;-dQrs|^#`vA=0P5~R(s4dNn0k8%_wb~WFs|9*OH?Lab05##V3dlnAm#}8 zW4gc_6S*Fkpo|#LcESkbbcfUJFhCumH`NK(ezEm#%QrF zp)U6fMo3S)f(-WYNKLU|?GH=tA+8`i%ow2|h6oEaLP)3~<%1pq0xuvan7w|&j1U-N z0Iz`4FtXSLm2r+B6 z^G_>cnBQl|SfGm-^rSy9i2HKZ6v!~L~W3&XvX@+0(EiB4W?bMmb!fn9b~l z5%d4dP*`BWG1m6VGDk+bDN>V6kq~c!xR@)fPiBD_+Bt!a9HO7yr6tc zw3lF_n=!zhHJI&e!DMR#Mmwr7*jkF#hD(+>+eKRS|mi-Lu zkeq3S*yJmW8&Jo&iyzas{|DCW_#=*={VQ|~_Q1+jn{kIeXzZ%c9hs8#eR)0o0q8xG z%th^};NlsF@kAH&g_zS%XoUV?Q;f%R-=9OfTshb7tBIrpzUDg3a|O+o#4i!CKgah4 z_UEsrV6rAzqVa0tois)nFi|Js{E7F*ObPoljeOmbg}D~S{1$O%gRF;4;YM6?k@zD6b7Lnm+~OnOQM`?SJoeO zV_jc2uKymC1#ZY-p0J=y?rBn!U67XIO8MZ1ES{57;EtqpD|iQuH<@7n!LJ1sLG^%pNYk5JYLP^F`s`I zQ2#IFKA?!i*L8ed&-WYnx~YUzj>VR8%(q^{OlK2DT52)ab`?EsrD$Rt;5F_AO6#If zR2`1oiXdba2O*QV&!e3{P+?vq3bP|5%4eRfpgiW?bF&ULj zaVV>gmgGPoZ4CM45hyN;K}mTm%4mx#FXVVZEbIJ*&>ldYfVBaNi$hV)I)ztRmqbt{ zZ60N15y;4NM=1RscC4AAqyI5f&%6zFop)&4`-HvzcEFc0jrkS6Xzj`k9h;Qnd1Tr5 zDc_O|}$ptcbb&#h7KC(CMZE>inf#=gUat%=@pvVmq@lNvD6-H4&i zdglFCp^I{%wTU`FeGzJyBUr^ep^9rsDCd5ptTG;@6>+TR8+DyZuSTP$JPp@Ml2Ob4 zg>}`m8`Q-x7C!-1&B3f0Vuiri3$SK9^EsoBp{Da!9M}2~Cv`T$$o43FBCepYu+YA? zwpO{nU*6-(xc~9s{+4IGNZy}jF5w{cfo9ehDvY8&;DPZldyG&P%tSM{KRu8=0cii@ zUSN_s{CHOtru*uE-byU?RA7Pjytz(dy`w~;#f~!KTh+Wk*9}K5A(XtghppfVMt8TYi4uyv&+o@1Y^~AL}hoMMFv;S`xj{66=P}C>IPco@gG`*UnhnKx+R+I?S-JRo*oQ$cA~ef z8J$h_XlbbCM^~Yx`5J8km1u6RL>ub{b$8dIx3`{g_qFJx9BF7vMR|1;Yy1ZwCfN>l z?x&$|x*u9bJE3u5Gwbhfhq1*8*n1eVZh#ANOH%t=yBl^5Psr=NjGb>DJ3p8?ohLpf z+TMcbQBR#jbe68v3apbamCC zqoW2btQS&STS%WkDsBF;2#vOYv!@R8eOY7t@_wAXxCQ6*wz9V85m>uvAUM_%d1c{f z=%nv+xK4R&womB?^NeKOMb=%uB_5a`>v(Oh>zd|NT|TDSBYYq|jIzKBLvijH=3Zee z$(uGq#tr6CHz=b`u{w?RzHG{X3Sfx){qb60lDfhq_Y6}!&NcxHtyq|Az~X!(=1H@& z^_ZBh#^?ldg2zhHH(G+m?o3oR#v{8dfVF>}5X6`rH^%N+x}0MD&ZD?!c7(k(4?ySA zAzZOL1AA`+_|blu%-G_R>dgF>?i!8ZNg4C9%#+vpugwFC^o7qbhG3X=ZQB_~P{;fN z#s(mtaYAYS7)_)+V2_evailyLNbx~Wk{5dEOBl!vMjv|%_ZBg4t~3h6j2Rkd>_BIG zHriS;xTYtfygU-wd48;?;fgTkFb77MGRL0#c(-%7Y5t-tQTwZ^*C8NH%ugq{@s`>pH4Bq>-fAhB_U*-Fh@%gXF1LHI8uZ@g0ZtU;l z-n_MhF1=i|l%=9EKMu_qk!VN^M0-LYy5a-TM;b~9#zb-$bA-b&&+*E#M8@>QFs3gU zN%3B+=jjMak|07GreSH;6||CtWyr8o@in3Xw^S?-6kTj8UqnE%0h=uhHlp zYJF>Pj6O{nud+@o^T4;v1H<>w_;)WBsb|kpKbT<8!lA~EQVwFrRc$8u7^0efh05$C zRFEpEH(V9Rto+8h?4;VN^!jTR$+*gX8D)$sE@9k3UQr}_1%@KGnEpQc|E@M>=8HXi zTYtH3&uF#A@J!RjiA8>o{H(v_XDzSOGSA68AoGCC12PZDJRtLc%mXqH$UGqPfXo9j z56CWGa%2vk9!9G?F)bYyh5Sa@C9cybNt^we_ULyP-uPb3B%I( ze}3+Gsx&5kVzkyUSB>lVl6l*V!Din8rh}VbjK7Ql&@BRaB-2UBvz>V8iD3tCp0XL7|?FXjb zxIe|+W?1`6#%~(F8w;#3$I@wc-rvnX?`}Wv=G*`B@jEBr*6Z(_faY87|IR0z`X%Gq zOXL5(bhvW@m)`$3G)ph;e9;_pK$y5?GHfu(B;#`=YQGw?GGrG z*Q`vcc>T|euUym2>u-O6+I(;2p2Tmt+xUj%`xg^%`#9gaKBwXdtlaC$42f~AY<*Sh5daZ5LDS(|@-(Rk`z$5*Bg5aSze`Ft@YH^!Uq7{B%V z?;O8%O7A|t@^}F8fbRVMTgR2|xW60ki0g0sB~BFIf8(87{}SW3e&csNE@tz_-LBl9 zmUtok`zqr%W=mWmexH_ft@uw||9u}Q#{~sG~=BI5CmtFsN>-BdZ|1$Y-V?6ba z&lgYRhWrxaTGzi@yts8-`;PHD<>#%}OUvu^Pvqryk*_!2x^w&%k$vljEq$uEWaTTx z&E8G^Z&86|S zHZFbW(hZ8?>(654dTCs`!KGJPD+9|%>87P`mG0z@>zC9zF~iN$=iheuvf3xUKwFHj zOx2Ckq^me>OCNvdcig<_*QOf<%|_pxbgkctgZ~-@%UEM&Xp6C zZd|zhYU#dL#y8wCzBH{X<63u&FHP{u_#MM56S#E3ozt_TH-G+k>dI&Hc<1!oIze|% z&#e=nbeH?Pc>*@vW&GwJsP!e|cNxC<3E%nj#RWHh;N5=S(i*ii2Y35bd$_;3J&iE1r?}DwB^Ot!1?!R7~v-Aiztek%5 zQT&3tPyW)m%^d%N%kTU^`5k!%K%N1427Zt;u%0X8&q&)z|E5^O|L^{fKVB%u zGJVH$&^xP_)B6y0N^imuL1#XxVLqOZGK(cRUh=p7zZ40Nn_D*y4@N9SX@M`w<@k|Q~3yDfjOAoH7tUTV_()i2% zfxiEDaA-i$+Sa0|t*ucsG&L$ZyLuJf-2;lAo*_j`OPgY#zhBWmHlpaA7`=mT=b2mX zUHq0Wxc|Y<4#jYHm!hq?<(cfv%yaJU?zI;U4ROlY0Q=00aLCyj+5w)h5B7&IkKW$) zu(vUS>80~9Fwlj8k-?(7hkIpiey&bSYx7f0y!XcDrf=o`hbP7q{cWv3Pm7HXG`eIw ze#}@82mM^Iw|7m7!j=k{}lZgY?COD82hmCEe z_+3vqp6@Eg`t~9`-dcq9t%Z2Ey%@orHE3$dMO&Ex8Y?y~QRzM&kD|3&2MrYt$cT1= zrHMYST)8ryo{?+WKQQ`}zP^!flz&DBhZJ3{ZLb7)cvYyHUc%ALSZwSl$EzJhc(NrA z>zZ<~z9Ab=*Jj|c>Qp>=Ego;wCc?Hp7sZtks7_Z$P3qrKo4N`0nOo6Vz~55z2^xyF z^S7NqbA>fB66|4Sq6crUfU?G>?%#BDbbTZI-^Y1xsH*zd%-VYVu%9b7Hy81~3-Cls zCe}8l;Yr@-6W3y}sw@Z(7kS~~Ja;^s?T)uf17UMD0og^q$d6G)M!*M1^LYo^LGPe2 z@;zLQ{}i=ppP?>iBkJ?EqNPL~4W;(*aWjFHmHlv4b?pbEBP0Jjey1lC12a>Kn#!x2 z^(@S0lv5({K}QLmY0APAb!m8_Iu5JLL$M~`6OU%vVQsoKUdeXEpNhP3pdtbno6?ce zR*6Rb-qx}JTuU`UX_N-CeYYXe`G1h?_9tWq`~^jke?tYIO-y5_T?sd37lu{-E?`O`~Tc{2@C{?s)$sTtc?&m78*#ecWvuJ@S98zVueJHLx^|UiVUv<$n#O< zZ#aN>m%k#)?!S@b^xw$ze+NZTf5p|5zvF897PJ&G%OdZOI#D6#EV6B+%%5cuN7amEs#e<1Pcrfk)){vfw zx`;IqT6iGxG@gpNfM2IsU~9hDxK%|&XIze3b83)QcZ|ahgkE|V0T+LZ5W_c-VDAwE-zke@0%wyC{i!ALYp(qapts(ui$07uWut?)I0)#s(F`!+oTFiEh9D zk%4}NpwWQ=#l+Ci{|yKZtT>(+jNdnB;?Zl-c(^POt8$&NI>ihR#azTgk!SEg#7R6D zdJ?O{wD5L@3yf;B8Uy;OtqUgmj`YkOq{{i0rfMRlFR8f$Vqkl5QllPvEwVZRMVqdJuw8ttj=TX|YFH9Z(6>7gNlm9F~z|AoMLclUQyp(%JrgBF+J9%7#Xfn%ubK| z+vrf!?<$IYVj~<5z(?l|`02dN=khW_4S$1Jvp*o!?QP_R5F2sCMz%iux&9RtB&?_Gd$iC8t1|5HXq*xG;~XC3z5g^= z4Ub1?<77$XaPD~j$yUt%dlM!VEekV>{HZ=g>HLVIZ*EpGFg3S){}qapm|Ve{LvQcRiYw-1_kt_- z@o{MklnT7?DChlwc=AkyChiYb$Adw~aeufbw9DfUqyiSD+o4v1>t5JkmmN^$oBsma^npV9_lyP-q!X=S4YPk z_dh`1FDolOsgV|n7r6GUqD+4<=`tRSJWoC+zlW;e!H`qfo$EhSH!<+;0A>~abpQOb z_uto3A_Q=*)N!=im42ui$(BmwXOyAl~Xtq`JO^{D|F%54S~m zdD)SH!I3-fe`MtUM5d>dd{hvOM{}I<5M}y9G3W6>*lF_n3H)b>3SLbxLvnkK{^;y9 z*S1MTANjp~VNOxAIIgJu2l!`lyj?LmT&gAQ0LU7qIF?f(YWx{@jJ!)9~jxGzcv_lIiW{vb8{BuEu6Q~sp3*11gccaMAy^$`zwQ@vjxe<~GI zlYNTukxtHkr($I2X6jH(jP)sMtMbi*%|C*V=8N#<{Rim0O1b(PqAlJ)hR4T9jIu#h zP22X{@4uwHOm9<$FIJ_R<9@E8KjGZ}G(-*e1*(#NPGM7)>tgdj*Xu$b>AwpNOmSa8 zohfIkM^SQD`PoeU`xu^ww_;$hTQPVW4Gs4yy1HB5jP+87pZ3e}BOe87{R+Vs ze~pMsuOY$qZ6t(V#MR2`E4SZ&dUUrv1^Eb=?a>e8*_2J=RMgI_Wlh2`n;Za5Z@Yn-s(e{J> z7hZ?=S;0g64HA}rg(%b4k>Gz4r9}nqx8HwaaZcLXnJ&D4p&Mx8f$-DBt~%}yIf>_^ z_2E{OkucEJr|9hLB6WWbbt$^3x3_UE>7AWX3`{Srzdc-kd-}Q+UDU<9dV9W*dIkpm zEiXN!FxceJ@S+^`J^w2FFZ>2Ulttl}e}{O_V<^eZioX5+lM8dxcI0{CPtW+nDr&3izXH{9Kij7087I#BI}}}Ao#YGN|5u=1>Hx)g zi5c;(`w(XOH$<3yNZ$WDVyr$vyxm452b@81e)hH7??1n=I3qaE8(zsa@Jh0QM}n0^ z?nzb%%=SihNnUhqLxZBKzV0hgO=E+ikNV)?-~iX!9z{Q&Ti;ip!R|gqeqLH;c;H$1 zIqX5O<8FjH?Lmn10ff38Mnw1p6c%Lt|Lt6Pe3jLi{v6vnZO7ucltnO5wWCw2Ok0(X zIyge77PL|kRQ9k0MUYLDRT4r%5(puLC1g(^`$j_c^=9AqeYs0+?!MpLC3k=3{Q^O` zplxTSf6Q-&A1~)_-#PC&-}%mY-{*M_MehFd+gpm|`5{my9)axI5y;8dQg-by#&3k8 zv#z{ZNpXjgFby_N`c|Xw6-t_C6i;ci6mMx%l#dfGB9<|7iJXyBU%ty!ay3(4ncs5$ z(t3FNyopmj|Bh4MWG9#5wC^&6MS7vWsVdFge?eb+v5kCno?8n?@H#lj9Ou@-6|xTY zs4vi7lx~ojOwZ~lF4NN7zMpB>&+S%5LVXYtRy6IujQk%TmoQ4g7GzS25x>RcEcPSS z&P~mZ)qQ?!F}8SlVC%N|*s|3FTef?^(`z0M`@e(M&gN)$|M~o(P~){@a0b2yXTU1B zSPNQ1eR&@)igWw2<1i$bpHPy2a*O#sHpwU~7RuoajEG{XaVf2jx5uCLpUdMjBB6x* zqd1q|-_O)^U&nakORvWHVfW|HVdLhR*sy6fHf)@Q^&4km-KHmS^z=&f4)%WT?!Qbb z-l$Ivf-`6}oc^oeIL-S10URM801;l`ri7ucqdi%vGci(?j*;EATd(!>nU)dK{E#Zt z_w}F07cc_;I3tpbGaAaHRBFXNrkl_*ZLRI`!54R9*S?>@bL%W@^qhnBn`Tizeu@p7 zXCWwT8(}ismG1r<4C<#<#R+Ct@LIV1--DIz18Dr6A!~u~ZO~+$MoCV(OrbH))SGOK z&S1S|Ml1P4_k-rd7>%z~srgR-xdJYup2|VAv=0~j@kI@l6^F)k6m5uFfgz8vd?quvX!d(T{KWyha}t69!GsDf@i> zwf-x$Y`!F?=UB<&lCzUjQwO6Gk0SKaYdC!LNqkE4XUq1PQ~s}AKNIV>(%eZ2&}dEa z*=mF2UjHfn(op=R9cs(8MeKxwy$7dh?Vc3p7CR|?Qk(QR;dL{A{;?mc^FTev|9-0fU-813oISZR;-T9E_&S!^SMSD-( zR~jQ>ygKX|q~2jNKsoQLRP_V5uNZf&R_f)PM-Q1K0n}r?OwAeW_Xfp9@cH0hw#`VdaYi($l}m2CY$1p zx%=<1D4A(<(rcGZ&Dd4EcTH(Q2KyD-6C(D&dC?P=Fp7&JcfghLIUMEQa5e_Q*%krx zX8|Ln&~_JLq%IdVH_JJ;%1RAN~({bqTUNPOW#0J`tyj2c^;t`pMkgk zQ`q;#OnB}5DR%DqIri^=0qZH&2?*Wd7?%vKv05i+Ua)iHYjgMC#ro`KuuU*F33rV# z{fyZewHK4AM_`Sku@Bz~OVke7llQ=te+15IKe$@X!POZK^u_^$slZqr98xY+qH?r# z??FMu>&VVqh}4XqBO&n_ghxMvz;g?5^s6WF+5WlMyYHWH(&yiB(r+m|KY12DA?sZ{ zanBA5<)UU9%Spl|ziEGw|C6*&5dUWrjs8)Wch+o6I0{EjC~P-_VW&LQLNWRz<#Sfb zkL?A=VdtEJv*|QkZRdcVRG5cbA?Qm1hx0ZP)8-?Rd=9&^0DUfaM)=daMoUjt1bbK+88)E;cyV{Vk4e{HEZoPXgZ70(|i><*=rCT^IJs5yoTu5 z6^Kt*kCc=>$jms78#hkT^WaZw=Njxe^a>J^PL0X*+&7gvA)_ID&g%Lf@ZU&ywUy?V zN&L9Evn)za7>2Dfj^=$Ht)r!IbeF;2Ujge-6|BQmunkwj!fSw8*bf=65nbJBXlRZ{ zb7KNpi1%n9eyP4D3AHr|xLFW{)3m=F_ov*jF*RLh6wlL6iYT7t{?GbPxCjV$!i0oz zs)tsX+Vg7+6-lr)<-s;o59?S59KtR*$GYL5I!{ixA)&7rRQ3}xF)DCdLF$N{nR$rw%jCa57+nC+?Z%DtG2Y4 zwW$ZIJ3G1xc}3;%{LI4e{-T;|r8TYjXL`7ee^MF8=4c2ld))H`<-{W7;nrb7IO*3*$k5zwXC$zoz?%+x@;X4xHQL zBDg(H)8j@LPK_gOk1LHbjliApW_4Qg$=)FQf1TE!pYwNgMpODC;wcFeRgozOXEYMt zDCTmXsVpu1Dn2QZb0+P&@vE$4T+GQp29+79(MU;-wO&uW-at8r-@wq&F9pIeMtocM zBqe-OP2Br8^-k=~`lj8leI4O#a=C0?MQQQ5vnkhfn{yJeqN5Z`h3$A<){RRVKIEc$ zhz5K}JM$GNT&Ar=HO7)-Z zzm&L-DWhClW0@qZxp{|AR?6gC{nhw|s0kj!rSRy?#C+1R{E}>WM>;Ny)}oIzA>Ad1 z(2%hKwW;r-<;J_{t5^ko+ZOZ@PoI%|8TIu|U+SoS1>q~x_u!jj&%RbtJj$A$c&~x} z{_RI{Q}J(9|NN0M%xh1@9MVQCC=JF7#9h8z9)iV{=dp+AhN7}iltp`@B5D)aI^yJ3 z(EHz^DeG<0rmV(j<3Z$QkPoe`#|QyL4q*;)j<1!6;j`|Xrpv;X#CnxrTi;042i*1>Cz=b-l@vvJ88QRjL{7l# z$P0M`RdK&Zd%+6y*X$*|LbPLababh~tYdV9QMtFTRjC<+O8xz`isI^}-G!Ljor!r> zmocZn2ajYQz|Zo0aD1SouFaxe&2w5F7;%{ygUv~oY9=MJbe%17^~lTc9q2m1$BZ>!0{0@8HM;e?Tn!v{Yh zp6TVHvnWw=&k&aJAeZz6y_BzZ+jYdl*cgGRn=vSdN#nw0CfT^yBxGcwt`BlAueT%~ zTZHt}i&1*@O|)dc4NguVx;lFIX-W6x-hZ`B{=Kxi%7#~3(=n5@0W*nbe3W>u1*Ct; zlJtd4(i{=N&I~#Y-`c)eFJO$?A;v|VuT;?PnH93qk#h7`$PD-`$|BxCW6oC8RAdfH zCGv;e+ZT()i#{iwb9Q|MW)+?)%~2Q`)4rTfV1sSV@OqVFtdn{ZVFeLco|P z_Oh5%jNNKt`ddr;|}R9E^%e1?dXxZTt<|5t_fZ_|plklrc`QSh{dgPPnHt z>DY@%_g#$qQ1T&V2XXD4!S3xBcelhp$GMD0^G{6iqYtO=!^e%uGSWrb7aA z93j0*w}bH2+xOq78fCOv%8N*Msv*94+H@28@7EPw=}RWQFrCKW#_5+)6tNs_jm1^& z?I*QYH_k6Ri^o}hmbmf9iDSOfQJJYF98pACSa%Z=KB}}@8NRriQ7gx3pCJ5=*3563 zN^fF%+DfC-{9Yr@_$6feEk<7GGBlJW4Y{|U+g8)EttK3v<>#@X^c*&GE~2oz=?Z_C z#|#aP+--yOesGu(v$`A=i`TM793^RAziBKNHPqW2P!O{Xd6zyyVfcC!$8AAfd5YA% z{n6&CMq!2z#*&UhkaP?J%4Y{!t20>~Q%v=`n~=1^a^kZzv^TQ+m5TT^CCi}@<~(Ke zTC3DCZEba7!6EM>@YH`G$o~(7%dJ3uNqnz+`;w080uV2SfcJ@CSPg`3#7If1LTa=9 zR89PoinJB1v9DP^NzP}84RJQ> zx6MX&L3F8m`$}H_m$uL?aQdwzu8DlOuofd}=P@c#zM|F>hom(!N-b%p)r2F;Z}DK$ z?F&fLJT^8)93aIEqPbyVB6A|rg_4KRz9q-RV_ zeCc+^C718JW$2Ib#( zbo6hGzZNNtOWTN`^G{>%fqB@xWi~$iXa->l+f*W%aDhll`LWyhPgo6%k#IeubYP`5 zitsz)863pfB7yjka$lH91Hj3kI5Y8jdue&)iOzw6l|tGxBofIB{R1O!l$BQ>x}KEM z7?IpXIyc6=_j@0bntzI*L?0fYRO?H1=GGckGZ{%2?PUWCgzoO;GGQ0;HK zw1aTn&2U^KTr}x3pz;)9i1Cosr=cwQ3UX6ok)4u^#R_C!wM>$nm{ z7@W@>!aSdZ*A5SC+OYunr7`Jtr{V9+78PS6Jk!1ZcQ8)EqhOz4ENx|%tg#1RO*jZg z%pTYhcf(nD7-*zNXCM|BN{5Zt3EogLN;tnK4D&_AC3@gY2x0Zq?=KH8#EBEHVFz(* znMD_G+8xFpTO8UuSmn32&tk2Fd4sgImi|hATh>{Zy)X=pl1s3coF_X&cw`_P^&zl! zm!P}(9Ihw*3u$r};Zpb#L_~au*!WLr{5QkfcRBLQW8!rt*+01Bjo;Zm%cGH|6js8j z#eKgw)ufEr8#3VNEQM{bnsBXZ*hgvz+v>qM#i&iKNrb<#mV%0kC|pZDh6@pU#a)A( z?GuELsR{dW$2$LP`>dALM0|#kI5DGehK<*<)!bEHX&r1Z*hbo5Aw0?==R!t!T6<3g z;ea_Pp{+m+eWcCXWFJeV@vv-z{BzoUG)%q zJ=HUV>hqv_mrTJEEL@=@@Z|O%7N?j+#-1trzkpLr<@qeeKzM+X^e9@LmeGpFXSCGT zZA>XCOb;l|=?yE%7Zns{NDA{328xSvb871vKkgqKeoUdH^D4y0s`OgQWpACGqG7yi zExl7earbOa+27s$epq2n-)c?{ULC2&vw|jkBIyC2)=hrPHVif&pj_PtH?w28Ev?<2 z>^UvMy1k`zqRQtVP?+ zl^CQvnA_ro%6xxRR#aY68797`q+FQI#ohha(6|^$^U&1R>a(VSwAz&S&u)stEYdqa zTjV!&hGJi5hJjOgNnJ^CZDH6-6kc40>crns-oFNo#m7-wR};aWjksT)X{PuPN#?XL{9WCZ11US~}8{xym& zEY;8nUuAQo#;lbTqbKs@a`g}0>mTKEm#(Ty$K#w3%pm>o z;*ua^zfrkNNAqCVX<|BUIz~+AYvi(F%0+pU4+Cs7Fzo0mBt2mi*v7)e7QI+v2 z#dU)eVB9@_N`|`6JX;k$rBh{dz<6#;sMY0S1Jib5l=89 z-DO9Uy%!_H_a&4>yoHvA!V>rTFLZJW9xn97k0`JIr+i;mpHlRy*2tc*A)OA{0OfWP z8XviAh;n+$>F8XDn$78S#Nn$IPv>3UU`jr+5II3Fp|0rS@ICdnm$tq{8p&UgMte(j zypcP~pUdY87%rDLWjrpQ5z})aQ&Rpy&mXJNAkE2CNjjtNS7)Cd&JOw=3NE~jnw!CN z4B+nZ8EMFC>`gw3o}{DbE{w8_iN#M(*((au(^*f}b`3c?Y*VSg-#Hl|5r~d>~B%L9XidU;`cFKPz8RaD1SBkCWlwYuM z_PCIE63S26a|3F^Z$#s=-}v~ivVFMzAF*cb3|xvi*yUcozOUi1gK{i3zO+T`KyL#l zRbz6{*$>JsNcSTmpJlh}=kbW|Qi`W?3WLeY)HHIg96Y>;a;eAg;rhoYkBdukuV14O zFPeHc3sAnAL{ zXt{TNa8R7>B<|ck-o45ix!+|c?Sb?9E};4}bPZ7$ERRD=X|AiF;Ho1zYa8hamLekd zY2xE&lXh$YKHl?7G`8h`;UpZ)Y@ry<&Hw2*8g>I?<8^GX-Z(p9&pZVOX`NAZ251c> z9Y6`XT77Um^?Bl+e}jvcmLn+m9ry;k>!@zY{F1GEDqbXQ#kbY(Vq;X&FF54`i>>W> z<+j#5m#wz~j$w*TiEkB*R-&2Cxm8!k;Kt2!NX`mq=;zii*Hb)dwyW;MgWp-dk=mGw zKgEn)Ht>pdv?biiYj3xX_l%8>HmYk{a)vpL*$Msprez`-kMb`z_TztN{qF7kZ+q`z zP7oKigyp}8^P0g3n1>k7R}V5AJjhhR_rAt{$Nq->Eqe`1K(Teu%)fJg**NSGvibi@ z;xP6cxRPQVokaS*y7tzs-t`p~ha1X8)tnef9p`FYQ&Y`nV?xD`RdiOB#r@syvyx&M zT0cp0{->AVqXVzR%SDFj$~tfT)l<% z=W&;n86^xstsEijmGV70ht!Z8l$v_{Su_-$YjVGSPkq|(96Aeet*7R&n$~22NW=(8 zJFHL&$tcDo?dniZ?Z!<1*HNCh|DJu2TX1!V8%}4o+pB%0q;+Mr;SzQqW!HB)N1A?=NV zv5UGsw2d?tSa@xso}N-=O;c7?b7#qRxlZr{vqMc|4dY*14ZW`Nz4aaY8w>rGy@s{_1kebYzW@LL 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 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 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 426/572] #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 427/572] 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 428/572] #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 429/572] #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 430/572] 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 431/572] #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 432/572] 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 433/572] 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 434/572] 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 435/572] #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 436/572] 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 437/572] #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 438/572] #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 439/572] #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 440/572] #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 441/572] #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 442/572] 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 443/572] 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 444/572] #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 445/572] 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 446/572] #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 447/572] #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 448/572] #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 449/572] #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 450/572] #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 451/572] #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 452/572] #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 453/572] #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 454/572] #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 455/572] #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 456/572] #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 457/572] #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 458/572] #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 459/572] #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 460/572] #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 461/572] #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 462/572] #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 463/572] #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 464/572] #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 465/572] #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 466/572] #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 467/572] #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 468/572] #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 469/572] #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 470/572] #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 471/572] #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 472/572] #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 473/572] #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 474/572] #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 475/572] #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 476/572] #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 477/572] #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 478/572] #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 479/572] #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 480/572] #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 481/572] #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 482/572] #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 483/572] #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 484/572] #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 485/572] #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 486/572] #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 487/572] 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 488/572] #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 489/572] 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 490/572] #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 491/572] #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 492/572] #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 493/572] #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 494/572] #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 495/572] #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 496/572] #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 497/572] #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 498/572] #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 499/572] #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 500/572] 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 501/572] 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 502/572] 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 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 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 zcmagFWmFzL)Gj0Du4(01XZJh)ID=2mmkx0DzSAf76pt000RI04yy3n?8aCfSHd+ zNa%mlF$e(AfeHX%@c*VUaR5Mu;G=?%>%aP3nE^omqim>xoCGog!AEWYS@OH6(ntK! zB7g=5^AVC`rP%r7!Pj!(S*)S1 zYWo?_@2{=Pf2CKcyRusniLsw(?`k+~g6$)`76tVN))ss)W4gw>l=O zc{Q%0f1eE&Usp6Z0|VYZLb-Y>6BJdG_xV(p}* z1%j#wa~Ze#maez6{+}mJd7j|jf8eCl1*&0}E`(pmAb!6MT841jEk)|PdZ!e)l=cathgui8AB2AE=|I+JH-dCeX=4%@EorsF3 zN&zNEQJ&b{a^0Tet;{EJC;s>eim`)tq@;v1_w!{nRu(tLyT^{m1De{7PJnBkg7ity zU!iBYREn&}@8ku-c>4_TrCUv2UNEG})y#iE{~W&L4}7-_6`_~yQIpFX+Rc16h>jRv zcgmyHM#BD!4tmrg?_n&?6r29a)7AI65`aUD-cgtD6-Hz4*b};w(xLg(`@OU9QHS>_SFJ&>4Z~7aG48KKaj^dRtje@V_g&&L^(VpS>o|c_(>c-;9S}p}LdR zID}apgpgP;;JV9YNB{itqzU;Witf!m_~jRq=lv2aH7W07Lc8H`C8c5#Xj(Vk)GO9* z0Zt{(Z>CJt)PM4HJ#b-V#u|h;8zruNu+ZZq_XgAXDiwIX84YF-4W%E@dfAU!8_4C6 zp_Q0Pqae{Z7*Y5KG&Hn<&%O}SrR0%?ugg;iti_H_KiDqIAB+A&B&|r_4)BEO_B|Rr zGXj!(Boyb=x%j>i4JSh$x6^3^*O8D(tIDIce_ho+Ev-N~QPChr{ zM)>bCV=lL!Fdb0vj7R-;8d@>$E3&->MBQ}J5;F)(E5|*-4+hzA$@cD9x9*+DQ-(ets2J#1kH_KGPB~I z66`1ln^$CMrzj*Zf55`>T}Tx|iU*63 zsiw1XrD?jViB1#k&vQ)ztS|kH(hcOCcR$}CXGk4Z5N3r13%o{l)iVV$95@;Bd%BO; z$mjPI^H-v!(2>k8A<1c&8GI0us!mNjPD?N0&8y-R4Zgs=!^tU~_%CT&q^DZ5heZVU zkk>`6)PG;}re9ujbHzsATY9KvhJwP|`LY*7V9I&tQ(pb@(bzsvUPvP} zxiq^Qe-c(2eV6nyH;K^eD}PE{S&kachze(cYf?vOg=AxiJUSvEIB|Hp%7o$Mq$>+~ z%g}E)5dQ0JG)|I97i!g|@kFS!K@|hz$X4`ZMUUfnD=SXI?%iAHbi?0K!F}cTD{j*M zsOK&pSv{jG%!ag2fzb>#5p6W=MDLQa}^ zzY(&2bGK*wnDh8^+&K{F*>c5tt9C`7!5uVrdsq6ArLE4lY$llQlC;~q1^9S^xndN z?*+6u`6v)uMrQ~KQYD?Fu0h(yfG|?b@fok(`**+e(JX=Vg0Y54!S-jb$!G7$`u$(# z-S^KpZ$o4@nJfeGvrJYESm~@cSKGMfH*oGWFZM)W^BN=d>A+m!O)(4IK}$&2 zb*Y$s-n0rb1W71ps<(MPlac-_$Ex!!R`cc=6J3C(gvnj6YV+#!G+cT!GBCIp^T3c! zN-{IU3CJ6&^$pD;bkiTb%HfA?AqvQtU|Qmx3s%MOBZlVE)^l1)tf+9L0R>^sjO{)O zkTi@zBw+JkqK1u@A5k@|d;MZgQg!TB3?Czinl zk`2rm)tC|td5mX%!j7=8Hb|n`Z-*IF%#lYF4QOS&Ru6gPIB3M_-zZLj^-Hxl8v%rl z!ty(z$A`{q574Ocev!gF*S0(fI9c3TzFHUMGArlsP0hK}Tf%;+9KAq4U!!NXy_2?Z z#gM;0v0Yei>xN4VCPIcZ?NRG9{V{sepH;hamnz^1Hoa)+#jrx={k^Xjp}4@tX3KeI z;rgwKyS)k~BZOOczO8(vGv(4zCH~*|)<$Y-Hl(!&lSa+@)f^*+2C_3823JqyBRxyA zgG4+7i_>m!AM`6CSF<`1GhABTPT)gyCLC%&*0mWj`si)aHxrgBBiXi$T8g3W$`MK& zXQJN&K6_2SrYo^g6`qDFeAHAu^TC+ETVMvufAWj>aS4v;xKk3OU{Ov&XP06&y>lHZ z4O>BOmS^(W1qCk1z{ss!Vv&35@pF%NSNej4lVI>i87%%(C2v_%`2#=~pM%omOk*Sot zXO=?9!NozY(nTQ^NSrd%(0!&hJ4jJy19J*R>StlZNYv}4INzNOzro~rh{ZGpO&+nE zIA6s>N)$4B84MQ)m&m|OluLaK2V>vUMCbkb*r)84BUzoUt`dIcGWaTQ=+_}CcbH;a z@$v2z7%4l|j+OUky7cGh)Y=UW-FKyF<$ISS2bWC>y1owE4Ia)1h@-6MJSBKh`m2$} z7TkBnKlp)n|q;Y<+vUq544$pOdWrc6;!+Vvr@{ii?0 zt>H#?{_jfPcumq(Zp~MmsGICQg*HLL=XE#bhy00sy7w+uye6``r-ux@EpdIGSr*D; zqAnnLW4(wW8seznJFbJ_bnq~|QT*FPUanFrUL=O?HUA}boWkS!cUBPHz~ZSIt@+gq zMlgBf$}O_>HtDEFj=#al@-LpZtobmT2*q^yApD{Z;i*a-UG>kz1o#?wst=%xP%nOp z@w>TaR}&gntJ-Dplsa#ppXS%gdVcdej>2Dp%paRMDng-ES-s_AkTrcvuA)?1KOyEl zHOgFZhpc!MNCd7lqW%m1-4fQw?T-CkBy9RA3>n0%j@ud%-QNN81gN|@P!kOPT|^>7%IJBo@#}LU{-GjE4p18pm7e~Se4 zO6VFKte6+-ZlSEJv)18B4~hmx`ZH%tTP=%Obx9Wzk|Tz>5l(*E$);^*!Jp69xKy3H zDwsSS5O3eZ|M+YiD4@fqNyV(02;ZH2X*`1YzPT;R3(3DWZH@w``s)<0vOg%#fkG$g znI>!ylA=UFyF_@=_3UYJKPtKZ6o#5R2FV9E@rO&Q-9A&sOxgH0*K7UIlW~4K-g-?4 zGd7X5QanoHkS+g13EfqVp`vZBx)_i8ZoM3dkE<1})AswPcThG@7aW^}d*c6t>4iSz zLjS|`WtI->0Dz$WAJhNLI8C>9R++;ddVgL`mn6-DgWD>c#@>L~&QDAkf)dGdwrq@R z`2HK9_YHAT)z--S>7O^(b`VD;84?o#uO-=|Rm!y$JAzn&%287CAgYoAR>FdiG&@^* zMWdF9OC4*C$3M_|dUl@V^~~;^)Vt5}JZpUh1g~d<*buo#rH@yXd)ys740>42L6GS#TWc>G zL4L#mQTToLLCwja%5vo;Z13A-2D3mLlx-2_Nk0O3ZlDlVC8G}Iw*wdT!SM63;rVxWS5s?f1w9k(6%B z-rd^KK?UUnfZ+_bi(!hlkDNeCGN6#%xXFX>~-~@ z`6hUp+h5Dbc9`{jN=ira^K!6&K9>IdtOO%8v-|OG=6-RpR2%Kdm#t8cAR$3GU>%6| zodv!-L|Uqf|6woPjK#1}ViPg-1uvwJ^T7^O2GIi5Up)CGZQX8T>9QGuA2!DgG|aL% zY+=dUL4e?9rQmxQ-Yf%~Ux7OD9jJ!*8Oc#a$37!?D9iJe#@wKN6Dz0%ds7=gf}3N3 z4A-TKlJJN>ao8*$va%{VrPiYL^q{rH zAf=ws(>cLlz~Oxh<+Rw$gAy-j%@+ z9_;zT{cPB#D1cS;MhBi{dT{?P<xHYF($qhg`I_;T)#*?09Rpsmpj7T(jurR|6$gB~h`1 z3kmp#=cRmq-L@SawPtIzl`biQZCE#*baQt%H-0)_pdD_Ff?0mtDs(qpwdn_|LWmmCAb`F<{aE!_e zXxwgeE_5vNDkp`BfyF{=7ACQ$d-SwEYEEV6G*0G#Ba%5G_MXguH*#aQ%1!prlFm2%*L8JRj~TJXFDJ^Fa^#vH;0!U)*!SM} zPPh0|`LjPP*aD4rFnaQ7THApfIJs{<2RSU>o%f-z7SV-duH_x4{x^9kwb}!4kjYrK zvV;UWS6D*)WG!eyJ*Ml{Tgs(vrNRblw&V4~#`=nhfI&kTBdc52h#X^}OQqzJmc;Vt zJFZ_*Nf?`vXEuNpmEf0@`&cd_1a*{JsnNy9Yw_Y=>b#v znv$EPS*mL-e65$5W)=0DqZTP`PJy$;=EBADI<1Z(K40hGL~Ane@Fy=-DoXAj_Nx)+N>A|9M_>yR?*87H@@H zM^E3d2Wc)t<}2v$AV%xOvJ>^xF*fJB>|d0Q#Z`a@2oi=l00x`V5Q63y)$M$i#}k_f zGIxHmJ9xu@12cG2bKh4}nftSohaH44WDgR(m5c{^&WS-C!U=@}1?K#&p0>Uul$>YL z(E=k2p=dr{3tEJVGhxvICQ=94)6z*J`>d8{ys{LJR+G)QZto&BWCb3YmhrQaJMH&N z+c(M)likMhUI~wcq^8}8o1Aohsn9qdnP0CdHwW>czqd<`m<21V)8!dX)%vJC=SV~t z5z(Onn5r_z&rJO z6tc^OPzs7{<&W=Ah43C>V9M~aQzfO|WmJ+3nK3b(nDHS!>!+DUNj*rR=44Mzb0d-?y^qnYjoDsyu^KKq@L-w?iuLHmV?t!g#Xw+ORA z!S|Q_oow2wM<9bj3>c_vvV!JAsGMud4Tyo0glqBD{ae=$z)J$S{qjtz?Ik6YIh%uW zd!5&>=*ASOl=U6l@c}PyY3}FiP80vKR^2u__p=Z)oo6IzK7x_hkp5+Wd!)r(#OPOZ zXK@OBr;Y!*(WlP-sr53Cn=Mo5GAXk{rqIC5+I~!H$PA;3>W1W}9cG-HALtW`Jg|4! z2tlO~d0^Ty`;?9H9)FUw!P&^qSuu$&Wu}g;WmydLG^C3V?}~|$L6&Miv}-nZXy5o3 zESd#`%BokL;tKc64ro=Ype7(Arl<~W!p#fUHZD#dDk?~{U{K+7q(*(R@5M@?G|e^U z!%318ejw_)omi@)#2hi)-&|`amwuU!iWBgH1{roONy$O zeLK{rsVK+5Jit{f(sc_kHmed)7ahvgLA-JOe$pPw=aHeSz*2&eGx^{U9Xj0bJrLn9 zU7c2f7$;5U4b88wKL3i^B+^~MV|=4=tVr3t_Qj^*Mn{hpGbsqgL|uA z7T-_eGdb(e`$Maa5CgfUAx)mtvdy%wLVBX}awmL1$bM zIxjt4@Bw|zCNf>yATL6RanRO`X1)wUO+n}vlPI5O&PtreC8gu@L|d#Dm#`Dks~yOw zG2ZI!9{K}L5r7z+(BW&4?s&wp$5gcbrSPvaXp;QSa(ZS32{+Bk<}5vPmx7`%A`qnL z23FB;rMa%+{5r*%LnE%;o#RRHwS0zQ`3Y5mOm3@LQo==d<@u@hHNR4@428vPaCug~ z;Urn*kqHemI$1oPKd-L1uugH%dU5ZQ7mbN7rfi|26WD=$K7vD39|lHT+ME>YiR)L> zfR*)Zvqb5Y$xOgyedx>sBfZo|A3Y%tMEVc9-&jzn(w?K3FCv#0uG@~=+$2L*N)-wH zp~8EvAYqTIUQa@4x*V0G%l}9F*vIKo9?kl{7wC`$Oc~i+%g1 zW4dvT`|*W-8uQmI7myhg#2mT_%)b9B(1Hrr6X4EX>OiLp{cBSo{^d{1a1!q30PnJuENXEdDc-Z?~?+ZWCd(leMfmMe4+DN zV^4gCLB|;3TCNJ{uNcvpI?4)Hxylekr=T_s0TMf!NL13itG_Au*7C56wLdRu_RhTY zeA|5?S(_?(<^Fy#9+<|YxwDtbF&5}qOfC08cFb13BK)^e(h+n~)uSf8Xhm>W?7fH? zZF&C6W;j$A40~>;v(ruQyOef&XpgAv1a&2WJhhHsL$8 z4tM>@4@hGHdzFGZ@=d3y0jocGR9#!?Ybws)%=RvmhK7Mbv>i=Z10x&5%bi%bkYUi^ zy4JEPhCnq#20>_sJ^Ig4apH@Vx`st=#-qv8g5x3N{-jUBKE&drrI>^@10AGsyZ?p> z4ffLb4IC-idQHN~?@4}{?_-W8MMgdF;?dE_q}Yd5&Yxp@i{BVCa^0_X-p4VGCJ`YN zJ1!1JwOZf8DbhuXisSvtE$i|>_vUJ5;&|C2%+7QVExU|Cz?D$04qNT^GMu*A`ioqC zN&TR9%WpE!2~S8O_gbTXvvax8wwU}T`PVtff6|=NX3c{luNx90zhF+ye`^I7OFck~ zl@S(?&sY||chUYR=(l=KJkF$)XWMO_O>o!JQZd7w(Wc*lMMAT+fWy9$foWsJUg zReSx5Q2=Z1JYxI;^6tys`~$!l7Y;*A!}5jT_F3{xWLBgS+Xyv>&1`R(-j^(w z9)XA{l5deJ|Kjc6+S##Va@UgPEH?4rE7fH+W1rJ94I<77T+_Gj0zE11^Lm((gX+%f zzHVRJ^C&pJ3dtH?8q>e%kQSWrBDH;TUF*zMfop>u{*OJ9ZJ#$_NX{=Xxq;Wz6ds>| z*#%yI92fW8le3mmsocI}2zb_ev942NGg%?xV#8hKgwxB6ZmR#rLiz*3YNcZH)cS|n zm#>cfAb#qkD3P9Dv?19Hd914v>;BEmtQ^y&@=H$(-@;EtX6M18qLQNmj19OUa-C)5 zuh|+L| z?oJ#c(w#lz(2zFl?b}OV5!c{fYlwkqd3okgN$q6Xi6BW$%@L+V%V`is8xdZhv@bEe z$8+7Y_^-pDle?fWUhKu=FnfOorVU$k7wi1(<6^uy`BuvIHN|%OEwxS7J}IV12oR1bMT4c*>^9#lJM9CGnQQ-ThFGl8%O>5rT%X|l>$;1 z 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 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 503/572] #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 504/572] #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 505/572] #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 506/572] #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 507/572] 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 508/572] #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 509/572] #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 510/572] #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 511/572] #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 512/572] #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 513/572] #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 514/572] #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 515/572] #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 516/572] 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 517/572] #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 518/572] #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 519/572] #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 520/572] 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 521/572] #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 522/572] #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 523/572] #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 524/572] #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 525/572] #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 526/572] 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 527/572] 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 528/572] 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 529/572] 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 530/572] #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 531/572] 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 532/572] #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 533/572] #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 534/572] #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 535/572] #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 536/572] #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 537/572] #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 538/572] #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 539/572] 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 540/572] 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 541/572] #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 542/572] 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 543/572] 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 544/572] 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 545/572] 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 546/572] 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 547/572] 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 548/572] 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 549/572] #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 550/572] 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 551/572] 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 552/572] 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 553/572] #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 554/572] #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 555/572] #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 556/572] #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 557/572] #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 558/572] #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 559/572] #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 560/572] #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 561/572] #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 562/572] #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 563/572] #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 564/572] #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 565/572] 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 566/572] #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 567/572] 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 568/572] 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 569/572] 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 570/572] 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 571/572] 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 572/572] 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'