Merge branch 'master' of github.com:synergy/synergy
This commit is contained in:
commit
d816ed6b43
36
ChangeLog
36
ChangeLog
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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
|
||||
/*!
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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();
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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";
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue