Merge branch 'master' of github.com:synergy/synergy

This commit is contained in:
Adam Potolsky 2015-05-26 13:18:22 -07:00
commit d816ed6b43
53 changed files with 1569 additions and 787 deletions

View File

@ -1,5 +1,28 @@
v1.7.2
======
v1.7.3-stable
=============
Bug #4565 - Incorrect plugin downloads on Debian and Mint
Bug #4677 - Windows service log file grows to very large size
Bug #4651 - High logging rate causes Windows service to crash
Bug #4650 - SSL error log message repeats excessively and freezes cursor
Bug #4624 - Runaway logging causes GUI to freeze
Bug #4617 - Windows service randomly stops after 'ssl handshake failure' error
Bug #4601 - Large clipboard data with SSL causes 'protocol is shutdown' error
Bug #4593 - Locking Windows server causes SSL_ERROR_SSL to repeat
Bug #4577 - Memory leak in GUI on Windows caused by logging
Bug #4538 - Windows service crashes intermittently with no error
Bug #4341 - GUI freezes on first load when reading log
Bug #4566 - Client or server crashes with 'ssl handshake failure' error
Bug #4706 - Installer is not output to build config dir on Windows
Bug #4704 - Plugin 'ns' release build is overwritten with debug version on Linux
Bug #4703 - Plugins are not built to config directory on Mac
Bug #4697 - Timing can allow an SSL socket to be used after cleanup call
Enhancement #4661 - Log error but do not crash when failing to load plugins
Enhancement #4708 - Download ns plugin for specific Mac versions
Enhancement #4587 - Include OpenSSL binaries in source for easier building
Enhancement #4695 - Automatically upload plugins as Buildbot step
v1.7.2-stable
=============
Bug #4564 - Modifier keys often stuck down on Mac client
Bug #4581 - Starting GUI on Mac crashes instantly on syntool segfault
Bug #4520 - Laggy or sluggish cursor (ping spikes) on Mac when using WiFi
@ -11,8 +34,8 @@ Enhancement #4569 - Reintroduce GUI auto-hide setting (disabled by default)
Enhancement #4570 - Make `--crypto-pass` show deprecated message
Enhancement #4596 - Typo 'occurred' in WebClient.cpp
v1.7.1
======
v1.7.1-stable
=============
Bug #3784 - Double click & drag doesn't select words on client
Bug #3052 - Triple-click (select line) does not work
Bug #4367 - Duplicate Alt-S Keyboard Shortcuts on Gui
@ -38,8 +61,8 @@ Enhancement #4540 - Enable Network Security checkbox only when ns plugin exists
Enhancement #4525 - Reorganize app data directory
Enhancement #4390 - Disable GUI auto-hide by default
1.7.0
=====
v1.7.0-beta
===========
Enhancement #4313 - SSL encrypted secure connection
Enhancement #4168 - Plugin manager for GUI
Enhancement #4307 - Always show client auto-detect dialog
@ -264,3 +287,4 @@ Feature #3119: Mac OS X secondary screen
Task #2905: Unit tests: Clipboard classes
Task #3072: Downgrade Linux build machines
Task #3090: CXWindowsKeyState integ test args wrong

View File

@ -17,7 +17,7 @@
# TODO: split this file up, it's too long!
import sys, os, ConfigParser, shutil, re, ftputil, zipfile, glob, commands
from generators import Generator, EclipseGenerator, XcodeGenerator, MakefilesGenerator
from generators import VisualStudioGenerator, EclipseGenerator, XcodeGenerator, MakefilesGenerator
from getopt import gnu_getopt
if sys.version_info >= (2, 4):
@ -254,12 +254,12 @@ class InternalCommands:
gmockDir = 'gmock-1.6.0'
win32_generators = {
1 : Generator('Visual Studio 10'),
2 : Generator('Visual Studio 10 Win64'),
3 : Generator('Visual Studio 9 2008'),
4 : Generator('Visual Studio 9 2008 Win64'),
5 : Generator('Visual Studio 8 2005'),
6 : Generator('Visual Studio 8 2005 Win64')
1 : VisualStudioGenerator('10'),
2 : VisualStudioGenerator('10 Win64'),
3 : VisualStudioGenerator('9 2008'),
4 : VisualStudioGenerator('9 2008 Win64'),
5 : VisualStudioGenerator('8 2005'),
6 : VisualStudioGenerator('8 2005 Win64')
}
unix_generators = {
@ -319,7 +319,6 @@ class InternalCommands:
self.configure(target)
def checkGTest(self):
dir = self.extDir + '/' + self.gtestDir
if (os.path.isdir(dir)):
return
@ -335,7 +334,6 @@ class InternalCommands:
self.zipExtractAll(zip, dir)
def checkGMock(self):
dir = self.extDir + '/' + self.gmockDir
if (os.path.isdir(dir)):
return
@ -840,7 +838,7 @@ class InternalCommands:
pwd = lines[0]
if (dist):
self.signFile(pfx, pwd, 'bin', self.dist_name('win'))
self.signFile(pfx, pwd, 'bin/Release', self.dist_name('win'))
else:
self.signFile(pfx, pwd, 'bin/Release', 'synergy.exe')
self.signFile(pfx, pwd, 'bin/Release', 'synergyc.exe')
@ -974,7 +972,13 @@ class InternalCommands:
if p.returncode != 0:
raise Exception('Could not get branch name, git error: ' + str(p.returncode))
return stdout.strip()
result = stdout.strip()
# sometimes, git will prepend "heads/" infront of the branch name,
# remove this as it's not useful to us and causes ftp issues.
result = re.sub("heads/", "", result)
return result
def find_revision_svn(self):
if sys.version_info < (2, 4):
@ -1100,6 +1104,7 @@ class InternalCommands:
err = os.system(cmd)
if err != 0:
raise Exception('rpmlint failed: ' + str(err))
finally:
self.restore_chdir()
@ -1294,7 +1299,7 @@ class InternalCommands:
arch)
old = "bin/Release/synergy.msi"
new = "bin/%s" % (filename)
new = "bin/Release/%s" % (filename)
try:
os.remove(new)
@ -1359,19 +1364,108 @@ class InternalCommands:
def distftp(self, type, ftp):
if not type:
raise Exception('Type not specified.')
if not ftp:
raise Exception('FTP info not defined.')
raise Exception('Platform type not specified.')
self.loadConfig()
src = self.dist_name(type)
dest = self.dist_name_rev(type)
print 'Uploading %s to FTP server %s...' % (dest, ftp.host)
binDir = self.getGenerator().getBinDir('Release')
ftp.run(binDir + '/' + src, dest)
print 'Done'
packageSource = binDir + '/' + self.dist_name(type)
packageTarget = self.dist_name_rev(type)
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)
branch = self.getGitBranchName()
revision = self.getGitRevision()
firstPart = '%s-%s-%s-%s' % (name, branch, revision, platform)
filename = '%s.%s' % (firstPart, libraryExt)
if type == 'rpm' or type == 'deb':
# linux is a bit special, include dist type (deb/rpm in filename)
filename = '%s-%s.%s' % (firstPart, packageExt, libraryExt)
return filename
def findLibraryFile(self, type, dir, name):
if not os.path.exists(dir):
return None
(platform, packageExt, libraryExt) = self.getDistributePlatformInfo(type)
ext = libraryExt
pattern = name + '\.' + ext
for filename in os.listdir(dir):
if re.search(pattern, filename):
return dir + '/' + filename
return None
def getDistributePlatformInfo(self, type):
ext = None
libraryExt = None
platform = None
if type == 'src':
ext = 'tar.gz'
platform = 'Source'
elif type == 'rpm' or type == 'deb':
ext = type
libraryExt = 'so'
platform = self.getLinuxPlatform()
elif type == 'win':
# get platform based on last generator used
ext = 'msi'
libraryExt = 'dll'
generator = self.getGeneratorFromConfig().cmakeName
if generator.find('Win64') != -1:
platform = 'Windows-x64'
else:
platform = 'Windows-x86'
elif type == 'mac':
ext = "dmg"
libraryExt = 'dylib'
platform = self.getMacPackageName()
if not platform:
raise Exception('Unable to detect distributable platform.')
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))
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':
@ -1405,56 +1499,6 @@ class InternalCommands:
else:
raise Exception("unknown os bits: " + os_bits)
def dist_name(self, type):
ext = None
platform = None
if type == 'src':
ext = 'tar.gz'
platform = 'Source'
elif type == 'rpm' or type == 'deb':
ext = type
platform = self.getLinuxPlatform()
elif type == 'win':
# get platform based on last generator used
ext = 'msi'
generator = self.getGeneratorFromConfig().cmakeName
if generator.find('Win64') != -1:
platform = 'Windows-x64'
else:
platform = 'Windows-x86'
elif type == 'mac':
ext = "dmg"
platform = self.getMacPackageName()
if not platform:
raise Exception('Unable to detect package platform.')
pattern = re.escape(self.project + '-') + '\d+\.\d+\.\d+' + re.escape('-' + platform + '.' + ext)
target = ''
if type == 'mac':
target = 'Release'
for filename in os.listdir(self.getBinDir(target)):
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):
# find the version number (we're puting the rev in after this)
pattern = '(\d+\.\d+\.\d+)'
replace = "%s-%s" % (
self.getGitBranchName(), self.getGitRevision())
return re.sub(pattern, replace, self.dist_name(type))
def dist_usage(self):
print ('Usage: %s package [package-type]\n'
'\n'
@ -1926,8 +1970,9 @@ class CommandHandler:
elif o == '--dir':
dir = a
ftp = None
if host:
if not host:
raise Exception('FTP host was not specified.')
ftp = ftputil.FtpUploader(
host, user, password, dir)

View File

@ -23,13 +23,32 @@ class FtpUploader:
self.password = password
self.dir = dir
def run(self, src, dest, replace=False):
def upload(self, src, dest, subDir=None):
print "Connecting to '%s'" % self.host
ftp = FTP(self.host, self.user, self.password)
ftp.cwd(self.dir)
self.changeDir(ftp, self.dir)
if subDir:
self.changeDir(ftp, subDir)
print "Uploading '%s' as '%s'" % (src, dest)
f = open(src, 'rb')
ftp.storbinary('STOR ' + dest, f)
f.close()
ftp.close()
print "Done"
def changeDir(self, ftp, dir):
if dir not in ftp.nlst():
print "Creating dir '%s'" % dir
try:
ftp.mkd(dir)
except:
# sometimes nlst may returns nothing, so mkd fails with 'File exists'
print "Failed to create dir '%s'" % dir
print "Changing to dir '%s'" % dir
ftp.cwd(dir)

View File

@ -30,6 +30,13 @@ class Generator(object):
def getSourceDir(self):
return self.sourceDir
class VisualStudioGenerator(Generator):
def __init__(self, version):
super(VisualStudioGenerator, self).__init__('Visual Studio ' + version)
def getBinDir(self, target=''):
return super(VisualStudioGenerator, self).getBinDir(target) + '/' + target
class MakefilesGenerator(Generator):
def __init__(self):
super(MakefilesGenerator, self).__init__('Unix Makefiles')

View File

@ -1132,12 +1132,12 @@ void MainWindow::downloadBonjour()
{
#if defined(Q_OS_WIN)
QUrl url;
int arch = checkProcessorArch();
if (arch == Win_x86) {
int arch = getProcessorArch();
if (arch == kProcessorArchWin32) {
url.setUrl(bonjourBaseUrl + bonjourFilename32);
appendLogNote("downloading 32-bit Bonjour");
}
else if (arch == Win_x64) {
else if (arch == kProcessorArchWin64) {
url.setUrl(bonjourBaseUrl + bonjourFilename64);
appendLogNote("downloading 64-bit Bonjour");
}

View File

@ -29,14 +29,15 @@
#include <QProcess>
#include <QCoreApplication>
static QString kBaseUrl = "http://synergy-project.org/files";
static const char kWinProcessorArch32[] = "Windows-x86";
static const char kWinProcessorArch64[] = "Windows-x64";
static const char kMacProcessorArch[] = "MacOSX-i386";
static const char kLinuxProcessorArchDeb32[] = "Linux-i686-deb";
static const char kLinuxProcessorArchDeb64[] = "Linux-x86_64-deb";
static const char kLinuxProcessorArchRpm32[] = "Linux-i686-rpm";
static const char kLinuxProcessorArchRpm64[] = "Linux-x86_64-rpm";
static const char kBaseUrl[] = "http://synergy-project.org/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";
@ -157,10 +158,10 @@ QString PluginManager::getPluginUrl(const QString& pluginName)
try {
QString coreArch = m_CoreInterface.getArch();
if (coreArch.startsWith("x86")) {
archName = kWinProcessorArch32;
archName = kWinPackagePlatform32;
}
else if (coreArch.startsWith("x64")) {
archName = kWinProcessorArch64;
archName = kWinPackagePlatform64;
}
}
catch (...) {
@ -170,22 +171,53 @@ QString PluginManager::getPluginUrl(const QString& pluginName)
#elif defined(Q_OS_MAC)
archName = kMacProcessorArch;
QString macVersion = "1010";
#if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1090 // 10.9
macVersion = "109";
#elif __MAC_OS_X_VERSION_MIN_REQUIRED <= 1080 // 10.8
macVersion = "108";
#elif __MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 // 10.7
emit error(tr("Plugins not supported on this Mac OS X version."));
return "";
#endif
archName = QString(kMacPackagePlatform).arg(macVersion);
#else
int arch = checkProcessorArch();
if (arch == Linux_rpm_i686) {
archName = kLinuxProcessorArchRpm32;
QString program("dpkg");
QStringList args;
args << "-s" << "synergy";
QProcess process;
process.setReadChannel(QProcess::StandardOutput);
process.start(program, args);
bool success = process.waitForStarted();
if (!success || !process.waitForFinished())
{
emit error(tr("Could not get Linux package type."));
return "";
}
else if (arch == Linux_rpm_x86_64) {
archName = kLinuxProcessorArchRpm64;
bool isDeb = (process.exitCode() == 0);
int arch = getProcessorArch();
if (arch == kProcessorArchLinux32) {
if (isDeb) {
archName = kLinuxPackagePlatformDeb32;
}
else if (arch == Linux_deb_i686) {
archName = kLinuxProcessorArchDeb32;
else {
archName = kLinuxPackagePlatformRpm32;
}
}
else if (arch == kProcessorArchLinux64) {
if (isDeb) {
archName = kLinuxPackagePlatformDeb64;
}
else {
archName = kLinuxPackagePlatformRpm64;
}
else if (arch == Linux_deb_x86_64) {
archName = kLinuxProcessorArchDeb64;
}
else {
emit error(tr("Could not get Linux architecture type."));
@ -194,13 +226,14 @@ QString PluginManager::getPluginUrl(const QString& pluginName)
#endif
QString result = kBaseUrl;
result.append("/plugins/");
result.append(pluginName).append("/1.0/");
result.append(archName);
result.append("/");
result.append(getPluginOsSpecificName(pluginName));
QString result = QString("%1/plugins/%2/%3/%4/%5")
.arg(kBaseUrl)
.arg(pluginName)
.arg(kDefaultVersion)
.arg(archName)
.arg(getPluginOsSpecificName(pluginName));
qDebug() << result;
return result;
}

View File

@ -15,18 +15,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROCESSORARCH_H
#define PROCESSORARCH_H
#pragma once
enum qProcessorArch {
Win_x86,
Win_x64,
Mac_i386,
Linux_rpm_i686,
Linux_rpm_x86_64,
Linux_deb_i686,
Linux_deb_x86_64,
unknown
kProcessorArchWin32,
kProcessorArchWin64,
kProcessorArchMac32,
kProcessorArchMac64,
kProcessorArchLinux32,
kProcessorArchLinux64,
kProcessorArchUnknown
};
#endif // PROCESSORARCH_H

View File

@ -29,12 +29,6 @@
#include <Windows.h>
#endif
#if defined(Q_OS_LINUX)
static const char kLinuxI686[] = "i686";
static const char kLinuxX8664[] = "x86_64";
static const char kUbuntu[] = "Ubuntu";
#endif
void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData)
{
for (int i = 0; i < comboBox->count(); ++i)
@ -68,7 +62,7 @@ QString getFirstMacAddress()
return mac;
}
int checkProcessorArch()
qProcessorArch getProcessorArch()
{
#if defined(Q_OS_WIN)
SYSTEM_INFO systemInfo;
@ -76,97 +70,23 @@ int checkProcessorArch()
switch (systemInfo.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_INTEL:
return Win_x86;
return kProcessorArchWin32;
case PROCESSOR_ARCHITECTURE_IA64:
return Win_x64;
return kProcessorArchWin64;
case PROCESSOR_ARCHITECTURE_AMD64:
return Win_x64;
return kProcessorArchWin64;
default:
return unknown;
}
#elif defined(Q_OS_MAC)
return Mac_i386;
#else
bool version32 = false;
bool debPackaging = false;
QString program1("uname");
QStringList args1("-m");
QProcess process1;
process1.setReadChannel(QProcess::StandardOutput);
process1.start(program1, args1);
bool success = process1.waitForStarted();
QString out, error;
if (success)
{
if (process1.waitForFinished()) {
out = process1.readAllStandardOutput();
error = process1.readAllStandardError();
}
}
out = out.trimmed();
error = error.trimmed();
if (out.isEmpty() ||
!error.isEmpty() ||
!success ||
process1.exitCode() != 0)
{
return unknown;
}
if (out == kLinuxI686) {
version32 = true;
}
QString program2("python");
QStringList args2("-mplatform");
QProcess process2;
process2.setReadChannel(QProcess::StandardOutput);
process2.start(program2, args2);
success = process2.waitForStarted();
if (success)
{
if (process2.waitForFinished()) {
out = process2.readAllStandardOutput();
error = process2.readAllStandardError();
}
}
out = out.trimmed();
error = error.trimmed();
if (out.isEmpty() ||
!error.isEmpty() ||
!success ||
process2.exitCode() != 0)
{
return unknown;
}
if (out.contains(kUbuntu)) {
debPackaging = true;
}
if (version32) {
if (debPackaging) {
return Linux_deb_i686;
}
else {
return Linux_rpm_i686;
}
}
else {
if (debPackaging) {
return Linux_deb_x86_64;
}
else {
return Linux_rpm_x86_64;
}
return kProcessorArchUnknown;
}
#endif
return unknown;
#if defined(Q_OS_LINUX)
#ifdef __i386__
return kProcessorArchLinux32;
#else
return kProcessorArchLinux64;
#endif
#endif
return kProcessorArchUnknown;
}

View File

@ -17,6 +17,8 @@
#pragma once
#include "ProcessorArch.h"
#include <QComboBox>
#include <QVariant>
#include <QCryptographicHash>
@ -25,4 +27,4 @@
void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData);
QString hash(const QString& string);
QString getFirstMacAddress();
int checkProcessorArch();
qProcessorArch getProcessorArch();

View File

@ -46,6 +46,7 @@ EVENT_TYPE_ACCESSOR(ServerApp)
EVENT_TYPE_ACCESSOR(IKeyState)
EVENT_TYPE_ACCESSOR(IPrimaryScreen)
EVENT_TYPE_ACCESSOR(IScreen)
EVENT_TYPE_ACCESSOR(Clipboard)
// interrupt handler. this just adds a quit event to the queue.
static
@ -82,6 +83,7 @@ EventQueue::EventQueue() :
m_typesForIKeyState(NULL),
m_typesForIPrimaryScreen(NULL),
m_typesForIScreen(NULL),
m_typesForClipboard(NULL),
m_readyMutex(new Mutex),
m_readyCondVar(new CondVar<bool>(m_readyMutex, false))
{

View File

@ -157,6 +157,7 @@ public:
IKeyStateEvents& forIKeyState();
IPrimaryScreenEvents& forIPrimaryScreen();
IScreenEvents& forIScreen();
ClipboardEvents& forClipboard();
private:
ClientEvents* m_typesForClient;
@ -177,6 +178,7 @@ private:
IKeyStateEvents* m_typesForIKeyState;
IPrimaryScreenEvents* m_typesForIPrimaryScreen;
IScreenEvents* m_typesForIScreen;
ClipboardEvents* m_typesForClipboard;
Mutex* m_readyMutex;
CondVar<bool>* m_readyCondVar;
std::queue<Event> m_pending;

View File

@ -115,7 +115,6 @@ REGISTER_EVENT(ClientListener, connected)
REGISTER_EVENT(ClientProxy, ready)
REGISTER_EVENT(ClientProxy, disconnected)
REGISTER_EVENT(ClientProxy, clipboardChanged)
//
// ClientProxyUnknown
@ -175,7 +174,6 @@ REGISTER_EVENT(IPrimaryScreen, fakeInputEnd)
REGISTER_EVENT(IScreen, error)
REGISTER_EVENT(IScreen, shapeChanged)
REGISTER_EVENT(IScreen, clipboardGrabbed)
REGISTER_EVENT(IScreen, suspend)
REGISTER_EVENT(IScreen, resume)
REGISTER_EVENT(IScreen, fileChunkSending)
@ -187,3 +185,11 @@ REGISTER_EVENT(IScreen, fileRecieveCompleted)
REGISTER_EVENT(IpcServer, clientConnected)
REGISTER_EVENT(IpcServer, messageReceived)
//
// Clipboard
//
REGISTER_EVENT(Clipboard, clipboardGrabbed)
REGISTER_EVENT(Clipboard, clipboardChanged)
REGISTER_EVENT(Clipboard, clipboardSending)

View File

@ -350,8 +350,7 @@ class ClientProxyEvents : public EventTypes {
public:
ClientProxyEvents() :
m_ready(Event::kUnknown),
m_disconnected(Event::kUnknown),
m_clipboardChanged(Event::kUnknown) { }
m_disconnected(Event::kUnknown) { }
//! @name accessors
//@{
@ -371,20 +370,11 @@ public:
*/
Event::Type disconnected();
//! Get clipboard changed event type
/*!
Returns the clipboard changed event type. This is sent whenever the
contents of the clipboard has changed. The data is a pointer to a
IScreen::ClipboardInfo.
*/
Event::Type clipboardChanged();
//@}
private:
Event::Type m_ready;
Event::Type m_disconnected;
Event::Type m_clipboardChanged;
};
class ClientProxyUnknownEvents : public EventTypes {
@ -634,7 +624,6 @@ public:
IScreenEvents() :
m_error(Event::kUnknown),
m_shapeChanged(Event::kUnknown),
m_clipboardGrabbed(Event::kUnknown),
m_suspend(Event::kUnknown),
m_resume(Event::kUnknown),
m_fileChunkSending(Event::kUnknown),
@ -657,14 +646,6 @@ public:
*/
Event::Type shapeChanged();
//! Get clipboard grabbed event type
/*!
Returns the clipboard grabbed event type. This is sent whenever the
clipboard is grabbed by some other application so we don't own it
anymore. The data is a pointer to a ClipboardInfo.
*/
Event::Type clipboardGrabbed();
//! Get suspend event type
/*!
Returns the suspend event type. This is sent whenever the system goes
@ -690,9 +671,49 @@ public:
private:
Event::Type m_error;
Event::Type m_shapeChanged;
Event::Type m_clipboardGrabbed;
Event::Type m_suspend;
Event::Type m_resume;
Event::Type m_fileChunkSending;
Event::Type m_fileRecieveCompleted;
};
class ClipboardEvents : public EventTypes {
public:
ClipboardEvents() :
m_clipboardGrabbed(Event::kUnknown),
m_clipboardChanged(Event::kUnknown),
m_clipboardSending(Event::kUnknown) { }
//! @name accessors
//@{
//! Get clipboard grabbed event type
/*!
Returns the clipboard grabbed event type. This is sent whenever the
clipboard is grabbed by some other application so we don't own it
anymore. The data is a pointer to a ClipboardInfo.
*/
Event::Type clipboardGrabbed();
//! Get clipboard changed event type
/*!
Returns the clipboard changed event type. This is sent whenever the
contents of the clipboard has changed. The data is a pointer to a
IScreen::ClipboardInfo.
*/
Event::Type clipboardChanged();
//! Clipboard sending event type
/*!
Returns the clipboard sending event type. This is used to send
clipboard chunks.
*/
Event::Type clipboardSending();
//@}
private:
Event::Type m_clipboardGrabbed;
Event::Type m_clipboardChanged;
Event::Type m_clipboardSending;
};

View File

@ -48,6 +48,7 @@ class ServerAppEvents;
class IKeyStateEvents;
class IPrimaryScreenEvents;
class IScreenEvents;
class ClipboardEvents;
//! Event queue interface
/*!
@ -244,4 +245,5 @@ public:
virtual IKeyStateEvents& forIKeyState() = 0;
virtual IPrimaryScreenEvents& forIPrimaryScreen() = 0;
virtual IScreenEvents& forIScreen() = 0;
virtual ClipboardEvents& forClipboard() = 0;
};

View File

@ -207,6 +207,23 @@ removeChar(String& subject, const char c)
subject.erase(std::remove(subject.begin(), subject.end(), c), subject.end());
}
String
sizeTypeToString(size_t n)
{
std::stringstream ss;
ss << n;
return ss.str();
}
size_t
stringToSizeType(String string)
{
std::istringstream iss(string);
size_t value;
iss >> value;
return value;
}
//
// CaselessCmp
//

View File

@ -84,10 +84,21 @@ void uppercase(String& subject);
//! Remove all specific char in suject
/*!
Remove all specific \c char in \c suject
Remove all specific \c c in \c suject
*/
void removeChar(String& subject, const char c);
//! Convert a size type to a string
/*!
Convert an size type to a string
*/
String sizeTypeToString(size_t n);
//! Convert a string to a size type
/*!
Convert an a \c string to an size type
*/
size_t stringToSizeType(String string);
//! Case-insensitive comparisons
/*!

View File

@ -22,12 +22,13 @@
#include "client/ServerProxy.h"
#include "synergy/Screen.h"
#include "synergy/Clipboard.h"
#include "synergy/FileChunk.h"
#include "synergy/DropHelper.h"
#include "synergy/PacketStreamFilter.h"
#include "synergy/ProtocolUtil.h"
#include "synergy/protocol_types.h"
#include "synergy/XSynergy.h"
#include "synergy/FileChunker.h"
#include "synergy/StreamChunker.h"
#include "synergy/IPlatformScreen.h"
#include "mt/Thread.h"
#include "net/TCPSocket.h"
@ -78,7 +79,8 @@ Client::Client(
m_writeToDropDirThread(NULL),
m_socket(NULL),
m_useSecureNetwork(false),
m_args(args)
m_args(args),
m_sendClipboardThread(NULL)
{
assert(m_socketFactory != NULL);
assert(m_screen != NULL);
@ -264,13 +266,16 @@ Client::leave()
m_active = false;
// send clipboards that we own and that have changed
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
if (m_ownClipboard[id]) {
sendClipboard(id);
}
if (m_sendClipboardThread != NULL) {
StreamChunker::interruptClipboard();
}
m_sendClipboardThread = new Thread(
new TMethodJob<Client>(
this,
&Client::sendClipboardThread,
NULL));
return true;
}
@ -422,12 +427,12 @@ Client::sendConnectionFailedEvent(const char* msg)
void
Client::sendFileChunk(const void* data)
{
FileChunker::FileChunk* fileChunk = reinterpret_cast<FileChunker::FileChunk*>(const_cast<void*>(data));
LOG((CLOG_DEBUG1 "sendFileChunk"));
FileChunk* chunk = reinterpret_cast<FileChunk*>(const_cast<void*>(data));
LOG((CLOG_DEBUG1 "send file chunk"));
assert(m_server != NULL);
// relay
m_server->fileChunkSending(fileChunk->m_chunk[0], &(fileChunk->m_chunk[1]), fileChunk->m_dataSize);
m_server->fileChunkSending(chunk->m_chunk[0], &chunk->m_chunk[1], chunk->m_dataSize);
}
void
@ -487,7 +492,7 @@ Client::setupScreen()
getEventTarget(),
new TMethodEventJob<Client>(this,
&Client::handleShapeChanged));
m_events->adoptHandler(m_events->forIScreen().clipboardGrabbed(),
m_events->adoptHandler(m_events->forClipboard().clipboardGrabbed(),
getEventTarget(),
new TMethodEventJob<Client>(this,
&Client::handleClipboardGrabbed));
@ -545,7 +550,7 @@ Client::cleanupScreen()
}
m_events->removeHandler(m_events->forIScreen().shapeChanged(),
getEventTarget());
m_events->removeHandler(m_events->forIScreen().clipboardGrabbed(),
m_events->removeHandler(m_events->forClipboard().clipboardGrabbed(),
getEventTarget());
delete m_server;
m_server = NULL;
@ -749,6 +754,17 @@ Client::onFileRecieveCompleted()
}
}
void
Client::sendClipboardThread(void*)
{
// send clipboards that we own and that have changed
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
if (m_ownClipboard[id]) {
sendClipboard(id);
}
}
}
void
Client::handleStopRetry(const Event&, void*)
{
@ -768,25 +784,6 @@ Client::writeToDropDirThread(void*)
m_receivedFileData);
}
void
Client::clearReceivedFileData()
{
m_receivedFileData.clear();
}
void
Client::setExpectedFileSize(String data)
{
std::istringstream iss(data);
iss >> m_expectedFileSize;
}
void
Client::fileChunkReceived(String data)
{
m_receivedFileData += data;
}
void
Client::dragInfoReceived(UInt32 fileNum, String data)
{
@ -810,6 +807,10 @@ Client::isReceivedFileSizeValid()
void
Client::sendFileToServer(const char* filename)
{
if (m_sendFileThread != NULL) {
StreamChunker::interruptFile();
}
m_sendFileThread = new Thread(
new TMethodJob<Client>(
this, &Client::sendFileThread,
@ -821,7 +822,7 @@ Client::sendFileThread(void* filename)
{
try {
char* name = reinterpret_cast<char*>(filename);
FileChunker::sendFileChunks(name, m_events, this);
StreamChunker::sendFile(name, m_events, this);
}
catch (std::runtime_error error) {
LOG((CLOG_ERR "failed sending file chunks: %s", error.what()));

View File

@ -85,15 +85,6 @@ public:
*/
virtual void handshakeComplete();
//! Clears the file buffer
void clearReceivedFileData();
//! Set the expected size of receiving file
void setExpectedFileSize(String data);
//! Received a chunk of file data
void fileChunkReceived(String data);
//! Received drag information
void dragInfoReceived(UInt32 fileNum, String data);
@ -131,7 +122,10 @@ public:
bool isReceivedFileSizeValid();
//! Return expected file size
size_t getExpectedFileSize() { return m_expectedFileSize; }
size_t& getExpectedFileSize() { return m_expectedFileSize; }
//! Return received file data
String& getReceivedFileData() { return m_receivedFileData; }
//@}
@ -194,6 +188,7 @@ private:
void handleFileRecieveCompleted(const Event&, void*);
void handleStopRetry(const Event&, void*);
void onFileRecieveCompleted();
void sendClipboardThread(void*);
public:
bool m_mock;
@ -224,4 +219,5 @@ private:
TCPSocket* m_socket;
bool m_useSecureNetwork;
ClientArgs& m_args;
Thread* m_sendClipboardThread;
};

View File

@ -19,6 +19,9 @@
#include "client/ServerProxy.h"
#include "client/Client.h"
#include "synergy/FileChunk.h"
#include "synergy/ClipboardChunk.h"
#include "synergy/StreamChunker.h"
#include "synergy/Clipboard.h"
#include "synergy/ProtocolUtil.h"
#include "synergy/option_types.h"
@ -35,8 +38,6 @@
// ServerProxy
//
const UInt16 ServerProxy::m_intervalThreshold = 1;
ServerProxy::ServerProxy(Client* client, synergy::IStream* stream, IEventQueue* events) :
m_client(client),
m_stream(stream),
@ -51,10 +52,7 @@ ServerProxy::ServerProxy(Client* client, synergy::IStream* stream, IEventQueue*
m_keepAliveAlarm(0.0),
m_keepAliveAlarmTimer(NULL),
m_parser(&ServerProxy::parseHandshakeMessage),
m_events(events),
m_stopwatch(true),
m_elapsedTime(0),
m_receivedDataSize(0)
m_events(events)
{
assert(m_client != NULL);
assert(m_stream != NULL);
@ -69,6 +67,11 @@ ServerProxy::ServerProxy(Client* client, synergy::IStream* stream, IEventQueue*
new TMethodEventJob<ServerProxy>(this,
&ServerProxy::handleData));
m_events->adoptHandler(m_events->forClipboard().clipboardSending(),
this,
new TMethodEventJob<ServerProxy>(this,
&ServerProxy::handleClipboardSendingEvent));
// send heartbeat
setKeepAliveRate(kKeepAliveRate);
}
@ -358,8 +361,11 @@ void
ServerProxy::onClipboardChanged(ClipboardID id, const IClipboard* clipboard)
{
String data = IClipboard::marshall(clipboard);
LOG((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size()));
ProtocolUtil::writef(m_stream, kMsgDClipboard, id, m_seqNum, &data);
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
@ -545,21 +551,20 @@ void
ServerProxy::setClipboard()
{
// parse
static String dataCached;
ClipboardID id;
UInt32 seqNum;
String data;
ProtocolUtil::readf(m_stream, kMsgDClipboard + 4, &id, &seqNum, &data);
LOG((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size()));
UInt32 seq;
// validate
if (id >= kClipboardEnd) {
return;
}
int r = ClipboardChunk::assemble(m_stream, dataCached, id, seq);
if (r == kFinish) {
LOG((CLOG_DEBUG "received clipboard %d size=%d", id, dataCached.size()));
// forward
Clipboard clipboard;
clipboard.unmarshall(data, 0);
clipboard.unmarshall(dataCached, 0);
m_client->setClipboard(id, &clipboard);
}
}
void
@ -851,50 +856,13 @@ ServerProxy::infoAcknowledgment()
void
ServerProxy::fileChunkReceived()
{
// parse
UInt8 mark = 0;
String content;
ProtocolUtil::readf(m_stream, kMsgDFileTransfer + 4, &mark, &content);
int result = FileChunk::assemble(
m_stream,
m_client->getReceivedFileData(),
m_client->getExpectedFileSize());
switch (mark) {
case kFileStart:
m_client->clearReceivedFileData();
m_client->setExpectedFileSize(content);
if (CLOG->getFilter() >= kDEBUG2) {
LOG((CLOG_DEBUG2 "recv file data from server: size=%s", content.c_str()));
m_stopwatch.start();
}
break;
case kFileChunk:
m_client->fileChunkReceived(content);
if (CLOG->getFilter() >= kDEBUG2) {
LOG((CLOG_DEBUG2 "recv file data from server: size=%i", content.size()));
double interval = m_stopwatch.getTime();
LOG((CLOG_DEBUG2 "recv file data from server: interval=%f s", interval));
m_receivedDataSize += content.size();
if (interval >= m_intervalThreshold) {
double averageSpeed = m_receivedDataSize / interval / 1000;
LOG((CLOG_DEBUG2 "recv file data from server: average speed=%f kb/s", averageSpeed));
m_receivedDataSize = 0;
m_elapsedTime += interval;
m_stopwatch.reset();
}
}
break;
case kFileEnd:
if (result == kFinish) {
m_events->addEvent(Event(m_events->forIScreen().fileRecieveCompleted(), m_client));
if (CLOG->getFilter() >= kDEBUG2) {
LOG((CLOG_DEBUG2 "file data transfer finished"));
m_elapsedTime += m_stopwatch.getTime();
double averageSpeed = m_client->getExpectedFileSize() / m_elapsedTime / 1000;
LOG((CLOG_DEBUG2 "file data transfer finished: total time consumed=%f s", m_elapsedTime));
LOG((CLOG_DEBUG2 "file data transfer finished: total data received=%i kb", m_client->getExpectedFileSize() / 1000));
LOG((CLOG_DEBUG2 "file data transfer finished: total average speed=%f kb/s", averageSpeed));
}
break;
}
}
@ -909,26 +877,16 @@ ServerProxy::dragInfoReceived()
m_client->dragInfoReceived(fileNum, content);
}
void
ServerProxy::handleClipboardSendingEvent(const Event& event, void*)
{
ClipboardChunk::send(m_stream, event.getData());
}
void
ServerProxy::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
{
String chunk(data, dataSize);
switch (mark) {
case kFileStart:
LOG((CLOG_DEBUG2 "file sending start: size=%s", data));
break;
case kFileChunk:
LOG((CLOG_DEBUG2 "file chunk sending: size=%i", chunk.size()));
break;
case kFileEnd:
LOG((CLOG_DEBUG2 "file sending finished"));
break;
}
ProtocolUtil::writef(m_stream, kMsgDFileTransfer, mark, &chunk);
FileChunk::send(m_stream, mark, data, dataSize);
}
void

View File

@ -106,6 +106,7 @@ private:
void infoAcknowledgment();
void fileChunkReceived();
void dragInfoReceived();
void handleClipboardSendingEvent(const Event&, void*);
private:
typedef EResult (ServerProxy::*MessageParser)(const UInt8*);
@ -129,9 +130,4 @@ private:
MessageParser m_parser;
IEventQueue* m_events;
Stopwatch m_stopwatch;
double m_elapsedTime;
size_t m_receivedDataSize;
static const UInt16 m_intervalThreshold;
};

View File

@ -433,8 +433,8 @@ MSWindowsScreen::checkClipboards()
if (m_ownClipboard && !MSWindowsClipboard::isOwnedBySynergy()) {
LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received"));
m_ownClipboard = false;
sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardClipboard);
sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardSelection);
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard);
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection);
}
}
@ -1501,8 +1501,8 @@ MSWindowsScreen::onClipboardChange()
if (m_ownClipboard) {
LOG((CLOG_DEBUG "clipboard changed: lost ownership"));
m_ownClipboard = false;
sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardClipboard);
sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardSelection);
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard);
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection);
}
}
else if (!m_ownClipboard) {

View File

@ -954,8 +954,8 @@ OSXScreen::checkClipboards()
LOG((CLOG_DEBUG2 "checking clipboard"));
if (m_pasteboard.synchronize()) {
LOG((CLOG_DEBUG "clipboard changed"));
sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardClipboard);
sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardSelection);
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard);
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection);
}
}

View File

@ -1325,7 +1325,7 @@ XWindowsScreen::handleSystemEvent(const Event& event, void*)
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->forIScreen().clipboardGrabbed(), id);
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), id);
return;
}
}

View File

@ -19,6 +19,7 @@ if (WIN32)
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()

View File

@ -85,18 +85,20 @@ if (WIN32)
..\\..\\..\\..\\..\\ext\\${OPENSSL_PLAT_DIR}\\out32dll\\ssleay32.*
..\\..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR}
)
else()
endif()
if (UNIX)
if (APPLE)
add_custom_command(
TARGET ns
POST_BUILD
COMMAND
mkdir -p
${CMAKE_SOURCE_DIR}/bin/plugins
${CMAKE_SOURCE_DIR}/bin/${CMAKE_CFG_INTDIR}/plugins
&&
cp
${CMAKE_SOURCE_DIR}/lib/${CMAKE_CFG_INTDIR}/libns.*
${CMAKE_SOURCE_DIR}/bin/plugins/
${CMAKE_SOURCE_DIR}/bin/${CMAKE_CFG_INTDIR}/plugins/
)
else()
if (CMAKE_BUILD_TYPE STREQUAL Debug)
@ -104,11 +106,11 @@ else()
TARGET ns
POST_BUILD
COMMAND mkdir -p
${CMAKE_SOURCE_DIR}/bin/plugins
${CMAKE_SOURCE_DIR}/bin/debug/plugins
&&
cp
${CMAKE_SOURCE_DIR}/lib/debug/libns.*
${CMAKE_SOURCE_DIR}/bin/plugins/
${CMAKE_SOURCE_DIR}/bin/debug/plugins/
)
else()
add_custom_command(

View File

@ -35,7 +35,12 @@
//
#define MAX_ERROR_SIZE 65535
#define MAX_RETRY_COUNT 60
enum {
// this limit seems extremely high, but mac client seem to generate around
// 50,000 errors before they establish a connection (wtf?)
kMaxRetryCount = 100000
};
static const char kFingerprintDirName[] = "SSL/Fingerprints";
//static const char kFingerprintLocalFilename[] = "Local.txt";
@ -52,8 +57,8 @@ SecureSocket::SecureSocket(
SocketMultiplexer* socketMultiplexer) :
TCPSocket(events, socketMultiplexer),
m_secureReady(false),
m_maxRetry(MAX_RETRY_COUNT),
m_fatal(false)
m_fatal(false),
m_maxRetry(kMaxRetryCount)
{
}
@ -63,8 +68,8 @@ SecureSocket::SecureSocket(
ArchSocket socket) :
TCPSocket(events, socketMultiplexer, socket),
m_secureReady(false),
m_maxRetry(MAX_RETRY_COUNT),
m_fatal(false)
m_fatal(false),
m_maxRetry(kMaxRetryCount)
{
}
@ -392,31 +397,25 @@ SecureSocket::checkResult(int status, int& retry)
case SSL_ERROR_ZERO_RETURN:
// connection closed
isFatal(true);
LOG((CLOG_DEBUG "SSL connection has been closed"));
LOG((CLOG_DEBUG "ssl connection closed"));
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_CONNECT:
case SSL_ERROR_WANT_ACCEPT:
retry += 1;
// If there are a lot of retrys, it's worth warning about
if ( retry % 5 == 0 ) {
LOG((CLOG_DEBUG "need to retry the same SSL function(%d) retry:%d", errorCode, retry));
}
else if ( retry == (maxRetry() / 2) ) {
LOG((CLOG_WARN "need to retry the same SSL function(%d) retry:%d", errorCode, retry));
}
else {
LOG((CLOG_DEBUG2 "need to retry the same SSL function(%d) retry:%d", errorCode, retry));
}
// it seems like these sort of errors are part of openssl's normal behavior,
// so we should expect a very high amount of these. sleeping doesn't seem to
// help... maybe you just have to swallow the errors (yuck).
retry++;
LOG((CLOG_DEBUG2 "passive ssl error, error=%d, attempt=%d", errorCode, retry));
break;
case SSL_ERROR_SYSCALL:
LOG((CLOG_ERR "some secure socket I/O error occurred"));
LOG((CLOG_ERR "ssl error occurred (system call failure)"));
if (ERR_peek_error() == 0) {
if (status == 0) {
LOG((CLOG_ERR "an EOF violates the protocol"));
LOG((CLOG_ERR "eof violates ssl protocol"));
}
else if (status == -1) {
// underlying socket I/O reproted an error
@ -433,19 +432,19 @@ SecureSocket::checkResult(int status, int& retry)
break;
case SSL_ERROR_SSL:
LOG((CLOG_ERR "a failure in the SSL library occurred"));
LOG((CLOG_ERR "ssl error occurred (generic failure)"));
isFatal(true);
break;
default:
LOG((CLOG_ERR "unknown secure socket error"));
LOG((CLOG_ERR "ssl error occurred (unknown failure)"));
isFatal(true);
break;
}
// If the retry max would exceed the allowed, treat it as a fatal error
if (retry > maxRetry()) {
LOG((CLOG_ERR "Maximum retry count exceeded:%d",retry));
LOG((CLOG_ERR "passive ssl error limit exceeded: %d", retry));
isFatal(true);
}

View File

@ -64,6 +64,9 @@ public:
//! Get server which owns this listener
Server* getServer() { return m_server; }
//! Return true if using secure network connection
bool isSecure() { return m_useSecureNetwork; }
//@}
private:

View File

@ -275,16 +275,7 @@ ClientProxy1_0::leave()
void
ClientProxy1_0::setClipboard(ClipboardID id, const IClipboard* clipboard)
{
// ignore if this clipboard is already clean
if (m_clipboard[id].m_dirty) {
// this clipboard is now clean
m_clipboard[id].m_dirty = false;
Clipboard::copy(&m_clipboard[id].m_clipboard, clipboard);
String data = m_clipboard[id].m_clipboard.marshall();
LOG((CLOG_DEBUG "send clipboard %d to \"%s\" size=%d", id, getName().c_str(), data.size()));
ProtocolUtil::writef(getStream(), kMsgDClipboard, id, 0, &data);
}
// ignore -- deprecated in protocol 1.0
}
void
@ -450,33 +441,8 @@ ClientProxy1_0::recvInfo()
bool
ClientProxy1_0::recvClipboard()
{
// parse message
ClipboardID id;
UInt32 seqNum;
String data;
if (!ProtocolUtil::readf(getStream(),
kMsgDClipboard + 4, &id, &seqNum, &data)) {
// deprecated in protocol 1.0
return false;
}
LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d", getName().c_str(), id, seqNum, data.size()));
// validate
if (id >= kClipboardEnd) {
return false;
}
// save clipboard
m_clipboard[id].m_clipboard.unmarshall(data, 0);
m_clipboard[id].m_sequenceNumber = seqNum;
// notify
ClipboardInfo* info = new ClipboardInfo;
info->m_id = id;
info->m_sequenceNumber = seqNum;
m_events->addEvent(Event(m_events->forClientProxy().clipboardChanged(),
getEventTarget(), info));
return true;
}
bool
@ -499,13 +465,12 @@ ClientProxy1_0::recvGrabClipboard()
ClipboardInfo* info = new ClipboardInfo;
info->m_id = id;
info->m_sequenceNumber = seqNum;
m_events->addEvent(Event(m_events->forIScreen().clipboardGrabbed(),
m_events->addEvent(Event(m_events->forClipboard().clipboardGrabbed(),
getEventTarget(), info));
return true;
}
//
// ClientProxy1_0::ClientClipboard
//

View File

@ -70,7 +70,7 @@ protected:
virtual void resetHeartbeatTimer();
virtual void addHeartbeatTimer();
virtual void removeHeartbeatTimer();
virtual bool recvClipboard();
private:
void disconnect();
void removeHandlers();
@ -81,11 +81,9 @@ private:
void handleFlatline(const Event&, void*);
bool recvInfo();
bool recvClipboard();
bool recvGrabClipboard();
private:
typedef bool (ClientProxy1_0::*MessageParser)(const UInt8*);
protected:
struct ClientClipboard {
public:
ClientClipboard();
@ -96,8 +94,12 @@ private:
bool m_dirty;
};
ClientInfo m_info;
ClientClipboard m_clipboard[kClipboardEnd];
private:
typedef bool (ClientProxy1_0::*MessageParser)(const UInt8*);
ClientInfo m_info;
double m_heartbeatAlarm;
EventQueueTimer* m_heartbeatTimer;
MessageParser m_parser;

View File

@ -18,22 +18,21 @@
#include "server/ClientProxy1_5.h"
#include "server/Server.h"
#include "synergy/FileChunk.h"
#include "synergy/StreamChunker.h"
#include "synergy/ProtocolUtil.h"
#include "io/IStream.h"
#include "base/Log.h"
#include <sstream>
//
// ClientProxy1_5
//
const UInt16 ClientProxy1_5::m_intervalThreshold = 1;
ClientProxy1_5::ClientProxy1_5(const String& name, synergy::IStream* stream, Server* server, IEventQueue* events) :
ClientProxy1_4(name, stream, server, events),
m_events(events),
m_stopwatch(true),
m_elapsedTime(0),
m_receivedDataSize(0)
m_events(events)
{
}
@ -52,23 +51,7 @@ ClientProxy1_5::sendDragInfo(UInt32 fileCount, const char* info, size_t size)
void
ClientProxy1_5::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
{
String chunk(data, dataSize);
switch (mark) {
case kFileStart:
LOG((CLOG_DEBUG2 "file sending start: size=%s", data));
break;
case kFileChunk:
LOG((CLOG_DEBUG2 "file chunk sending: size=%i", chunk.size()));
break;
case kFileEnd:
LOG((CLOG_DEBUG2 "file sending finished"));
break;
}
ProtocolUtil::writef(getStream(), kMsgDFileTransfer, mark, &chunk);
FileChunk::send(getStream(), mark, data, dataSize);
}
bool
@ -90,51 +73,15 @@ ClientProxy1_5::parseMessage(const UInt8* code)
void
ClientProxy1_5::fileChunkReceived()
{
// parse
UInt8 mark = 0;
String content;
ProtocolUtil::readf(getStream(), kMsgDFileTransfer + 4, &mark, &content);
Server* server = getServer();
switch (mark) {
case kFileStart:
server->clearReceivedFileData();
server->setExpectedFileSize(content);
if (CLOG->getFilter() >= kDEBUG2) {
LOG((CLOG_DEBUG2 "recv file data from client: file size=%s", content.c_str()));
m_stopwatch.start();
}
break;
int result = FileChunk::assemble(
getStream(),
server->getReceivedFileData(),
server->getExpectedFileSize());
case kFileChunk:
server->fileChunkReceived(content);
if (CLOG->getFilter() >= kDEBUG2) {
LOG((CLOG_DEBUG2 "recv file data from client: chunck size=%i", content.size()));
double interval = m_stopwatch.getTime();
m_receivedDataSize += content.size();
LOG((CLOG_DEBUG2 "recv file data from client: interval=%f s", interval));
if (interval >= m_intervalThreshold) {
double averageSpeed = m_receivedDataSize / interval / 1000;
LOG((CLOG_DEBUG2 "recv file data from client: average speed=%f kb/s", averageSpeed));
m_receivedDataSize = 0;
m_elapsedTime += interval;
m_stopwatch.reset();
}
}
break;
case kFileEnd:
if (result == kFinish) {
m_events->addEvent(Event(m_events->forIScreen().fileRecieveCompleted(), server));
if (CLOG->getFilter() >= kDEBUG2) {
LOG((CLOG_DEBUG2 "file data transfer finished"));
m_elapsedTime += m_stopwatch.getTime();
double averageSpeed = getServer()->getExpectedFileSize() / m_elapsedTime / 1000;
LOG((CLOG_DEBUG2 "file data transfer finished: total time consumed=%f s", m_elapsedTime));
LOG((CLOG_DEBUG2 "file data transfer finished: total data received=%i kb", getServer()->getExpectedFileSize() / 1000));
LOG((CLOG_DEBUG2 "file data transfer finished: total average speed=%f kb/s", averageSpeed));
}
break;
}
}

View File

@ -19,6 +19,7 @@
#include "server/ClientProxy1_4.h"
#include "base/Stopwatch.h"
#include "common/stdvector.h"
class Server;
class IEventQueue;
@ -37,9 +38,4 @@ public:
private:
IEventQueue* m_events;
Stopwatch m_stopwatch;
double m_elapsedTime;
size_t m_receivedDataSize;
static const UInt16 m_intervalThreshold;
};

View File

@ -0,0 +1,110 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2015 Synergy Si Inc.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "server/ClientProxy1_6.h"
#include "server/Server.h"
#include "synergy/ProtocolUtil.h"
#include "synergy/StreamChunker.h"
#include "synergy/ClipboardChunk.h"
#include "io/IStream.h"
#include "base/TMethodEventJob.h"
#include "base/Log.h"
//
// ClientProxy1_6
//
enum
{
kSslClipboardMaxSize = 1024
};
ClientProxy1_6::ClientProxy1_6(const String& name, synergy::IStream* stream, Server* server, IEventQueue* events) :
ClientProxy1_5(name, stream, server, events),
m_events(events)
{
m_events->adoptHandler(m_events->forClipboard().clipboardSending(),
this,
new TMethodEventJob<ClientProxy1_6>(this,
&ClientProxy1_6::handleClipboardSendingEvent));
}
ClientProxy1_6::~ClientProxy1_6()
{
}
void
ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard)
{
// ignore if this clipboard is already clean
if (m_clipboard[id].m_dirty) {
// this clipboard is now clean
m_clipboard[id].m_dirty = false;
Clipboard::copy(&m_clipboard[id].m_clipboard, clipboard);
String data = m_clipboard[id].m_clipboard.marshall();
size_t size = data.size();
LOG((CLOG_DEBUG "sending clipboard %d to \"%s\"", id, getName().c_str()));
// HACK: if using SSL, don't send large clipboards (#4601)
bool send = true;
if (getServer()->isSecure() && (size > kSslClipboardMaxSize)) {
send = false;
LOG((CLOG_WARN "large clipboards not supported with ssl, size=%d", size));
}
if (send) {
StreamChunker::sendClipboard(data, size, id, 0, m_events, this);
LOG((CLOG_DEBUG "sent clipboard size=%d", size));
}
}
}
void
ClientProxy1_6::handleClipboardSendingEvent(const Event& event, void*)
{
ClipboardChunk::send(getStream(), event.getData());
}
bool
ClientProxy1_6::recvClipboard()
{
// parse message
static String dataCached;
ClipboardID id;
UInt32 seq;
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()));
// save clipboard
m_clipboard[id].m_clipboard.unmarshall(dataCached, 0);
m_clipboard[id].m_sequenceNumber = seq;
// notify
ClipboardInfo* info = new ClipboardInfo;
info->m_id = id;
info->m_sequenceNumber = seq;
m_events->addEvent(Event(m_events->forClipboard().clipboardChanged(),
getEventTarget(), info));
}
return true;
}

View File

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 Synergy Si Ltd.
* Copyright (C) 2015 Synergy Si Inc.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -17,30 +17,23 @@
#pragma once
#include "base/String.h"
#include "server/ClientProxy1_5.h"
class Server;
class IEventQueue;
class FileChunker {
//! Proxy for client implementing protocol version 1.6
class ClientProxy1_6 : public ClientProxy1_5 {
public:
//! FileChunk data
class FileChunk {
public:
FileChunk(size_t chunkSize) : m_dataSize(chunkSize - 2)
{
m_chunk = new char[chunkSize];
}
ClientProxy1_6(const String& name, synergy::IStream* adoptedStream, Server* server, IEventQueue* events);
~ClientProxy1_6();
~FileChunk() { delete[] m_chunk; }
public:
const size_t m_dataSize;
char* m_chunk;
};
static void sendFileChunks(char* filename, IEventQueue* events, void* eventTarget);
static String intToString(size_t i);
virtual void setClipboard(ClipboardID id, const IClipboard* clipboard);
virtual bool recvClipboard();
private:
static const size_t m_chunkSize;
void handleClipboardSendingEvent(const Event&, void*);
private:
IEventQueue* m_events;
};

View File

@ -25,6 +25,7 @@
#include "server/ClientProxy1_3.h"
#include "server/ClientProxy1_4.h"
#include "server/ClientProxy1_5.h"
#include "server/ClientProxy1_6.h"
#include "synergy/protocol_types.h"
#include "synergy/ProtocolUtil.h"
#include "synergy/XSynergy.h"
@ -227,6 +228,10 @@ ClientProxyUnknown::handleData(const Event&, void*)
case 5:
m_proxy = new ClientProxy1_5(name, m_stream, m_server, m_events);
break;
case 6:
m_proxy = new ClientProxy1_6(name, m_stream, m_server, m_events);
break;
}
}

View File

@ -22,13 +22,14 @@
#include "server/ClientProxyUnknown.h"
#include "server/PrimaryClient.h"
#include "server/ClientListener.h"
#include "synergy/FileChunk.h"
#include "synergy/IPlatformScreen.h"
#include "synergy/DropHelper.h"
#include "synergy/option_types.h"
#include "synergy/protocol_types.h"
#include "synergy/XScreen.h"
#include "synergy/XSynergy.h"
#include "synergy/FileChunker.h"
#include "synergy/StreamChunker.h"
#include "synergy/KeyState.h"
#include "synergy/Screen.h"
#include "synergy/PacketStreamFilter.h"
@ -91,7 +92,8 @@ Server::Server(
m_ignoreFileTransfer(false),
m_enableDragDrop(enableDragDrop),
m_getDragInfoThread(NULL),
m_waitDragInfoThread(true)
m_waitDragInfoThread(true),
m_sendClipboardThread(NULL)
{
// must have a primary client and it must have a canonical name
assert(m_primaryClient != NULL);
@ -503,11 +505,18 @@ 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::interruptClipboard();
}
// send the clipboard data to new active screen
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
m_active->setClipboard(id, &m_clipboards[id].m_clipboard);
}
m_sendClipboardThread = new Thread(
new TMethodJob<Server>(
this,
&Server::sendClipboardThread,
NULL));
Server::SwitchToScreenInfo* info =
Server::SwitchToScreenInfo::alloc(m_active->getName());
@ -1849,6 +1858,14 @@ Server::sendDragInfo(BaseClientProxy* newScreen)
}
}
void
Server::sendClipboardThread(void*)
{
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
m_active->setClipboard(id, &m_clipboards[id].m_clipboard);
}
}
void
Server::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
{
@ -2023,13 +2040,13 @@ Server::onMouseWheel(SInt32 xDelta, SInt32 yDelta)
void
Server::onFileChunkSending(const void* data)
{
FileChunker::FileChunk* fileChunk = reinterpret_cast<FileChunker::FileChunk*>(const_cast<void*>(data));
FileChunk* chunk = reinterpret_cast<FileChunk*>(const_cast<void*>(data));
LOG((CLOG_DEBUG1 "onFileChunkSending"));
LOG((CLOG_DEBUG1 "sending file chunk"));
assert(m_active != NULL);
// relay
m_active->fileChunkSending(fileChunk->m_chunk[0], &(fileChunk->m_chunk[1]), fileChunk->m_dataSize);
m_active->fileChunkSending(chunk->m_chunk[0], &chunk->m_chunk[1], chunk->m_dataSize);
}
void
@ -2068,11 +2085,11 @@ Server::addClient(BaseClientProxy* client)
client->getEventTarget(),
new TMethodEventJob<Server>(this,
&Server::handleShapeChanged, client));
m_events->adoptHandler(m_events->forIScreen().clipboardGrabbed(),
m_events->adoptHandler(m_events->forClipboard().clipboardGrabbed(),
client->getEventTarget(),
new TMethodEventJob<Server>(this,
&Server::handleClipboardGrabbed, client));
m_events->adoptHandler(m_events->forClientProxy().clipboardChanged(),
m_events->adoptHandler(m_events->forClipboard().clipboardChanged(),
client->getEventTarget(),
new TMethodEventJob<Server>(this,
&Server::handleClipboardChanged, client));
@ -2104,9 +2121,9 @@ Server::removeClient(BaseClientProxy* client)
// remove event handlers
m_events->removeHandler(m_events->forIScreen().shapeChanged(),
client->getEventTarget());
m_events->removeHandler(m_events->forIScreen().clipboardGrabbed(),
m_events->removeHandler(m_events->forClipboard().clipboardGrabbed(),
client->getEventTarget());
m_events->removeHandler(m_events->forClientProxy().clipboardChanged(),
m_events->removeHandler(m_events->forClipboard().clipboardChanged(),
client->getEventTarget());
// remove from list
@ -2326,25 +2343,6 @@ Server::KeyboardBroadcastInfo::alloc(State state, const String& screens)
return info;
}
void
Server::clearReceivedFileData()
{
m_receivedFileData.clear();
}
void
Server::setExpectedFileSize(String data)
{
std::istringstream iss(data);
iss >> m_expectedFileSize;
}
void
Server::fileChunkReceived(String data)
{
m_receivedFileData += data;
}
bool
Server::isReceivedFileSizeValid()
{
@ -2354,6 +2352,10 @@ Server::isReceivedFileSizeValid()
void
Server::sendFileToClient(const char* filename)
{
if (m_sendFileThread != NULL) {
StreamChunker::interruptFile();
}
m_sendFileThread = new Thread(
new TMethodJob<Server>(
this, &Server::sendFileThread,
@ -2366,7 +2368,7 @@ Server::sendFileThread(void* data)
try {
char* filename = reinterpret_cast<char*>(data);
LOG((CLOG_DEBUG "sending file to client, filename=%s", filename));
FileChunker::sendFileChunks(filename, m_events, this);
StreamChunker::sendFile(filename, m_events, this);
}
catch (std::runtime_error error) {
LOG((CLOG_ERR "failed sending file chunks, error: %s", error.what()));
@ -2387,3 +2389,9 @@ Server::dragInfoReceived(UInt32 fileNum, String content)
m_screen->startDraggingFiles(m_dragFileList);
}
bool
Server::isSecure() const
{
return m_clientListener->isSecure();
}

View File

@ -141,15 +141,6 @@ public:
*/
void disconnect();
//! Clears the file buffer
void clearReceivedFileData();
//! Set the expected size of receiving file
void setExpectedFileSize(String data);
//! Received a chunk of file data
void fileChunkReceived(String data);
//! Create a new thread and use it to send file to client
void sendFileToClient(const char* filename);
@ -178,8 +169,14 @@ public:
//! Return true if recieved file size is valid
bool isReceivedFileSizeValid();
//! Return expected file size
size_t getExpectedFileSize() { return m_expectedFileSize; }
//! Return expected file data size
size_t& getExpectedFileSize() { return m_expectedFileSize; }
//! Return received file data
String& getReceivedFileData() { return m_receivedFileData; }
//! Return true if using secure network connection
bool isSecure() const;
//@}
@ -370,6 +367,9 @@ private:
// send drag info to new client screen
void sendDragInfo(BaseClientProxy* newScreen);
// thread funciton for sending clipboard
void sendClipboardThread(void*);
public:
bool m_mock;
@ -480,4 +480,6 @@ private:
bool m_waitDragInfoThread;
ClientListener* m_clientListener;
Thread* m_sendClipboardThread;
};

View File

@ -17,6 +17,7 @@
#include "synergy/ArgParser.h"
#include "synergy/StreamChunker.h"
#include "synergy/App.h"
#include "synergy/ServerArgs.h"
#include "synergy/ClientArgs.h"
@ -288,6 +289,7 @@ 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];

30
src/lib/synergy/Chunk.cpp Normal file
View File

@ -0,0 +1,30 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2015 Synergy Si Inc.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "synergy/Chunk.h"
#include "base/String.h"
Chunk::Chunk(size_t size)
{
m_chunk = new char[size];
memset(m_chunk, 0, size);
}
Chunk::~Chunk()
{
delete[] m_chunk;
}

30
src/lib/synergy/Chunk.h Normal file
View File

@ -0,0 +1,30 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2015 Synergy Si Inc.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/basic_types.h"
class Chunk {
public:
Chunk(size_t size);
~Chunk();
public:
size_t m_dataSize;
char* m_chunk;
};

View File

@ -0,0 +1,154 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2015 Synergy Si Inc.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "synergy/ClipboardChunk.h"
#include "synergy/ProtocolUtil.h"
#include "synergy/protocol_types.h"
#include "io/IStream.h"
#include "base/Log.h"
ClipboardChunk::ClipboardChunk(size_t size) :
Chunk(size)
{
m_dataSize = size - CLIPBOARD_CHUNK_META_SIZE;
}
ClipboardChunk*
ClipboardChunk::start(
ClipboardID id,
UInt32 sequence,
const String& size)
{
size_t sizeLength = size.size();
ClipboardChunk* start = new ClipboardChunk(sizeLength + CLIPBOARD_CHUNK_META_SIZE);
char* chunk = start->m_chunk;
chunk[0] = id;
UInt32* seq = reinterpret_cast<UInt32*>(&chunk[1]);
*seq = sequence;
chunk[5] = kDataStart;
memcpy(&chunk[6], size.c_str(), sizeLength);
chunk[sizeLength + CLIPBOARD_CHUNK_META_SIZE - 1] = '\0';
return start;
}
ClipboardChunk*
ClipboardChunk::data(
ClipboardID id,
UInt32 sequence,
const String& data)
{
size_t dataSize = data.size();
ClipboardChunk* chunk = new ClipboardChunk(dataSize + CLIPBOARD_CHUNK_META_SIZE);
char* chunkData = chunk->m_chunk;
chunkData[0] = id;
UInt32* seq = reinterpret_cast<UInt32*>(&chunkData[1]);
*seq = sequence;
chunkData[5] = kDataChunk;
memcpy(&chunkData[6], data.c_str(), dataSize);
chunkData[dataSize + CLIPBOARD_CHUNK_META_SIZE - 1] = '\0';
return chunk;
}
ClipboardChunk*
ClipboardChunk::end(ClipboardID id, UInt32 sequence)
{
ClipboardChunk* end = new ClipboardChunk(CLIPBOARD_CHUNK_META_SIZE);
char* chunk = end->m_chunk;
chunk[0] = id;
UInt32* seq = reinterpret_cast<UInt32*>(&chunk[1]);
*seq = sequence;
chunk[5] = kDataEnd;
chunk[CLIPBOARD_CHUNK_META_SIZE - 1] = '\0';
return end;
}
int
ClipboardChunk::assemble(synergy::IStream* stream,
String& dataCached,
ClipboardID& id,
UInt32& sequence)
{
static size_t expectedSize;
UInt8 mark;
String data;
if (!ProtocolUtil::readf(stream, kMsgDClipboard + 4, &id, &sequence, &mark, &data)) {
return kError;
}
if (mark == kDataStart) {
expectedSize = synergy::string::stringToSizeType(data);
LOG((CLOG_DEBUG "start receiving clipboard data"));
dataCached.clear();
return kNotFinish;
}
else if (mark == kDataChunk) {
dataCached.append(data);
return kNotFinish;
}
else if (mark == kDataEnd) {
// validate
if (id >= kClipboardEnd) {
return kError;
}
else if (expectedSize != dataCached.size()) {
LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", expectedSize, dataCached.size()));
return kError;
}
return kFinish;
}
return kError;
}
void
ClipboardChunk::send(synergy::IStream* stream, void* data)
{
ClipboardChunk* clipboardData = reinterpret_cast<ClipboardChunk*>(data);
LOG((CLOG_DEBUG1 "sending clipboard chunk"));
char* chunk = clipboardData->m_chunk;
ClipboardID id = chunk[0];
UInt32* seq = reinterpret_cast<UInt32*>(&chunk[1]);
UInt32 sequence = *seq;
UInt8 mark = chunk[5];
String dataChunk(&chunk[6], clipboardData->m_dataSize);
switch (mark) {
case kDataStart:
LOG((CLOG_DEBUG2 "sending clipboard chunk start: size=%s", dataChunk.c_str()));
break;
case kDataChunk:
LOG((CLOG_DEBUG2 "sending clipboard chunk data: size=%i", dataChunk.size()));
break;
case kDataEnd:
LOG((CLOG_DEBUG2 "sending clipboard finished"));
break;
}
ProtocolUtil::writef(stream, kMsgDClipboard, id, sequence, mark, &dataChunk);
}

View File

@ -0,0 +1,55 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2015 Synergy Si Inc.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "synergy/Chunk.h"
#include "synergy/clipboard_types.h"
#include "base/String.h"
#include "common/basic_types.h"
#define CLIPBOARD_CHUNK_META_SIZE 7
namespace synergy {
class IStream;
};
class ClipboardChunk : public Chunk {
public:
ClipboardChunk(size_t size);
static ClipboardChunk*
start(
ClipboardID id,
UInt32 sequence,
const String& size);
static ClipboardChunk*
data(
ClipboardID id,
UInt32 sequence,
const String& data);
static ClipboardChunk*
end(ClipboardID id, UInt32 sequence);
static int assemble(
synergy::IStream* stream,
String& dataCached,
ClipboardID& id,
UInt32& sequence);
static void send(synergy::IStream* stream, void* data);
};

View File

@ -0,0 +1,152 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2015 Synergy Si Inc.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "synergy/FileChunk.h"
#include "synergy/ProtocolUtil.h"
#include "synergy/protocol_types.h"
#include "io/IStream.h"
#include "base/Stopwatch.h"
#include "base/Log.h"
static const UInt16 kIntervalThreshold = 1;
FileChunk::FileChunk(size_t size) :
Chunk(size)
{
m_dataSize = size - FILE_CHUNK_META_SIZE;
}
FileChunk*
FileChunk::start(const String& size)
{
size_t sizeLength = size.size();
FileChunk* start = new FileChunk(sizeLength + FILE_CHUNK_META_SIZE);
char* chunk = start->m_chunk;
chunk[0] = kDataStart;
memcpy(&chunk[1], size.c_str(), sizeLength);
chunk[sizeLength + 1] = '\0';
return start;
}
FileChunk*
FileChunk::data(UInt8* data, size_t dataSize)
{
FileChunk* chunk = new FileChunk(dataSize + FILE_CHUNK_META_SIZE);
char* chunkData = chunk->m_chunk;
chunkData[0] = kDataChunk;
memcpy(&chunkData[1], data, dataSize);
chunkData[dataSize + 1] = '\0';
return chunk;
}
FileChunk*
FileChunk::end()
{
FileChunk* end = new FileChunk(FILE_CHUNK_META_SIZE);
char* chunk = end->m_chunk;
chunk[0] = kDataEnd;
chunk[1] = '\0';
return end;
}
int
FileChunk::assemble(synergy::IStream* stream, String& dataReceived, size_t& expectedSize)
{
// parse
UInt8 mark = 0;
String content;
static size_t receivedDataSize;
static double elapsedTime;
static Stopwatch stopwatch;
if (!ProtocolUtil::readf(stream, kMsgDFileTransfer + 4, &mark, &content)) {
return kError;
}
switch (mark) {
case kDataStart:
dataReceived.clear();
expectedSize = synergy::string::stringToSizeType(content);
receivedDataSize = 0;
elapsedTime = 0;
stopwatch.reset();
if (CLOG->getFilter() >= kDEBUG2) {
LOG((CLOG_DEBUG2 "recv file data from client: file size=%s", content.c_str()));
stopwatch.start();
}
return kNotFinish;
case kDataChunk:
dataReceived.append(content);
if (CLOG->getFilter() >= kDEBUG2) {
LOG((CLOG_DEBUG2 "recv file data from client: chunck size=%i", content.size()));
double interval = stopwatch.getTime();
receivedDataSize += content.size();
LOG((CLOG_DEBUG2 "recv file data from client: 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));
receivedDataSize = 0;
elapsedTime += interval;
stopwatch.reset();
}
}
return kNotFinish;
case kDataEnd:
//m_events->addEvent(Event(m_events->forIScreen().fileRecieveCompleted(), server));
if (CLOG->getFilter() >= kDEBUG2) {
LOG((CLOG_DEBUG2 "file data 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));
}
return kFinish;
}
return kError;
}
void
FileChunk::send(synergy::IStream* stream, UInt8 mark, char* data, size_t dataSize)
{
String chunk(data, dataSize);
switch (mark) {
case kDataStart:
LOG((CLOG_DEBUG2 "sending file chunk start: size=%s", data));
break;
case kDataChunk:
LOG((CLOG_DEBUG2 "sending file chunk: size=%i", chunk.size()));
break;
case kDataEnd:
LOG((CLOG_DEBUG2 "sending file finished"));
break;
}
ProtocolUtil::writef(stream, kMsgDFileTransfer, mark, &chunk);
}

View File

@ -0,0 +1,46 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2015 Synergy Si Inc.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "synergy/Chunk.h"
#include "base/String.h"
#include "common/basic_types.h"
#define FILE_CHUNK_META_SIZE 2
namespace synergy {
class IStream;
};
class FileChunk : public Chunk {
public:
FileChunk(size_t size);
static FileChunk* start(const String& size);
static FileChunk* data(UInt8* data, size_t dataSize);
static FileChunk* end();
static int assemble(
synergy::IStream* stream,
String& dataCached,
size_t& expectedSize);
static void send(
synergy::IStream* stream,
UInt8 mark,
char* data,
size_t dataSize);
};

View File

@ -1,112 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
#include "synergy/FileChunker.h"
#include "synergy/protocol_types.h"
#include "base/EventTypes.h"
#include "base/Event.h"
#include "base/IEventQueue.h"
#include "base/EventTypes.h"
#include "base/Log.h"
#include "base/Stopwatch.h"
#include "common/stdexcept.h"
#include <fstream>
#include <sstream>
#define PAUSE_TIME_HACK 0.1
using namespace std;
const size_t FileChunker::m_chunkSize = 512 * 1024; // 512kb
void
FileChunker::sendFileChunks(char* filename, IEventQueue* events, void* eventTarget)
{
std::fstream file(reinterpret_cast<char*>(filename), std::ios::in | std::ios::binary);
if (!file.is_open()) {
throw runtime_error("failed to open file");
}
// check file size
file.seekg (0, std::ios::end);
size_t size = (size_t)file.tellg();
// send first message (file size)
String fileSize = intToString(size);
size_t sizeLength = fileSize.size();
FileChunk* sizeMessage = new FileChunk(sizeLength + 2);
char* chunkData = sizeMessage->m_chunk;
chunkData[0] = kFileStart;
memcpy(&chunkData[1], fileSize.c_str(), sizeLength);
chunkData[sizeLength + 1] = '\0';
events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, sizeMessage));
// send chunk messages with a fixed chunk size
size_t sentLength = 0;
size_t chunkSize = m_chunkSize;
Stopwatch stopwatch;
stopwatch.start();
file.seekg (0, std::ios::beg);
while (true) {
if (stopwatch.getTime() > PAUSE_TIME_HACK) {
// make sure we don't read too much from the mock data.
if (sentLength + chunkSize > size) {
chunkSize = size - sentLength;
}
// for fileChunk->m_chunk, the first byte is the chunk mark, last is \0
FileChunk* fileChunk = new FileChunk(chunkSize + 2);
char* chunkData = fileChunk->m_chunk;
chunkData[0] = kFileChunk;
file.read(&chunkData[1], chunkSize);
chunkData[chunkSize + 1] = '\0';
events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, fileChunk));
sentLength += chunkSize;
file.seekg (sentLength, std::ios::beg);
if (sentLength == size) {
break;
}
stopwatch.reset();
}
}
// send last message
FileChunk* transferFinished = new FileChunk(2);
chunkData = transferFinished->m_chunk;
chunkData[0] = kFileEnd;
chunkData[1] = '\0';
events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, transferFinished));
file.close();
}
String
FileChunker::intToString(size_t i)
{
stringstream ss;
ss << i;
return ss.str();
}

View File

@ -0,0 +1,204 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
#include "synergy/StreamChunker.h"
#include "synergy/FileChunk.h"
#include "synergy/ClipboardChunk.h"
#include "synergy/protocol_types.h"
#include "base/EventTypes.h"
#include "base/Event.h"
#include "base/IEventQueue.h"
#include "base/EventTypes.h"
#include "base/Log.h"
#include "base/Stopwatch.h"
#include "base/String.h"
#include "common/stdexcept.h"
#include <fstream>
#define PAUSE_TIME_HACK 0.1
using namespace std;
#define SOCKET_CHUNK_SIZE 512 * 1024; // 512kb
#define SECURE_SOCKET_CHUNK_SIZE 2 * 1024; // 2kb
size_t StreamChunker::s_chunkSize = SOCKET_CHUNK_SIZE;
bool StreamChunker::s_isChunkingClipboard = false;
bool StreamChunker::s_interruptClipboard = false;
bool StreamChunker::s_isChunkingFile = false;
bool StreamChunker::s_interruptFile = false;
void
StreamChunker::sendFile(
char* filename,
IEventQueue* events,
void* eventTarget)
{
s_isChunkingFile = true;
std::fstream file(reinterpret_cast<char*>(filename), std::ios::in | std::ios::binary);
if (!file.is_open()) {
throw runtime_error("failed to open file");
}
// check file size
file.seekg (0, std::ios::end);
size_t size = (size_t)file.tellg();
// send first message (file size)
String fileSize = synergy::string::sizeTypeToString(size);
FileChunk* sizeMessage = FileChunk::start(fileSize);
events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, sizeMessage));
// send chunk messages with a fixed chunk size
size_t sentLength = 0;
size_t chunkSize = s_chunkSize;
Stopwatch stopwatch;
stopwatch.start();
file.seekg (0, std::ios::beg);
while (true) {
if (s_interruptFile) {
s_interruptFile = false;
break;
}
if (stopwatch.getTime() > PAUSE_TIME_HACK) {
// 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<UInt8*>(chunkData);
FileChunk* fileChunk = FileChunk::data(data, chunkSize);
delete[] chunkData;
events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, fileChunk));
sentLength += chunkSize;
file.seekg (sentLength, std::ios::beg);
if (sentLength == size) {
break;
}
stopwatch.reset();
}
}
// send last message
FileChunk* end = FileChunk::end();
events->addEvent(Event(events->forIScreen().fileChunkSending(), eventTarget, end));
file.close();
s_isChunkingFile = false;
}
void
StreamChunker::sendClipboard(
String& data,
size_t size,
ClipboardID id,
UInt32 sequence,
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);
events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, sizeMessage));
// send clipboard chunk with a fixed size
size_t sentLength = 0;
size_t chunkSize = s_chunkSize;
Stopwatch stopwatch;
stopwatch.start();
while (true) {
if (s_interruptClipboard) {
s_interruptClipboard = false;
break;
}
if (stopwatch.getTime() > 0.1f) {
// 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;
}
stopwatch.reset();
}
}
// send last message
ClipboardChunk* end = ClipboardChunk::end(id, sequence);
events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, end));
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()
{
if (s_isChunkingFile) {
s_interruptFile = true;
LOG((CLOG_INFO "previous dragged file has become invalid"));
}
}
void
StreamChunker::interruptClipboard()
{
if (s_isChunkingClipboard) {
s_interruptClipboard = true;
LOG((CLOG_INFO "previous clipboard data has become invalid"));
}
}

View File

@ -0,0 +1,48 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "synergy/clipboard_types.h"
#include "base/String.h"
class IEventQueue;
class StreamChunker {
public:
static void sendFile(
char* filename,
IEventQueue* events,
void* eventTarget);
static void sendClipboard(
String& data,
size_t size,
ClipboardID id,
UInt32 sequence,
IEventQueue* events,
void* eventTarget);
static void updateChunkSize(bool useSecureSocket);
static void interruptFile();
static void interruptClipboard();
private:
static size_t s_chunkSize;
static bool s_isChunkingClipboard;
static bool s_interruptClipboard;
static bool s_isChunkingFile;
static bool s_interruptFile;
};

View File

@ -41,7 +41,7 @@ const char* kMsgDMouseMove = "DMMV%2i%2i";
const char* kMsgDMouseRelMove = "DMRM%2i%2i";
const char* kMsgDMouseWheel = "DMWM%2i%2i";
const char* kMsgDMouseWheel1_0 = "DMWM%2i";
const char* kMsgDClipboard = "DCLP%1i%4i%s";
const char* kMsgDClipboard = "DCLP%1i%4i%1i%s";
const char* kMsgDInfo = "DINF%2i%2i%2i%2i%2i%2i%2i";
const char* kMsgDSetOptions = "DSOP%4I";
const char* kMsgDFileTransfer = "DFTR%1i%s";

View File

@ -28,9 +28,10 @@
// adds horizontal mouse scrolling
// 1.4: adds crypto support
// 1.5: adds file transfer and removes home brew crypto
// 1.6: adds clipboard streaming
// NOTE: with new version, synergy minor version should increment
static const SInt16 kProtocolMajorVersion = 1;
static const SInt16 kProtocolMinorVersion = 5;
static const SInt16 kProtocolMinorVersion = 6;
// default contact port number
static const UInt16 kDefaultPort = 24800;
@ -69,13 +70,19 @@ enum EDirectionMask {
kBottomMask = 1 << kBottom
};
// file transfer constants
enum EFileTransfer {
kFileStart = 1,
kFileChunk = 2,
kFileEnd = 3
// Data transfer constants
enum EDataTransfer {
kDataStart = 1,
kDataChunk = 2,
kDataEnd = 3
};
// Data received constants
enum EDataReceived {
kNotFinish,
kFinish,
kError
};
//
// message codes (trailing NUL is not part of code). in comments, $n
@ -225,7 +232,7 @@ extern const char* kMsgDMouseWheel;
extern const char* kMsgDMouseWheel1_0;
// clipboard data: primary <-> secondary
// $2 = sequence number, $3 = clipboard data. the sequence number
// $2 = sequence number, $3 = mark $4 = clipboard data. the sequence number
// is 0 when sent by the primary. secondary screens should use the
// sequence number from the most recent kMsgCEnter. $1 = clipboard
// identifier.

View File

@ -28,7 +28,8 @@
#include "server/Server.h"
#include "server/ClientListener.h"
#include "client/Client.h"
#include "synergy/FileChunker.h"
#include "synergy/FileChunk.h"
#include "synergy/StreamChunker.h"
#include "net/SocketMultiplexer.h"
#include "net/NetworkAddress.h"
#include "net/TCPSocketFactory.h"
@ -60,7 +61,6 @@ const size_t kMockFileSize = 1024 * 1024 * 10; // 10MB
void getScreenShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h);
void getCursorPos(SInt32& x, SInt32& y);
String intToString(size_t i);
UInt8* newMockData(size_t size);
void createFile(fstream& file, const char* filename, size_t size);
@ -415,38 +415,28 @@ void
NetworkTests::sendMockData(void* eventTarget)
{
// send first message (file size)
String size = intToString(kMockDataSize);
size_t sizeLength = size.size();
FileChunker::FileChunk* sizeMessage = new FileChunker::FileChunk(sizeLength + 2);
char* chunkData = sizeMessage->m_chunk;
String size = synergy::string::sizeTypeToString(kMockDataSize);
FileChunk* sizeMessage = FileChunk::start(size);
chunkData[0] = kFileStart;
memcpy(&chunkData[1], size.c_str(), sizeLength);
chunkData[sizeLength + 1] = '\0';
m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, sizeMessage));
// send chunk messages with incrementing chunk size
size_t lastSize = 0;
size_t sentLength = 0;
while (true) {
size_t chunkSize = lastSize + kMockDataChunkIncrement;
size_t dataSize = lastSize + kMockDataChunkIncrement;
// make sure we don't read too much from the mock data.
if (sentLength + chunkSize > kMockDataSize) {
chunkSize = kMockDataSize - sentLength;
if (sentLength + dataSize > kMockDataSize) {
dataSize = kMockDataSize - sentLength;
}
// first byte is the chunk mark, last is \0
FileChunker::FileChunk* fileChunk = new FileChunker::FileChunk(chunkSize + 2);
char* chunkData = fileChunk->m_chunk;
FileChunk* chunk = FileChunk::data(m_mockData, dataSize);
m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, chunk));
chunkData[0] = kFileChunk;
memcpy(&chunkData[1], &m_mockData[sentLength], chunkSize);
chunkData[chunkSize + 1] = '\0';
m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, fileChunk));
sentLength += chunkSize;
lastSize = chunkSize;
sentLength += dataSize;
lastSize = dataSize;
if (sentLength == kMockDataSize) {
break;
@ -455,11 +445,7 @@ NetworkTests::sendMockData(void* eventTarget)
}
// send last message
FileChunker::FileChunk* transferFinished = new FileChunker::FileChunk(2);
chunkData = transferFinished->m_chunk;
chunkData[0] = kFileEnd;
chunkData[1] = '\0';
FileChunk* transferFinished = FileChunk::end();
m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, transferFinished));
}
@ -527,12 +513,4 @@ getCursorPos(SInt32& x, SInt32& y)
y = 0;
}
String
intToString(size_t i)
{
stringstream ss;
ss << i;
return ss.str();
}
#endif // WINAPI_CARBON

View File

@ -61,5 +61,6 @@ public:
MOCK_METHOD0(forIKeyState, IKeyStateEvents&());
MOCK_METHOD0(forIPrimaryScreen, IPrimaryScreenEvents&());
MOCK_METHOD0(forIScreen, IScreenEvents&());
MOCK_METHOD0(forClipboard, ClipboardEvents&());
MOCK_CONST_METHOD0(waitForReady, void());
};

View File

@ -82,3 +82,21 @@ TEST(StringTests, removeChar)
EXPECT_EQ("fbar", subject);
}
TEST(StringTests, intToString)
{
size_t value = 123;
String number = string::sizeTypeToString(value);
EXPECT_EQ("123", number);
}
TEST(StringTests, stringToUint)
{
String number = "123";
size_t value = string::stringToSizeType(number);
EXPECT_EQ(123, value);
}

View File

@ -120,7 +120,9 @@ TEST(IpcLogOutputterTests, write_overBufferRateLimit_lastLineTruncated)
// after waiting the time limit send another to make sure
// we can log after the time limit passes.
ARCH->sleep(0.01); // 10ms
// HACK: sleep causes the unit test to fail intermittently,
// so lets try 100ms (there must be a better way to solve this)
ARCH->sleep(0.1); // 100ms
outputter.write(kNOTE, "mock 3");
outputter.write(kNOTE, "mock 4");
outputter.sendBuffer();

View File

@ -0,0 +1,76 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2015 Synergy Si Inc.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "synergy/ClipboardChunk.h"
#include "synergy/protocol_types.h"
#include "test/global/gtest.h"
TEST(ClipboardChunkTests, start_formatStartChunk)
{
ClipboardID id = 0;
UInt32 sequence = 0;
String mockDataSize("10");
ClipboardChunk* chunk = ClipboardChunk::start(id, sequence, mockDataSize);
EXPECT_EQ(id, chunk->m_chunk[0]);
EXPECT_EQ(sequence, (UInt32)chunk->m_chunk[1]);
EXPECT_EQ(kDataStart, chunk->m_chunk[5]);
EXPECT_EQ('1', chunk->m_chunk[6]);
EXPECT_EQ('0', chunk->m_chunk[7]);
EXPECT_EQ('\0', chunk->m_chunk[8]);
delete chunk;
}
TEST(ClipboardChunkTests, data_formatDataChunk)
{
ClipboardID id = 0;
UInt32 sequence = 1;
String mockData("mock data");
ClipboardChunk* chunk = ClipboardChunk::data(id, sequence, mockData);
EXPECT_EQ(id, chunk->m_chunk[0]);
EXPECT_EQ(sequence, (UInt32)chunk->m_chunk[1]);
EXPECT_EQ(kDataChunk, chunk->m_chunk[5]);
EXPECT_EQ('m', chunk->m_chunk[6]);
EXPECT_EQ('o', chunk->m_chunk[7]);
EXPECT_EQ('c', chunk->m_chunk[8]);
EXPECT_EQ('k', chunk->m_chunk[9]);
EXPECT_EQ(' ', chunk->m_chunk[10]);
EXPECT_EQ('d', chunk->m_chunk[11]);
EXPECT_EQ('a', chunk->m_chunk[12]);
EXPECT_EQ('t', chunk->m_chunk[13]);
EXPECT_EQ('a', chunk->m_chunk[14]);
EXPECT_EQ('\0', chunk->m_chunk[15]);
delete chunk;
}
TEST(ClipboardChunkTests, end_formatDataChunk)
{
ClipboardID id = 1;
UInt32 sequence = 1;
ClipboardChunk* chunk = ClipboardChunk::end(id, sequence);
EXPECT_EQ(id, chunk->m_chunk[0]);
EXPECT_EQ(sequence, (UInt32)chunk->m_chunk[1]);
EXPECT_EQ(kDataEnd, chunk->m_chunk[5]);
EXPECT_EQ('\0', chunk->m_chunk[6]);
delete chunk;
}