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 #4564 - Modifier keys often stuck down on Mac client
|
||||||
Bug #4581 - Starting GUI on Mac crashes instantly on syntool segfault
|
Bug #4581 - Starting GUI on Mac crashes instantly on syntool segfault
|
||||||
Bug #4520 - Laggy or sluggish cursor (ping spikes) on Mac when using WiFi
|
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 #4570 - Make `--crypto-pass` show deprecated message
|
||||||
Enhancement #4596 - Typo 'occurred' in WebClient.cpp
|
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 #3784 - Double click & drag doesn't select words on client
|
||||||
Bug #3052 - Triple-click (select line) does not work
|
Bug #3052 - Triple-click (select line) does not work
|
||||||
Bug #4367 - Duplicate Alt-S Keyboard Shortcuts on Gui
|
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 #4525 - Reorganize app data directory
|
||||||
Enhancement #4390 - Disable GUI auto-hide by default
|
Enhancement #4390 - Disable GUI auto-hide by default
|
||||||
|
|
||||||
1.7.0
|
v1.7.0-beta
|
||||||
=====
|
===========
|
||||||
Enhancement #4313 - SSL encrypted secure connection
|
Enhancement #4313 - SSL encrypted secure connection
|
||||||
Enhancement #4168 - Plugin manager for GUI
|
Enhancement #4168 - Plugin manager for GUI
|
||||||
Enhancement #4307 - Always show client auto-detect dialog
|
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 #2905: Unit tests: Clipboard classes
|
||||||
Task #3072: Downgrade Linux build machines
|
Task #3072: Downgrade Linux build machines
|
||||||
Task #3090: CXWindowsKeyState integ test args wrong
|
Task #3090: CXWindowsKeyState integ test args wrong
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
# TODO: split this file up, it's too long!
|
# TODO: split this file up, it's too long!
|
||||||
|
|
||||||
import sys, os, ConfigParser, shutil, re, ftputil, zipfile, glob, commands
|
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
|
from getopt import gnu_getopt
|
||||||
|
|
||||||
if sys.version_info >= (2, 4):
|
if sys.version_info >= (2, 4):
|
||||||
|
@ -55,14 +55,14 @@ class Toolchain:
|
||||||
'reformat' : ['', []],
|
'reformat' : ['', []],
|
||||||
'open' : ['', []],
|
'open' : ['', []],
|
||||||
'genlist' : ['', []],
|
'genlist' : ['', []],
|
||||||
'reset' : ['', []],
|
'reset' : ['', []],
|
||||||
'signwin' : ['', ['pfx=', 'pwd=', 'dist']],
|
'signwin' : ['', ['pfx=', 'pwd=', 'dist']],
|
||||||
'signmac' : ['', []]
|
'signmac' : ['', []]
|
||||||
}
|
}
|
||||||
|
|
||||||
# aliases to valid commands
|
# aliases to valid commands
|
||||||
cmd_alias_dict = {
|
cmd_alias_dict = {
|
||||||
'info' : 'about',
|
'info' : 'about',
|
||||||
'help' : 'usage',
|
'help' : 'usage',
|
||||||
'package' : 'dist',
|
'package' : 'dist',
|
||||||
'docs' : 'doxygen',
|
'docs' : 'doxygen',
|
||||||
|
@ -254,12 +254,12 @@ class InternalCommands:
|
||||||
gmockDir = 'gmock-1.6.0'
|
gmockDir = 'gmock-1.6.0'
|
||||||
|
|
||||||
win32_generators = {
|
win32_generators = {
|
||||||
1 : Generator('Visual Studio 10'),
|
1 : VisualStudioGenerator('10'),
|
||||||
2 : Generator('Visual Studio 10 Win64'),
|
2 : VisualStudioGenerator('10 Win64'),
|
||||||
3 : Generator('Visual Studio 9 2008'),
|
3 : VisualStudioGenerator('9 2008'),
|
||||||
4 : Generator('Visual Studio 9 2008 Win64'),
|
4 : VisualStudioGenerator('9 2008 Win64'),
|
||||||
5 : Generator('Visual Studio 8 2005'),
|
5 : VisualStudioGenerator('8 2005'),
|
||||||
6 : Generator('Visual Studio 8 2005 Win64')
|
6 : VisualStudioGenerator('8 2005 Win64')
|
||||||
}
|
}
|
||||||
|
|
||||||
unix_generators = {
|
unix_generators = {
|
||||||
|
@ -319,7 +319,6 @@ class InternalCommands:
|
||||||
self.configure(target)
|
self.configure(target)
|
||||||
|
|
||||||
def checkGTest(self):
|
def checkGTest(self):
|
||||||
|
|
||||||
dir = self.extDir + '/' + self.gtestDir
|
dir = self.extDir + '/' + self.gtestDir
|
||||||
if (os.path.isdir(dir)):
|
if (os.path.isdir(dir)):
|
||||||
return
|
return
|
||||||
|
@ -335,7 +334,6 @@ class InternalCommands:
|
||||||
self.zipExtractAll(zip, dir)
|
self.zipExtractAll(zip, dir)
|
||||||
|
|
||||||
def checkGMock(self):
|
def checkGMock(self):
|
||||||
|
|
||||||
dir = self.extDir + '/' + self.gmockDir
|
dir = self.extDir + '/' + self.gmockDir
|
||||||
if (os.path.isdir(dir)):
|
if (os.path.isdir(dir)):
|
||||||
return
|
return
|
||||||
|
@ -524,14 +522,14 @@ class InternalCommands:
|
||||||
version = commands.getoutput("qmake --version")
|
version = commands.getoutput("qmake --version")
|
||||||
result = re.search('(\d+)\.(\d+)\.(\d)', version)
|
result = re.search('(\d+)\.(\d+)\.(\d)', version)
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
raise Exception("Could not get qmake version.")
|
raise Exception("Could not get qmake version.")
|
||||||
|
|
||||||
major = int(result.group(1))
|
major = int(result.group(1))
|
||||||
minor = int(result.group(2))
|
minor = int(result.group(2))
|
||||||
rev = int(result.group(3))
|
rev = int(result.group(3))
|
||||||
|
|
||||||
return (major, minor, rev)
|
return (major, minor, rev)
|
||||||
|
|
||||||
def getMacSdkDir(self):
|
def getMacSdkDir(self):
|
||||||
sdkName = "macosx" + self.macSdk
|
sdkName = "macosx" + self.macSdk
|
||||||
|
@ -575,10 +573,10 @@ class InternalCommands:
|
||||||
# if return code from cmake is not 0, then either something has
|
# if return code from cmake is not 0, then either something has
|
||||||
# gone terribly wrong with --version, or it genuinely doesn't exist.
|
# gone terribly wrong with --version, or it genuinely doesn't exist.
|
||||||
print ('Could not find `%s` in system path.\n'
|
print ('Could not find `%s` in system path.\n'
|
||||||
'Download the latest version from:\n %s') % (
|
'Download the latest version from:\n %s') % (
|
||||||
self.cmake_cmd, self.cmake_url)
|
self.cmake_cmd, self.cmake_url)
|
||||||
raise Exception('Cannot continue without CMake.')
|
raise Exception('Cannot continue without CMake.')
|
||||||
else:
|
else:
|
||||||
return self.cmake_cmd
|
return self.cmake_cmd
|
||||||
|
|
||||||
def persist_qt(self):
|
def persist_qt(self):
|
||||||
|
@ -786,15 +784,15 @@ class InternalCommands:
|
||||||
# reorganize Qt frameworks layout on Mac 10.9.5 or later
|
# reorganize Qt frameworks layout on Mac 10.9.5 or later
|
||||||
# http://goo.gl/BFnQ8l
|
# http://goo.gl/BFnQ8l
|
||||||
# QtCore example:
|
# QtCore example:
|
||||||
# QtCore.framework/
|
# QtCore.framework/
|
||||||
# QtCore -> Versions/Current/QtCore
|
# QtCore -> Versions/Current/QtCore
|
||||||
# Resources -> Versions/Current/Resources
|
# Resources -> Versions/Current/Resources
|
||||||
# Versions/
|
# Versions/
|
||||||
# Current -> 5
|
# Current -> 5
|
||||||
# 5/
|
# 5/
|
||||||
# QtCore
|
# QtCore
|
||||||
# Resources/
|
# Resources/
|
||||||
# Info.plist
|
# Info.plist
|
||||||
targetDir = self.getGenerator().getBinDir(target)
|
targetDir = self.getGenerator().getBinDir(target)
|
||||||
|
|
||||||
target = targetDir + "/Synergy.app/Contents/Frameworks"
|
target = targetDir + "/Synergy.app/Contents/Frameworks"
|
||||||
|
@ -840,7 +838,7 @@ class InternalCommands:
|
||||||
pwd = lines[0]
|
pwd = lines[0]
|
||||||
|
|
||||||
if (dist):
|
if (dist):
|
||||||
self.signFile(pfx, pwd, 'bin', self.dist_name('win'))
|
self.signFile(pfx, pwd, 'bin/Release', self.dist_name('win'))
|
||||||
else:
|
else:
|
||||||
self.signFile(pfx, pwd, 'bin/Release', 'synergy.exe')
|
self.signFile(pfx, pwd, 'bin/Release', 'synergy.exe')
|
||||||
self.signFile(pfx, pwd, 'bin/Release', 'synergyc.exe')
|
self.signFile(pfx, pwd, 'bin/Release', 'synergyc.exe')
|
||||||
|
@ -974,7 +972,13 @@ class InternalCommands:
|
||||||
if p.returncode != 0:
|
if p.returncode != 0:
|
||||||
raise Exception('Could not get branch name, git error: ' + str(p.returncode))
|
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):
|
def find_revision_svn(self):
|
||||||
if sys.version_info < (2, 4):
|
if sys.version_info < (2, 4):
|
||||||
|
@ -1026,7 +1030,7 @@ class InternalCommands:
|
||||||
|
|
||||||
elif type == 'rpm':
|
elif type == 'rpm':
|
||||||
if sys.platform == 'linux2':
|
if sys.platform == 'linux2':
|
||||||
self.distRpm()
|
self.distRpm()
|
||||||
else:
|
else:
|
||||||
package_unsupported = True
|
package_unsupported = True
|
||||||
|
|
||||||
|
@ -1062,9 +1066,9 @@ class InternalCommands:
|
||||||
("Package type, '%s' is not supported for platform, '%s'")
|
("Package type, '%s' is not supported for platform, '%s'")
|
||||||
% (type, sys.platform))
|
% (type, sys.platform))
|
||||||
|
|
||||||
def distRpm(self):
|
def distRpm(self):
|
||||||
rpmDir = self.getGenerator().buildDir + '/rpm'
|
rpmDir = self.getGenerator().buildDir + '/rpm'
|
||||||
if os.path.exists(rpmDir):
|
if os.path.exists(rpmDir):
|
||||||
shutil.rmtree(rpmDir)
|
shutil.rmtree(rpmDir)
|
||||||
|
|
||||||
os.makedirs(rpmDir)
|
os.makedirs(rpmDir)
|
||||||
|
@ -1072,51 +1076,52 @@ class InternalCommands:
|
||||||
templateFile = open(self.cmake_dir + '/synergy.spec.in')
|
templateFile = open(self.cmake_dir + '/synergy.spec.in')
|
||||||
template = templateFile.read()
|
template = templateFile.read()
|
||||||
|
|
||||||
template = template.replace('${in:version}', self.getVersionFromCmake())
|
template = template.replace('${in:version}', self.getVersionFromCmake())
|
||||||
|
|
||||||
specPath = rpmDir + '/synergy.spec'
|
specPath = rpmDir + '/synergy.spec'
|
||||||
|
|
||||||
specFile = open(specPath, 'w')
|
specFile = open(specPath, 'w')
|
||||||
specFile.write(template)
|
specFile.write(template)
|
||||||
specFile.close()
|
specFile.close()
|
||||||
|
|
||||||
version = self.getVersionFromCmake()
|
version = self.getVersionFromCmake()
|
||||||
target = '../../bin/synergy-%s-%s.rpm' % (
|
target = '../../bin/synergy-%s-%s.rpm' % (
|
||||||
version, self.getLinuxPlatform())
|
version, self.getLinuxPlatform())
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.try_chdir(rpmDir)
|
self.try_chdir(rpmDir)
|
||||||
cmd = 'rpmbuild -bb --define "_topdir `pwd`" synergy.spec'
|
cmd = 'rpmbuild -bb --define "_topdir `pwd`" synergy.spec'
|
||||||
print "Command: " + cmd
|
print "Command: " + cmd
|
||||||
err = os.system(cmd)
|
err = os.system(cmd)
|
||||||
if err != 0:
|
if err != 0:
|
||||||
raise Exception('rpmbuild failed: ' + str(err))
|
raise Exception('rpmbuild failed: ' + str(err))
|
||||||
|
|
||||||
self.unixMove('RPMS/*/*.rpm', target)
|
self.unixMove('RPMS/*/*.rpm', target)
|
||||||
|
|
||||||
cmd = 'rpmlint ' + target
|
cmd = 'rpmlint ' + target
|
||||||
print "Command: " + cmd
|
print "Command: " + cmd
|
||||||
err = os.system(cmd)
|
err = os.system(cmd)
|
||||||
if err != 0:
|
if err != 0:
|
||||||
raise Exception('rpmlint failed: ' + str(err))
|
raise Exception('rpmlint failed: ' + str(err))
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
self.restore_chdir()
|
self.restore_chdir()
|
||||||
|
|
||||||
def distDeb(self):
|
def distDeb(self):
|
||||||
buildDir = self.getGenerator().buildDir
|
buildDir = self.getGenerator().buildDir
|
||||||
binDir = self.getGenerator().binDir
|
binDir = self.getGenerator().binDir
|
||||||
resDir = self.cmake_dir
|
resDir = self.cmake_dir
|
||||||
|
|
||||||
version = self.getVersionFromCmake()
|
version = self.getVersionFromCmake()
|
||||||
package = '%s-%s-%s' % (
|
package = '%s-%s-%s' % (
|
||||||
self.project, version, self.getLinuxPlatform())
|
self.project, version, self.getLinuxPlatform())
|
||||||
|
|
||||||
debDir = '%s/deb' % buildDir
|
debDir = '%s/deb' % buildDir
|
||||||
if os.path.exists(debDir):
|
if os.path.exists(debDir):
|
||||||
shutil.rmtree(debDir)
|
shutil.rmtree(debDir)
|
||||||
|
|
||||||
metaDir = '%s/%s/DEBIAN' % (debDir, package)
|
metaDir = '%s/%s/DEBIAN' % (debDir, package)
|
||||||
os.makedirs(metaDir)
|
os.makedirs(metaDir)
|
||||||
|
|
||||||
templateFile = open(resDir + '/deb/control.in')
|
templateFile = open(resDir + '/deb/control.in')
|
||||||
|
@ -1194,14 +1199,14 @@ class InternalCommands:
|
||||||
self.try_chdir(debDir)
|
self.try_chdir(debDir)
|
||||||
|
|
||||||
# TODO: consider dpkg-buildpackage (higher level tool)
|
# TODO: consider dpkg-buildpackage (higher level tool)
|
||||||
cmd = 'fakeroot dpkg-deb --build %s' % package
|
cmd = 'fakeroot dpkg-deb --build %s' % package
|
||||||
print "Command: " + cmd
|
print "Command: " + cmd
|
||||||
err = os.system(cmd)
|
err = os.system(cmd)
|
||||||
if err != 0:
|
if err != 0:
|
||||||
raise Exception('dpkg-deb failed: ' + str(err))
|
raise Exception('dpkg-deb failed: ' + str(err))
|
||||||
|
|
||||||
cmd = 'lintian %s.deb' % package
|
cmd = 'lintian %s.deb' % package
|
||||||
print "Command: " + cmd
|
print "Command: " + cmd
|
||||||
err = os.system(cmd)
|
err = os.system(cmd)
|
||||||
if err != 0:
|
if err != 0:
|
||||||
raise Exception('lintian failed: ' + str(err))
|
raise Exception('lintian failed: ' + str(err))
|
||||||
|
@ -1294,7 +1299,7 @@ class InternalCommands:
|
||||||
arch)
|
arch)
|
||||||
|
|
||||||
old = "bin/Release/synergy.msi"
|
old = "bin/Release/synergy.msi"
|
||||||
new = "bin/%s" % (filename)
|
new = "bin/Release/%s" % (filename)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.remove(new)
|
os.remove(new)
|
||||||
|
@ -1320,7 +1325,7 @@ class InternalCommands:
|
||||||
|
|
||||||
if generator.endswith('Win64'):
|
if generator.endswith('Win64'):
|
||||||
arch = 'x64'
|
arch = 'x64'
|
||||||
installDirVar = '$PROGRAMFILES64'
|
installDirVar = '$PROGRAMFILES64'
|
||||||
|
|
||||||
templateFile = open(self.cmake_dir + '\Installer.nsi.in')
|
templateFile = open(self.cmake_dir + '\Installer.nsi.in')
|
||||||
template = templateFile.read()
|
template = templateFile.read()
|
||||||
|
@ -1359,54 +1364,54 @@ class InternalCommands:
|
||||||
|
|
||||||
def distftp(self, type, ftp):
|
def distftp(self, type, ftp):
|
||||||
if not type:
|
if not type:
|
||||||
raise Exception('Type not specified.')
|
raise Exception('Platform type not specified.')
|
||||||
|
|
||||||
if not ftp:
|
|
||||||
raise Exception('FTP info not defined.')
|
|
||||||
|
|
||||||
self.loadConfig()
|
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')
|
binDir = self.getGenerator().getBinDir('Release')
|
||||||
ftp.run(binDir + '/' + src, dest)
|
|
||||||
print 'Done'
|
|
||||||
|
|
||||||
def getDebianArch(self):
|
|
||||||
if os.uname()[4][:3] == 'arm':
|
|
||||||
return 'armhf'
|
|
||||||
|
|
||||||
# os_bits should be loaded with '32bit' or '64bit'
|
packageSource = binDir + '/' + self.dist_name(type)
|
||||||
import platform
|
packageTarget = self.dist_name_rev(type)
|
||||||
(os_bits, other) = platform.architecture()
|
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
|
||||||
|
|
||||||
# get platform based on current platform
|
return None
|
||||||
if os_bits == '32bit':
|
|
||||||
return 'i386'
|
|
||||||
elif os_bits == '64bit':
|
|
||||||
return 'amd64'
|
|
||||||
else:
|
|
||||||
raise Exception("unknown os bits: " + os_bits)
|
|
||||||
|
|
||||||
def getLinuxPlatform(self):
|
def getDistributePlatformInfo(self, type):
|
||||||
if os.uname()[4][:3] == 'arm':
|
|
||||||
return 'Linux-armv6l'
|
|
||||||
|
|
||||||
# os_bits should be loaded with '32bit' or '64bit'
|
|
||||||
import platform
|
|
||||||
(os_bits, other) = platform.architecture()
|
|
||||||
|
|
||||||
# get platform based on current platform
|
|
||||||
if os_bits == '32bit':
|
|
||||||
return 'Linux-i686'
|
|
||||||
elif os_bits == '64bit':
|
|
||||||
return 'Linux-x86_64'
|
|
||||||
else:
|
|
||||||
raise Exception("unknown os bits: " + os_bits)
|
|
||||||
|
|
||||||
def dist_name(self, type):
|
|
||||||
ext = None
|
ext = None
|
||||||
|
libraryExt = None
|
||||||
platform = None
|
platform = None
|
||||||
|
|
||||||
if type == 'src':
|
if type == 'src':
|
||||||
|
@ -1414,14 +1419,14 @@ class InternalCommands:
|
||||||
platform = 'Source'
|
platform = 'Source'
|
||||||
|
|
||||||
elif type == 'rpm' or type == 'deb':
|
elif type == 'rpm' or type == 'deb':
|
||||||
|
|
||||||
ext = type
|
ext = type
|
||||||
platform = self.getLinuxPlatform()
|
libraryExt = 'so'
|
||||||
|
platform = self.getLinuxPlatform()
|
||||||
|
|
||||||
elif type == 'win':
|
elif type == 'win':
|
||||||
|
|
||||||
# get platform based on last generator used
|
# get platform based on last generator used
|
||||||
ext = 'msi'
|
ext = 'msi'
|
||||||
|
libraryExt = 'dll'
|
||||||
generator = self.getGeneratorFromConfig().cmakeName
|
generator = self.getGeneratorFromConfig().cmakeName
|
||||||
if generator.find('Win64') != -1:
|
if generator.find('Win64') != -1:
|
||||||
platform = 'Windows-x64'
|
platform = 'Windows-x64'
|
||||||
|
@ -1430,18 +1435,23 @@ class InternalCommands:
|
||||||
|
|
||||||
elif type == 'mac':
|
elif type == 'mac':
|
||||||
ext = "dmg"
|
ext = "dmg"
|
||||||
|
libraryExt = 'dylib'
|
||||||
platform = self.getMacPackageName()
|
platform = self.getMacPackageName()
|
||||||
|
|
||||||
if not platform:
|
if not platform:
|
||||||
raise Exception('Unable to detect package platform.')
|
raise Exception('Unable to detect distributable 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)):
|
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):
|
if re.search(pattern, filename):
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
|
@ -1449,11 +1459,45 @@ class InternalCommands:
|
||||||
raise Exception('Could not find package name with pattern: ' + pattern)
|
raise Exception('Could not find package name with pattern: ' + pattern)
|
||||||
|
|
||||||
def dist_name_rev(self, type):
|
def dist_name_rev(self, type):
|
||||||
|
branch = self.getGitBranchName()
|
||||||
|
revision = self.getGitRevision()
|
||||||
|
|
||||||
# find the version number (we're puting the rev in after this)
|
# find the version number (we're puting the rev in after this)
|
||||||
pattern = '(\d+\.\d+\.\d+)'
|
pattern = '(\d+\.\d+\.\d+)'
|
||||||
replace = "%s-%s" % (
|
replace = "%s-%s" % (branch, revision)
|
||||||
self.getGitBranchName(), self.getGitRevision())
|
|
||||||
return re.sub(pattern, replace, self.dist_name(type))
|
return re.sub(pattern, replace, self.dist_name(type))
|
||||||
|
|
||||||
|
def getDebianArch(self):
|
||||||
|
if os.uname()[4][:3] == 'arm':
|
||||||
|
return 'armhf'
|
||||||
|
|
||||||
|
# os_bits should be loaded with '32bit' or '64bit'
|
||||||
|
import platform
|
||||||
|
(os_bits, other) = platform.architecture()
|
||||||
|
|
||||||
|
# get platform based on current platform
|
||||||
|
if os_bits == '32bit':
|
||||||
|
return 'i386'
|
||||||
|
elif os_bits == '64bit':
|
||||||
|
return 'amd64'
|
||||||
|
else:
|
||||||
|
raise Exception("unknown os bits: " + os_bits)
|
||||||
|
|
||||||
|
def getLinuxPlatform(self):
|
||||||
|
if os.uname()[4][:3] == 'arm':
|
||||||
|
return 'Linux-armv6l'
|
||||||
|
|
||||||
|
# os_bits should be loaded with '32bit' or '64bit'
|
||||||
|
import platform
|
||||||
|
(os_bits, other) = platform.architecture()
|
||||||
|
|
||||||
|
# get platform based on current platform
|
||||||
|
if os_bits == '32bit':
|
||||||
|
return 'Linux-i686'
|
||||||
|
elif os_bits == '64bit':
|
||||||
|
return 'Linux-x86_64'
|
||||||
|
else:
|
||||||
|
raise Exception("unknown os bits: " + os_bits)
|
||||||
|
|
||||||
def dist_usage(self):
|
def dist_usage(self):
|
||||||
print ('Usage: %s package [package-type]\n'
|
print ('Usage: %s package [package-type]\n'
|
||||||
|
@ -1517,7 +1561,7 @@ class InternalCommands:
|
||||||
|
|
||||||
oldGenerator = self.findGeneratorFromConfig()
|
oldGenerator = self.findGeneratorFromConfig()
|
||||||
if not oldGenerator == None:
|
if not oldGenerator == None:
|
||||||
for target in ['debug', 'release']:
|
for target in ['debug', 'release']:
|
||||||
buildDir = oldGenerator.getBuildDir(target)
|
buildDir = oldGenerator.getBuildDir(target)
|
||||||
|
|
||||||
cmakeCacheFilename = 'CMakeCache.txt'
|
cmakeCacheFilename = 'CMakeCache.txt'
|
||||||
|
@ -1673,7 +1717,7 @@ class InternalCommands:
|
||||||
|
|
||||||
return generators[generator_id]
|
return generators[generator_id]
|
||||||
|
|
||||||
def get_vcvarsall(self, generator):
|
def get_vcvarsall(self, generator):
|
||||||
import platform, _winreg
|
import platform, _winreg
|
||||||
|
|
||||||
# os_bits should be loaded with '32bit' or '64bit'
|
# os_bits should be loaded with '32bit' or '64bit'
|
||||||
|
@ -1703,7 +1747,7 @@ class InternalCommands:
|
||||||
if os_bits == '64bit':
|
if os_bits == '64bit':
|
||||||
path = value + r'vc\vcvarsall.bat'
|
path = value + r'vc\vcvarsall.bat'
|
||||||
else:
|
else:
|
||||||
path = value + r'vcvarsall.bat'
|
path = value + r'vcvarsall.bat'
|
||||||
|
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
raise Exception("'%s' not found." % path)
|
raise Exception("'%s' not found." % path)
|
||||||
|
@ -1833,7 +1877,7 @@ class InternalCommands:
|
||||||
|
|
||||||
# qt 4.3 generates ui_ files.
|
# qt 4.3 generates ui_ files.
|
||||||
for filename in glob.glob("src/gui/ui_*"):
|
for filename in glob.glob("src/gui/ui_*"):
|
||||||
os.remove(filename)
|
os.remove(filename)
|
||||||
|
|
||||||
# the command handler should be called only from hm.py (i.e. directly
|
# the command handler should be called only from hm.py (i.e. directly
|
||||||
# from the command prompt). the purpose of this class is so that we
|
# from the command prompt). the purpose of this class is so that we
|
||||||
|
@ -1902,7 +1946,7 @@ class CommandHandler:
|
||||||
|
|
||||||
type = None
|
type = None
|
||||||
if len(self.args) > 0:
|
if len(self.args) > 0:
|
||||||
type = self.args[0]
|
type = self.args[0]
|
||||||
|
|
||||||
self.ic.dist(type, self.vcRedistDir, self.qtDir)
|
self.ic.dist(type, self.vcRedistDir, self.qtDir)
|
||||||
|
|
||||||
|
@ -1926,10 +1970,11 @@ class CommandHandler:
|
||||||
elif o == '--dir':
|
elif o == '--dir':
|
||||||
dir = a
|
dir = a
|
||||||
|
|
||||||
ftp = None
|
if not host:
|
||||||
if host:
|
raise Exception('FTP host was not specified.')
|
||||||
ftp = ftputil.FtpUploader(
|
|
||||||
host, user, password, dir)
|
ftp = ftputil.FtpUploader(
|
||||||
|
host, user, password, dir)
|
||||||
|
|
||||||
self.ic.distftp(type, ftp)
|
self.ic.distftp(type, ftp)
|
||||||
|
|
||||||
|
|
|
@ -23,13 +23,32 @@ class FtpUploader:
|
||||||
self.password = password
|
self.password = password
|
||||||
self.dir = dir
|
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 = 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')
|
f = open(src, 'rb')
|
||||||
ftp.storbinary('STOR ' + dest, f)
|
ftp.storbinary('STOR ' + dest, f)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
ftp.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):
|
def getSourceDir(self):
|
||||||
return self.sourceDir
|
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):
|
class MakefilesGenerator(Generator):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(MakefilesGenerator, self).__init__('Unix Makefiles')
|
super(MakefilesGenerator, self).__init__('Unix Makefiles')
|
||||||
|
|
|
@ -1132,12 +1132,12 @@ void MainWindow::downloadBonjour()
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
QUrl url;
|
QUrl url;
|
||||||
int arch = checkProcessorArch();
|
int arch = getProcessorArch();
|
||||||
if (arch == Win_x86) {
|
if (arch == kProcessorArchWin32) {
|
||||||
url.setUrl(bonjourBaseUrl + bonjourFilename32);
|
url.setUrl(bonjourBaseUrl + bonjourFilename32);
|
||||||
appendLogNote("downloading 32-bit Bonjour");
|
appendLogNote("downloading 32-bit Bonjour");
|
||||||
}
|
}
|
||||||
else if (arch == Win_x64) {
|
else if (arch == kProcessorArchWin64) {
|
||||||
url.setUrl(bonjourBaseUrl + bonjourFilename64);
|
url.setUrl(bonjourBaseUrl + bonjourFilename64);
|
||||||
appendLogNote("downloading 64-bit Bonjour");
|
appendLogNote("downloading 64-bit Bonjour");
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,14 +29,15 @@
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
static QString kBaseUrl = "http://synergy-project.org/files";
|
static const char kBaseUrl[] = "http://synergy-project.org/files";
|
||||||
static const char kWinProcessorArch32[] = "Windows-x86";
|
static const char kDefaultVersion[] = "1.1";
|
||||||
static const char kWinProcessorArch64[] = "Windows-x64";
|
static const char kWinPackagePlatform32[] = "Windows-x86";
|
||||||
static const char kMacProcessorArch[] = "MacOSX-i386";
|
static const char kWinPackagePlatform64[] = "Windows-x64";
|
||||||
static const char kLinuxProcessorArchDeb32[] = "Linux-i686-deb";
|
static const char kMacPackagePlatform[] = "MacOSX%1-i386";
|
||||||
static const char kLinuxProcessorArchDeb64[] = "Linux-x86_64-deb";
|
static const char kLinuxPackagePlatformDeb32[] = "Linux-i686-deb";
|
||||||
static const char kLinuxProcessorArchRpm32[] = "Linux-i686-rpm";
|
static const char kLinuxPackagePlatformDeb64[] = "Linux-x86_64-deb";
|
||||||
static const char kLinuxProcessorArchRpm64[] = "Linux-x86_64-rpm";
|
static const char kLinuxPackagePlatformRpm32[] = "Linux-i686-rpm";
|
||||||
|
static const char kLinuxPackagePlatformRpm64[] = "Linux-x86_64-rpm";
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
static const char kWinPluginExt[] = ".dll";
|
static const char kWinPluginExt[] = ".dll";
|
||||||
|
@ -157,10 +158,10 @@ QString PluginManager::getPluginUrl(const QString& pluginName)
|
||||||
try {
|
try {
|
||||||
QString coreArch = m_CoreInterface.getArch();
|
QString coreArch = m_CoreInterface.getArch();
|
||||||
if (coreArch.startsWith("x86")) {
|
if (coreArch.startsWith("x86")) {
|
||||||
archName = kWinProcessorArch32;
|
archName = kWinPackagePlatform32;
|
||||||
}
|
}
|
||||||
else if (coreArch.startsWith("x64")) {
|
else if (coreArch.startsWith("x64")) {
|
||||||
archName = kWinProcessorArch64;
|
archName = kWinPackagePlatform64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
|
@ -170,22 +171,53 @@ QString PluginManager::getPluginUrl(const QString& pluginName)
|
||||||
|
|
||||||
#elif defined(Q_OS_MAC)
|
#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
|
#else
|
||||||
|
|
||||||
int arch = checkProcessorArch();
|
QString program("dpkg");
|
||||||
if (arch == Linux_rpm_i686) {
|
QStringList args;
|
||||||
archName = kLinuxProcessorArchRpm32;
|
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 {
|
||||||
|
archName = kLinuxPackagePlatformRpm32;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (arch == Linux_deb_i686) {
|
else if (arch == kProcessorArchLinux64) {
|
||||||
archName = kLinuxProcessorArchDeb32;
|
if (isDeb) {
|
||||||
}
|
archName = kLinuxPackagePlatformDeb64;
|
||||||
else if (arch == Linux_deb_x86_64) {
|
}
|
||||||
archName = kLinuxProcessorArchDeb64;
|
else {
|
||||||
|
archName = kLinuxPackagePlatformRpm64;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
emit error(tr("Could not get Linux architecture type."));
|
emit error(tr("Could not get Linux architecture type."));
|
||||||
|
@ -194,13 +226,14 @@ QString PluginManager::getPluginUrl(const QString& pluginName)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QString result = kBaseUrl;
|
QString result = QString("%1/plugins/%2/%3/%4/%5")
|
||||||
result.append("/plugins/");
|
.arg(kBaseUrl)
|
||||||
result.append(pluginName).append("/1.0/");
|
.arg(pluginName)
|
||||||
result.append(archName);
|
.arg(kDefaultVersion)
|
||||||
result.append("/");
|
.arg(archName)
|
||||||
result.append(getPluginOsSpecificName(pluginName));
|
.arg(getPluginOsSpecificName(pluginName));
|
||||||
|
|
||||||
|
qDebug() << result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,18 +15,14 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PROCESSORARCH_H
|
#pragma once
|
||||||
#define PROCESSORARCH_H
|
|
||||||
|
|
||||||
enum qProcessorArch {
|
enum qProcessorArch {
|
||||||
Win_x86,
|
kProcessorArchWin32,
|
||||||
Win_x64,
|
kProcessorArchWin64,
|
||||||
Mac_i386,
|
kProcessorArchMac32,
|
||||||
Linux_rpm_i686,
|
kProcessorArchMac64,
|
||||||
Linux_rpm_x86_64,
|
kProcessorArchLinux32,
|
||||||
Linux_deb_i686,
|
kProcessorArchLinux64,
|
||||||
Linux_deb_x86_64,
|
kProcessorArchUnknown
|
||||||
unknown
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PROCESSORARCH_H
|
|
||||||
|
|
|
@ -29,12 +29,6 @@
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#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)
|
void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < comboBox->count(); ++i)
|
for (int i = 0; i < comboBox->count(); ++i)
|
||||||
|
@ -68,7 +62,7 @@ QString getFirstMacAddress()
|
||||||
return mac;
|
return mac;
|
||||||
}
|
}
|
||||||
|
|
||||||
int checkProcessorArch()
|
qProcessorArch getProcessorArch()
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
SYSTEM_INFO systemInfo;
|
SYSTEM_INFO systemInfo;
|
||||||
|
@ -76,97 +70,23 @@ int checkProcessorArch()
|
||||||
|
|
||||||
switch (systemInfo.wProcessorArchitecture) {
|
switch (systemInfo.wProcessorArchitecture) {
|
||||||
case PROCESSOR_ARCHITECTURE_INTEL:
|
case PROCESSOR_ARCHITECTURE_INTEL:
|
||||||
return Win_x86;
|
return kProcessorArchWin32;
|
||||||
case PROCESSOR_ARCHITECTURE_IA64:
|
case PROCESSOR_ARCHITECTURE_IA64:
|
||||||
return Win_x64;
|
return kProcessorArchWin64;
|
||||||
case PROCESSOR_ARCHITECTURE_AMD64:
|
case PROCESSOR_ARCHITECTURE_AMD64:
|
||||||
return Win_x64;
|
return kProcessorArchWin64;
|
||||||
default:
|
default:
|
||||||
return unknown;
|
return kProcessorArchUnknown;
|
||||||
}
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
#pragma once
|
||||||
|
|
||||||
|
#include "ProcessorArch.h"
|
||||||
|
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
|
@ -25,4 +27,4 @@
|
||||||
void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData);
|
void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData);
|
||||||
QString hash(const QString& string);
|
QString hash(const QString& string);
|
||||||
QString getFirstMacAddress();
|
QString getFirstMacAddress();
|
||||||
int checkProcessorArch();
|
qProcessorArch getProcessorArch();
|
||||||
|
|
|
@ -46,6 +46,7 @@ EVENT_TYPE_ACCESSOR(ServerApp)
|
||||||
EVENT_TYPE_ACCESSOR(IKeyState)
|
EVENT_TYPE_ACCESSOR(IKeyState)
|
||||||
EVENT_TYPE_ACCESSOR(IPrimaryScreen)
|
EVENT_TYPE_ACCESSOR(IPrimaryScreen)
|
||||||
EVENT_TYPE_ACCESSOR(IScreen)
|
EVENT_TYPE_ACCESSOR(IScreen)
|
||||||
|
EVENT_TYPE_ACCESSOR(Clipboard)
|
||||||
|
|
||||||
// interrupt handler. this just adds a quit event to the queue.
|
// interrupt handler. this just adds a quit event to the queue.
|
||||||
static
|
static
|
||||||
|
@ -82,6 +83,7 @@ EventQueue::EventQueue() :
|
||||||
m_typesForIKeyState(NULL),
|
m_typesForIKeyState(NULL),
|
||||||
m_typesForIPrimaryScreen(NULL),
|
m_typesForIPrimaryScreen(NULL),
|
||||||
m_typesForIScreen(NULL),
|
m_typesForIScreen(NULL),
|
||||||
|
m_typesForClipboard(NULL),
|
||||||
m_readyMutex(new Mutex),
|
m_readyMutex(new Mutex),
|
||||||
m_readyCondVar(new CondVar<bool>(m_readyMutex, false))
|
m_readyCondVar(new CondVar<bool>(m_readyMutex, false))
|
||||||
{
|
{
|
||||||
|
|
|
@ -157,6 +157,7 @@ public:
|
||||||
IKeyStateEvents& forIKeyState();
|
IKeyStateEvents& forIKeyState();
|
||||||
IPrimaryScreenEvents& forIPrimaryScreen();
|
IPrimaryScreenEvents& forIPrimaryScreen();
|
||||||
IScreenEvents& forIScreen();
|
IScreenEvents& forIScreen();
|
||||||
|
ClipboardEvents& forClipboard();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ClientEvents* m_typesForClient;
|
ClientEvents* m_typesForClient;
|
||||||
|
@ -177,6 +178,7 @@ private:
|
||||||
IKeyStateEvents* m_typesForIKeyState;
|
IKeyStateEvents* m_typesForIKeyState;
|
||||||
IPrimaryScreenEvents* m_typesForIPrimaryScreen;
|
IPrimaryScreenEvents* m_typesForIPrimaryScreen;
|
||||||
IScreenEvents* m_typesForIScreen;
|
IScreenEvents* m_typesForIScreen;
|
||||||
|
ClipboardEvents* m_typesForClipboard;
|
||||||
Mutex* m_readyMutex;
|
Mutex* m_readyMutex;
|
||||||
CondVar<bool>* m_readyCondVar;
|
CondVar<bool>* m_readyCondVar;
|
||||||
std::queue<Event> m_pending;
|
std::queue<Event> m_pending;
|
||||||
|
|
|
@ -115,7 +115,6 @@ REGISTER_EVENT(ClientListener, connected)
|
||||||
|
|
||||||
REGISTER_EVENT(ClientProxy, ready)
|
REGISTER_EVENT(ClientProxy, ready)
|
||||||
REGISTER_EVENT(ClientProxy, disconnected)
|
REGISTER_EVENT(ClientProxy, disconnected)
|
||||||
REGISTER_EVENT(ClientProxy, clipboardChanged)
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// ClientProxyUnknown
|
// ClientProxyUnknown
|
||||||
|
@ -175,7 +174,6 @@ REGISTER_EVENT(IPrimaryScreen, fakeInputEnd)
|
||||||
|
|
||||||
REGISTER_EVENT(IScreen, error)
|
REGISTER_EVENT(IScreen, error)
|
||||||
REGISTER_EVENT(IScreen, shapeChanged)
|
REGISTER_EVENT(IScreen, shapeChanged)
|
||||||
REGISTER_EVENT(IScreen, clipboardGrabbed)
|
|
||||||
REGISTER_EVENT(IScreen, suspend)
|
REGISTER_EVENT(IScreen, suspend)
|
||||||
REGISTER_EVENT(IScreen, resume)
|
REGISTER_EVENT(IScreen, resume)
|
||||||
REGISTER_EVENT(IScreen, fileChunkSending)
|
REGISTER_EVENT(IScreen, fileChunkSending)
|
||||||
|
@ -187,3 +185,11 @@ REGISTER_EVENT(IScreen, fileRecieveCompleted)
|
||||||
|
|
||||||
REGISTER_EVENT(IpcServer, clientConnected)
|
REGISTER_EVENT(IpcServer, clientConnected)
|
||||||
REGISTER_EVENT(IpcServer, messageReceived)
|
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:
|
public:
|
||||||
ClientProxyEvents() :
|
ClientProxyEvents() :
|
||||||
m_ready(Event::kUnknown),
|
m_ready(Event::kUnknown),
|
||||||
m_disconnected(Event::kUnknown),
|
m_disconnected(Event::kUnknown) { }
|
||||||
m_clipboardChanged(Event::kUnknown) { }
|
|
||||||
|
|
||||||
//! @name accessors
|
//! @name accessors
|
||||||
//@{
|
//@{
|
||||||
|
@ -371,20 +370,11 @@ public:
|
||||||
*/
|
*/
|
||||||
Event::Type disconnected();
|
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:
|
private:
|
||||||
Event::Type m_ready;
|
Event::Type m_ready;
|
||||||
Event::Type m_disconnected;
|
Event::Type m_disconnected;
|
||||||
Event::Type m_clipboardChanged;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClientProxyUnknownEvents : public EventTypes {
|
class ClientProxyUnknownEvents : public EventTypes {
|
||||||
|
@ -634,7 +624,6 @@ public:
|
||||||
IScreenEvents() :
|
IScreenEvents() :
|
||||||
m_error(Event::kUnknown),
|
m_error(Event::kUnknown),
|
||||||
m_shapeChanged(Event::kUnknown),
|
m_shapeChanged(Event::kUnknown),
|
||||||
m_clipboardGrabbed(Event::kUnknown),
|
|
||||||
m_suspend(Event::kUnknown),
|
m_suspend(Event::kUnknown),
|
||||||
m_resume(Event::kUnknown),
|
m_resume(Event::kUnknown),
|
||||||
m_fileChunkSending(Event::kUnknown),
|
m_fileChunkSending(Event::kUnknown),
|
||||||
|
@ -657,14 +646,6 @@ public:
|
||||||
*/
|
*/
|
||||||
Event::Type shapeChanged();
|
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
|
//! Get suspend event type
|
||||||
/*!
|
/*!
|
||||||
Returns the suspend event type. This is sent whenever the system goes
|
Returns the suspend event type. This is sent whenever the system goes
|
||||||
|
@ -690,9 +671,49 @@ public:
|
||||||
private:
|
private:
|
||||||
Event::Type m_error;
|
Event::Type m_error;
|
||||||
Event::Type m_shapeChanged;
|
Event::Type m_shapeChanged;
|
||||||
Event::Type m_clipboardGrabbed;
|
|
||||||
Event::Type m_suspend;
|
Event::Type m_suspend;
|
||||||
Event::Type m_resume;
|
Event::Type m_resume;
|
||||||
Event::Type m_fileChunkSending;
|
Event::Type m_fileChunkSending;
|
||||||
Event::Type m_fileRecieveCompleted;
|
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 IKeyStateEvents;
|
||||||
class IPrimaryScreenEvents;
|
class IPrimaryScreenEvents;
|
||||||
class IScreenEvents;
|
class IScreenEvents;
|
||||||
|
class ClipboardEvents;
|
||||||
|
|
||||||
//! Event queue interface
|
//! Event queue interface
|
||||||
/*!
|
/*!
|
||||||
|
@ -244,4 +245,5 @@ public:
|
||||||
virtual IKeyStateEvents& forIKeyState() = 0;
|
virtual IKeyStateEvents& forIKeyState() = 0;
|
||||||
virtual IPrimaryScreenEvents& forIPrimaryScreen() = 0;
|
virtual IPrimaryScreenEvents& forIPrimaryScreen() = 0;
|
||||||
virtual IScreenEvents& forIScreen() = 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());
|
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
|
// CaselessCmp
|
||||||
//
|
//
|
||||||
|
|
|
@ -84,10 +84,21 @@ void uppercase(String& subject);
|
||||||
|
|
||||||
//! Remove all specific char in suject
|
//! 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);
|
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
|
//! Case-insensitive comparisons
|
||||||
/*!
|
/*!
|
||||||
|
|
|
@ -22,12 +22,13 @@
|
||||||
#include "client/ServerProxy.h"
|
#include "client/ServerProxy.h"
|
||||||
#include "synergy/Screen.h"
|
#include "synergy/Screen.h"
|
||||||
#include "synergy/Clipboard.h"
|
#include "synergy/Clipboard.h"
|
||||||
|
#include "synergy/FileChunk.h"
|
||||||
#include "synergy/DropHelper.h"
|
#include "synergy/DropHelper.h"
|
||||||
#include "synergy/PacketStreamFilter.h"
|
#include "synergy/PacketStreamFilter.h"
|
||||||
#include "synergy/ProtocolUtil.h"
|
#include "synergy/ProtocolUtil.h"
|
||||||
#include "synergy/protocol_types.h"
|
#include "synergy/protocol_types.h"
|
||||||
#include "synergy/XSynergy.h"
|
#include "synergy/XSynergy.h"
|
||||||
#include "synergy/FileChunker.h"
|
#include "synergy/StreamChunker.h"
|
||||||
#include "synergy/IPlatformScreen.h"
|
#include "synergy/IPlatformScreen.h"
|
||||||
#include "mt/Thread.h"
|
#include "mt/Thread.h"
|
||||||
#include "net/TCPSocket.h"
|
#include "net/TCPSocket.h"
|
||||||
|
@ -78,7 +79,8 @@ Client::Client(
|
||||||
m_writeToDropDirThread(NULL),
|
m_writeToDropDirThread(NULL),
|
||||||
m_socket(NULL),
|
m_socket(NULL),
|
||||||
m_useSecureNetwork(false),
|
m_useSecureNetwork(false),
|
||||||
m_args(args)
|
m_args(args),
|
||||||
|
m_sendClipboardThread(NULL)
|
||||||
{
|
{
|
||||||
assert(m_socketFactory != NULL);
|
assert(m_socketFactory != NULL);
|
||||||
assert(m_screen != NULL);
|
assert(m_screen != NULL);
|
||||||
|
@ -263,13 +265,16 @@ Client::leave()
|
||||||
m_screen->leave();
|
m_screen->leave();
|
||||||
|
|
||||||
m_active = false;
|
m_active = false;
|
||||||
|
|
||||||
// send clipboards that we own and that have changed
|
if (m_sendClipboardThread != NULL) {
|
||||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
StreamChunker::interruptClipboard();
|
||||||
if (m_ownClipboard[id]) {
|
|
||||||
sendClipboard(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_sendClipboardThread = new Thread(
|
||||||
|
new TMethodJob<Client>(
|
||||||
|
this,
|
||||||
|
&Client::sendClipboardThread,
|
||||||
|
NULL));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -422,12 +427,12 @@ Client::sendConnectionFailedEvent(const char* msg)
|
||||||
void
|
void
|
||||||
Client::sendFileChunk(const void* data)
|
Client::sendFileChunk(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 "sendFileChunk"));
|
LOG((CLOG_DEBUG1 "send file chunk"));
|
||||||
assert(m_server != NULL);
|
assert(m_server != NULL);
|
||||||
|
|
||||||
// relay
|
// 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
|
void
|
||||||
|
@ -487,7 +492,7 @@ Client::setupScreen()
|
||||||
getEventTarget(),
|
getEventTarget(),
|
||||||
new TMethodEventJob<Client>(this,
|
new TMethodEventJob<Client>(this,
|
||||||
&Client::handleShapeChanged));
|
&Client::handleShapeChanged));
|
||||||
m_events->adoptHandler(m_events->forIScreen().clipboardGrabbed(),
|
m_events->adoptHandler(m_events->forClipboard().clipboardGrabbed(),
|
||||||
getEventTarget(),
|
getEventTarget(),
|
||||||
new TMethodEventJob<Client>(this,
|
new TMethodEventJob<Client>(this,
|
||||||
&Client::handleClipboardGrabbed));
|
&Client::handleClipboardGrabbed));
|
||||||
|
@ -545,7 +550,7 @@ Client::cleanupScreen()
|
||||||
}
|
}
|
||||||
m_events->removeHandler(m_events->forIScreen().shapeChanged(),
|
m_events->removeHandler(m_events->forIScreen().shapeChanged(),
|
||||||
getEventTarget());
|
getEventTarget());
|
||||||
m_events->removeHandler(m_events->forIScreen().clipboardGrabbed(),
|
m_events->removeHandler(m_events->forClipboard().clipboardGrabbed(),
|
||||||
getEventTarget());
|
getEventTarget());
|
||||||
delete m_server;
|
delete m_server;
|
||||||
m_server = NULL;
|
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
|
void
|
||||||
Client::handleStopRetry(const Event&, void*)
|
Client::handleStopRetry(const Event&, void*)
|
||||||
{
|
{
|
||||||
|
@ -768,25 +784,6 @@ Client::writeToDropDirThread(void*)
|
||||||
m_receivedFileData);
|
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
|
void
|
||||||
Client::dragInfoReceived(UInt32 fileNum, String data)
|
Client::dragInfoReceived(UInt32 fileNum, String data)
|
||||||
{
|
{
|
||||||
|
@ -810,6 +807,10 @@ Client::isReceivedFileSizeValid()
|
||||||
void
|
void
|
||||||
Client::sendFileToServer(const char* filename)
|
Client::sendFileToServer(const char* filename)
|
||||||
{
|
{
|
||||||
|
if (m_sendFileThread != NULL) {
|
||||||
|
StreamChunker::interruptFile();
|
||||||
|
}
|
||||||
|
|
||||||
m_sendFileThread = new Thread(
|
m_sendFileThread = new Thread(
|
||||||
new TMethodJob<Client>(
|
new TMethodJob<Client>(
|
||||||
this, &Client::sendFileThread,
|
this, &Client::sendFileThread,
|
||||||
|
@ -821,7 +822,7 @@ Client::sendFileThread(void* filename)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
char* name = reinterpret_cast<char*>(filename);
|
char* name = reinterpret_cast<char*>(filename);
|
||||||
FileChunker::sendFileChunks(name, m_events, this);
|
StreamChunker::sendFile(name, m_events, this);
|
||||||
}
|
}
|
||||||
catch (std::runtime_error error) {
|
catch (std::runtime_error error) {
|
||||||
LOG((CLOG_ERR "failed sending file chunks: %s", error.what()));
|
LOG((CLOG_ERR "failed sending file chunks: %s", error.what()));
|
||||||
|
|
|
@ -85,15 +85,6 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void handshakeComplete();
|
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
|
//! Received drag information
|
||||||
void dragInfoReceived(UInt32 fileNum, String data);
|
void dragInfoReceived(UInt32 fileNum, String data);
|
||||||
|
|
||||||
|
@ -131,7 +122,10 @@ public:
|
||||||
bool isReceivedFileSizeValid();
|
bool isReceivedFileSizeValid();
|
||||||
|
|
||||||
//! Return expected file size
|
//! 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 handleFileRecieveCompleted(const Event&, void*);
|
||||||
void handleStopRetry(const Event&, void*);
|
void handleStopRetry(const Event&, void*);
|
||||||
void onFileRecieveCompleted();
|
void onFileRecieveCompleted();
|
||||||
|
void sendClipboardThread(void*);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool m_mock;
|
bool m_mock;
|
||||||
|
@ -224,4 +219,5 @@ private:
|
||||||
TCPSocket* m_socket;
|
TCPSocket* m_socket;
|
||||||
bool m_useSecureNetwork;
|
bool m_useSecureNetwork;
|
||||||
ClientArgs& m_args;
|
ClientArgs& m_args;
|
||||||
|
Thread* m_sendClipboardThread;
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
#include "client/ServerProxy.h"
|
#include "client/ServerProxy.h"
|
||||||
|
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
|
#include "synergy/FileChunk.h"
|
||||||
|
#include "synergy/ClipboardChunk.h"
|
||||||
|
#include "synergy/StreamChunker.h"
|
||||||
#include "synergy/Clipboard.h"
|
#include "synergy/Clipboard.h"
|
||||||
#include "synergy/ProtocolUtil.h"
|
#include "synergy/ProtocolUtil.h"
|
||||||
#include "synergy/option_types.h"
|
#include "synergy/option_types.h"
|
||||||
|
@ -35,8 +38,6 @@
|
||||||
// ServerProxy
|
// ServerProxy
|
||||||
//
|
//
|
||||||
|
|
||||||
const UInt16 ServerProxy::m_intervalThreshold = 1;
|
|
||||||
|
|
||||||
ServerProxy::ServerProxy(Client* client, synergy::IStream* stream, IEventQueue* events) :
|
ServerProxy::ServerProxy(Client* client, synergy::IStream* stream, IEventQueue* events) :
|
||||||
m_client(client),
|
m_client(client),
|
||||||
m_stream(stream),
|
m_stream(stream),
|
||||||
|
@ -51,10 +52,7 @@ ServerProxy::ServerProxy(Client* client, synergy::IStream* stream, IEventQueue*
|
||||||
m_keepAliveAlarm(0.0),
|
m_keepAliveAlarm(0.0),
|
||||||
m_keepAliveAlarmTimer(NULL),
|
m_keepAliveAlarmTimer(NULL),
|
||||||
m_parser(&ServerProxy::parseHandshakeMessage),
|
m_parser(&ServerProxy::parseHandshakeMessage),
|
||||||
m_events(events),
|
m_events(events)
|
||||||
m_stopwatch(true),
|
|
||||||
m_elapsedTime(0),
|
|
||||||
m_receivedDataSize(0)
|
|
||||||
{
|
{
|
||||||
assert(m_client != NULL);
|
assert(m_client != NULL);
|
||||||
assert(m_stream != NULL);
|
assert(m_stream != NULL);
|
||||||
|
@ -69,6 +67,11 @@ ServerProxy::ServerProxy(Client* client, synergy::IStream* stream, IEventQueue*
|
||||||
new TMethodEventJob<ServerProxy>(this,
|
new TMethodEventJob<ServerProxy>(this,
|
||||||
&ServerProxy::handleData));
|
&ServerProxy::handleData));
|
||||||
|
|
||||||
|
m_events->adoptHandler(m_events->forClipboard().clipboardSending(),
|
||||||
|
this,
|
||||||
|
new TMethodEventJob<ServerProxy>(this,
|
||||||
|
&ServerProxy::handleClipboardSendingEvent));
|
||||||
|
|
||||||
// send heartbeat
|
// send heartbeat
|
||||||
setKeepAliveRate(kKeepAliveRate);
|
setKeepAliveRate(kKeepAliveRate);
|
||||||
}
|
}
|
||||||
|
@ -358,8 +361,11 @@ void
|
||||||
ServerProxy::onClipboardChanged(ClipboardID id, const IClipboard* clipboard)
|
ServerProxy::onClipboardChanged(ClipboardID id, const IClipboard* clipboard)
|
||||||
{
|
{
|
||||||
String data = IClipboard::marshall(clipboard);
|
String data = IClipboard::marshall(clipboard);
|
||||||
LOG((CLOG_DEBUG1 "sending clipboard %d seqnum=%d, size=%d", id, m_seqNum, data.size()));
|
LOG((CLOG_DEBUG "sending clipboard %d seqnum=%d", id, m_seqNum));
|
||||||
ProtocolUtil::writef(m_stream, kMsgDClipboard, id, m_seqNum, &data);
|
|
||||||
|
StreamChunker::sendClipboard(data, data.size(), id, m_seqNum, m_events, this);
|
||||||
|
|
||||||
|
LOG((CLOG_DEBUG "sent clipboard size=%d", data.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -545,21 +551,20 @@ void
|
||||||
ServerProxy::setClipboard()
|
ServerProxy::setClipboard()
|
||||||
{
|
{
|
||||||
// parse
|
// parse
|
||||||
|
static String dataCached;
|
||||||
ClipboardID id;
|
ClipboardID id;
|
||||||
UInt32 seqNum;
|
UInt32 seq;
|
||||||
String data;
|
|
||||||
ProtocolUtil::readf(m_stream, kMsgDClipboard + 4, &id, &seqNum, &data);
|
int r = ClipboardChunk::assemble(m_stream, dataCached, id, seq);
|
||||||
LOG((CLOG_DEBUG "recv clipboard %d size=%d", id, data.size()));
|
|
||||||
|
|
||||||
// validate
|
if (r == kFinish) {
|
||||||
if (id >= kClipboardEnd) {
|
LOG((CLOG_DEBUG "received clipboard %d size=%d", id, dataCached.size()));
|
||||||
return;
|
|
||||||
|
// forward
|
||||||
|
Clipboard clipboard;
|
||||||
|
clipboard.unmarshall(dataCached, 0);
|
||||||
|
m_client->setClipboard(id, &clipboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
// forward
|
|
||||||
Clipboard clipboard;
|
|
||||||
clipboard.unmarshall(data, 0);
|
|
||||||
m_client->setClipboard(id, &clipboard);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -851,50 +856,13 @@ ServerProxy::infoAcknowledgment()
|
||||||
void
|
void
|
||||||
ServerProxy::fileChunkReceived()
|
ServerProxy::fileChunkReceived()
|
||||||
{
|
{
|
||||||
// parse
|
int result = FileChunk::assemble(
|
||||||
UInt8 mark = 0;
|
m_stream,
|
||||||
String content;
|
m_client->getReceivedFileData(),
|
||||||
ProtocolUtil::readf(m_stream, kMsgDFileTransfer + 4, &mark, &content);
|
m_client->getExpectedFileSize());
|
||||||
|
|
||||||
switch (mark) {
|
if (result == kFinish) {
|
||||||
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:
|
|
||||||
m_events->addEvent(Event(m_events->forIScreen().fileRecieveCompleted(), m_client));
|
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);
|
m_client->dragInfoReceived(fileNum, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ServerProxy::handleClipboardSendingEvent(const Event& event, void*)
|
||||||
|
{
|
||||||
|
ClipboardChunk::send(m_stream, event.getData());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ServerProxy::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
|
ServerProxy::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
|
||||||
{
|
{
|
||||||
String chunk(data, dataSize);
|
FileChunk::send(m_stream, mark, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -106,6 +106,7 @@ private:
|
||||||
void infoAcknowledgment();
|
void infoAcknowledgment();
|
||||||
void fileChunkReceived();
|
void fileChunkReceived();
|
||||||
void dragInfoReceived();
|
void dragInfoReceived();
|
||||||
|
void handleClipboardSendingEvent(const Event&, void*);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef EResult (ServerProxy::*MessageParser)(const UInt8*);
|
typedef EResult (ServerProxy::*MessageParser)(const UInt8*);
|
||||||
|
@ -129,9 +130,4 @@ private:
|
||||||
|
|
||||||
MessageParser m_parser;
|
MessageParser m_parser;
|
||||||
IEventQueue* m_events;
|
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()) {
|
if (m_ownClipboard && !MSWindowsClipboard::isOwnedBySynergy()) {
|
||||||
LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received"));
|
LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received"));
|
||||||
m_ownClipboard = false;
|
m_ownClipboard = false;
|
||||||
sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardClipboard);
|
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard);
|
||||||
sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardSelection);
|
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1501,8 +1501,8 @@ MSWindowsScreen::onClipboardChange()
|
||||||
if (m_ownClipboard) {
|
if (m_ownClipboard) {
|
||||||
LOG((CLOG_DEBUG "clipboard changed: lost ownership"));
|
LOG((CLOG_DEBUG "clipboard changed: lost ownership"));
|
||||||
m_ownClipboard = false;
|
m_ownClipboard = false;
|
||||||
sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardClipboard);
|
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard);
|
||||||
sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardSelection);
|
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!m_ownClipboard) {
|
else if (!m_ownClipboard) {
|
||||||
|
|
|
@ -954,8 +954,8 @@ OSXScreen::checkClipboards()
|
||||||
LOG((CLOG_DEBUG2 "checking clipboard"));
|
LOG((CLOG_DEBUG2 "checking clipboard"));
|
||||||
if (m_pasteboard.synchronize()) {
|
if (m_pasteboard.synchronize()) {
|
||||||
LOG((CLOG_DEBUG "clipboard changed"));
|
LOG((CLOG_DEBUG "clipboard changed"));
|
||||||
sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardClipboard);
|
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard);
|
||||||
sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), kClipboardSelection);
|
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1325,7 +1325,7 @@ XWindowsScreen::handleSystemEvent(const Event& event, void*)
|
||||||
if (id != kClipboardEnd) {
|
if (id != kClipboardEnd) {
|
||||||
LOG((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time));
|
LOG((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time));
|
||||||
m_clipboard[id]->lost(xevent->xselectionclear.time);
|
m_clipboard[id]->lost(xevent->xselectionclear.time);
|
||||||
sendClipboardEvent(m_events->forIScreen().clipboardGrabbed(), id);
|
sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ if (WIN32)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
# 10.7 should be supported, but gives is a _NXArgv linker error
|
||||||
if (OSX_TARGET_MINOR GREATER 7)
|
if (OSX_TARGET_MINOR GREATER 7)
|
||||||
add_subdirectory(ns)
|
add_subdirectory(ns)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -85,18 +85,20 @@ if (WIN32)
|
||||||
..\\..\\..\\..\\..\\ext\\${OPENSSL_PLAT_DIR}\\out32dll\\ssleay32.*
|
..\\..\\..\\..\\..\\ext\\${OPENSSL_PLAT_DIR}\\out32dll\\ssleay32.*
|
||||||
..\\..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR}
|
..\\..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR}
|
||||||
)
|
)
|
||||||
else()
|
endif()
|
||||||
|
|
||||||
|
if (UNIX)
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET ns
|
TARGET ns
|
||||||
POST_BUILD
|
POST_BUILD
|
||||||
COMMAND
|
COMMAND
|
||||||
mkdir -p
|
mkdir -p
|
||||||
${CMAKE_SOURCE_DIR}/bin/plugins
|
${CMAKE_SOURCE_DIR}/bin/${CMAKE_CFG_INTDIR}/plugins
|
||||||
&&
|
&&
|
||||||
cp
|
cp
|
||||||
${CMAKE_SOURCE_DIR}/lib/${CMAKE_CFG_INTDIR}/libns.*
|
${CMAKE_SOURCE_DIR}/lib/${CMAKE_CFG_INTDIR}/libns.*
|
||||||
${CMAKE_SOURCE_DIR}/bin/plugins/
|
${CMAKE_SOURCE_DIR}/bin/${CMAKE_CFG_INTDIR}/plugins/
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL Debug)
|
if (CMAKE_BUILD_TYPE STREQUAL Debug)
|
||||||
|
@ -104,11 +106,11 @@ else()
|
||||||
TARGET ns
|
TARGET ns
|
||||||
POST_BUILD
|
POST_BUILD
|
||||||
COMMAND mkdir -p
|
COMMAND mkdir -p
|
||||||
${CMAKE_SOURCE_DIR}/bin/plugins
|
${CMAKE_SOURCE_DIR}/bin/debug/plugins
|
||||||
&&
|
&&
|
||||||
cp
|
cp
|
||||||
${CMAKE_SOURCE_DIR}/lib/debug/libns.*
|
${CMAKE_SOURCE_DIR}/lib/debug/libns.*
|
||||||
${CMAKE_SOURCE_DIR}/bin/plugins/
|
${CMAKE_SOURCE_DIR}/bin/debug/plugins/
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
|
|
|
@ -35,7 +35,12 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#define MAX_ERROR_SIZE 65535
|
#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 kFingerprintDirName[] = "SSL/Fingerprints";
|
||||||
//static const char kFingerprintLocalFilename[] = "Local.txt";
|
//static const char kFingerprintLocalFilename[] = "Local.txt";
|
||||||
|
@ -52,8 +57,8 @@ SecureSocket::SecureSocket(
|
||||||
SocketMultiplexer* socketMultiplexer) :
|
SocketMultiplexer* socketMultiplexer) :
|
||||||
TCPSocket(events, socketMultiplexer),
|
TCPSocket(events, socketMultiplexer),
|
||||||
m_secureReady(false),
|
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) :
|
ArchSocket socket) :
|
||||||
TCPSocket(events, socketMultiplexer, socket),
|
TCPSocket(events, socketMultiplexer, socket),
|
||||||
m_secureReady(false),
|
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:
|
case SSL_ERROR_ZERO_RETURN:
|
||||||
// connection closed
|
// connection closed
|
||||||
isFatal(true);
|
isFatal(true);
|
||||||
LOG((CLOG_DEBUG "SSL connection has been closed"));
|
LOG((CLOG_DEBUG "ssl connection closed"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SSL_ERROR_WANT_READ:
|
case SSL_ERROR_WANT_READ:
|
||||||
case SSL_ERROR_WANT_WRITE:
|
case SSL_ERROR_WANT_WRITE:
|
||||||
case SSL_ERROR_WANT_CONNECT:
|
case SSL_ERROR_WANT_CONNECT:
|
||||||
case SSL_ERROR_WANT_ACCEPT:
|
case SSL_ERROR_WANT_ACCEPT:
|
||||||
retry += 1;
|
// it seems like these sort of errors are part of openssl's normal behavior,
|
||||||
// If there are a lot of retrys, it's worth warning about
|
// so we should expect a very high amount of these. sleeping doesn't seem to
|
||||||
if ( retry % 5 == 0 ) {
|
// help... maybe you just have to swallow the errors (yuck).
|
||||||
LOG((CLOG_DEBUG "need to retry the same SSL function(%d) retry:%d", errorCode, retry));
|
retry++;
|
||||||
}
|
LOG((CLOG_DEBUG2 "passive ssl error, error=%d, attempt=%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));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SSL_ERROR_SYSCALL:
|
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 (ERR_peek_error() == 0) {
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
LOG((CLOG_ERR "an EOF violates the protocol"));
|
LOG((CLOG_ERR "eof violates ssl protocol"));
|
||||||
}
|
}
|
||||||
else if (status == -1) {
|
else if (status == -1) {
|
||||||
// underlying socket I/O reproted an error
|
// underlying socket I/O reproted an error
|
||||||
|
@ -433,19 +432,19 @@ SecureSocket::checkResult(int status, int& retry)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SSL_ERROR_SSL:
|
case SSL_ERROR_SSL:
|
||||||
LOG((CLOG_ERR "a failure in the SSL library occurred"));
|
LOG((CLOG_ERR "ssl error occurred (generic failure)"));
|
||||||
isFatal(true);
|
isFatal(true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LOG((CLOG_ERR "unknown secure socket error"));
|
LOG((CLOG_ERR "ssl error occurred (unknown failure)"));
|
||||||
isFatal(true);
|
isFatal(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the retry max would exceed the allowed, treat it as a fatal error
|
// If the retry max would exceed the allowed, treat it as a fatal error
|
||||||
if (retry > maxRetry()) {
|
if (retry > maxRetry()) {
|
||||||
LOG((CLOG_ERR "Maximum retry count exceeded:%d",retry));
|
LOG((CLOG_ERR "passive ssl error limit exceeded: %d", retry));
|
||||||
isFatal(true);
|
isFatal(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,9 @@ public:
|
||||||
//! Get server which owns this listener
|
//! Get server which owns this listener
|
||||||
Server* getServer() { return m_server; }
|
Server* getServer() { return m_server; }
|
||||||
|
|
||||||
|
//! Return true if using secure network connection
|
||||||
|
bool isSecure() { return m_useSecureNetwork; }
|
||||||
|
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -275,16 +275,7 @@ ClientProxy1_0::leave()
|
||||||
void
|
void
|
||||||
ClientProxy1_0::setClipboard(ClipboardID id, const IClipboard* clipboard)
|
ClientProxy1_0::setClipboard(ClipboardID id, const IClipboard* clipboard)
|
||||||
{
|
{
|
||||||
// ignore if this clipboard is already clean
|
// ignore -- deprecated in protocol 1.0
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -450,33 +441,8 @@ ClientProxy1_0::recvInfo()
|
||||||
bool
|
bool
|
||||||
ClientProxy1_0::recvClipboard()
|
ClientProxy1_0::recvClipboard()
|
||||||
{
|
{
|
||||||
// parse message
|
// deprecated in protocol 1.0
|
||||||
ClipboardID id;
|
return false;
|
||||||
UInt32 seqNum;
|
|
||||||
String data;
|
|
||||||
if (!ProtocolUtil::readf(getStream(),
|
|
||||||
kMsgDClipboard + 4, &id, &seqNum, &data)) {
|
|
||||||
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
|
bool
|
||||||
|
@ -499,13 +465,12 @@ ClientProxy1_0::recvGrabClipboard()
|
||||||
ClipboardInfo* info = new ClipboardInfo;
|
ClipboardInfo* info = new ClipboardInfo;
|
||||||
info->m_id = id;
|
info->m_id = id;
|
||||||
info->m_sequenceNumber = seqNum;
|
info->m_sequenceNumber = seqNum;
|
||||||
m_events->addEvent(Event(m_events->forIScreen().clipboardGrabbed(),
|
m_events->addEvent(Event(m_events->forClipboard().clipboardGrabbed(),
|
||||||
getEventTarget(), info));
|
getEventTarget(), info));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// ClientProxy1_0::ClientClipboard
|
// ClientProxy1_0::ClientClipboard
|
||||||
//
|
//
|
||||||
|
|
|
@ -70,7 +70,7 @@ protected:
|
||||||
virtual void resetHeartbeatTimer();
|
virtual void resetHeartbeatTimer();
|
||||||
virtual void addHeartbeatTimer();
|
virtual void addHeartbeatTimer();
|
||||||
virtual void removeHeartbeatTimer();
|
virtual void removeHeartbeatTimer();
|
||||||
|
virtual bool recvClipboard();
|
||||||
private:
|
private:
|
||||||
void disconnect();
|
void disconnect();
|
||||||
void removeHandlers();
|
void removeHandlers();
|
||||||
|
@ -81,11 +81,9 @@ private:
|
||||||
void handleFlatline(const Event&, void*);
|
void handleFlatline(const Event&, void*);
|
||||||
|
|
||||||
bool recvInfo();
|
bool recvInfo();
|
||||||
bool recvClipboard();
|
|
||||||
bool recvGrabClipboard();
|
bool recvGrabClipboard();
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
typedef bool (ClientProxy1_0::*MessageParser)(const UInt8*);
|
|
||||||
struct ClientClipboard {
|
struct ClientClipboard {
|
||||||
public:
|
public:
|
||||||
ClientClipboard();
|
ClientClipboard();
|
||||||
|
@ -96,8 +94,12 @@ private:
|
||||||
bool m_dirty;
|
bool m_dirty;
|
||||||
};
|
};
|
||||||
|
|
||||||
ClientInfo m_info;
|
|
||||||
ClientClipboard m_clipboard[kClipboardEnd];
|
ClientClipboard m_clipboard[kClipboardEnd];
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef bool (ClientProxy1_0::*MessageParser)(const UInt8*);
|
||||||
|
|
||||||
|
ClientInfo m_info;
|
||||||
double m_heartbeatAlarm;
|
double m_heartbeatAlarm;
|
||||||
EventQueueTimer* m_heartbeatTimer;
|
EventQueueTimer* m_heartbeatTimer;
|
||||||
MessageParser m_parser;
|
MessageParser m_parser;
|
||||||
|
|
|
@ -18,22 +18,21 @@
|
||||||
#include "server/ClientProxy1_5.h"
|
#include "server/ClientProxy1_5.h"
|
||||||
|
|
||||||
#include "server/Server.h"
|
#include "server/Server.h"
|
||||||
|
#include "synergy/FileChunk.h"
|
||||||
|
#include "synergy/StreamChunker.h"
|
||||||
#include "synergy/ProtocolUtil.h"
|
#include "synergy/ProtocolUtil.h"
|
||||||
#include "io/IStream.h"
|
#include "io/IStream.h"
|
||||||
#include "base/Log.h"
|
#include "base/Log.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
//
|
//
|
||||||
// ClientProxy1_5
|
// ClientProxy1_5
|
||||||
//
|
//
|
||||||
|
|
||||||
const UInt16 ClientProxy1_5::m_intervalThreshold = 1;
|
|
||||||
|
|
||||||
ClientProxy1_5::ClientProxy1_5(const String& name, synergy::IStream* stream, Server* server, IEventQueue* events) :
|
ClientProxy1_5::ClientProxy1_5(const String& name, synergy::IStream* stream, Server* server, IEventQueue* events) :
|
||||||
ClientProxy1_4(name, stream, server, events),
|
ClientProxy1_4(name, stream, server, events),
|
||||||
m_events(events),
|
m_events(events)
|
||||||
m_stopwatch(true),
|
|
||||||
m_elapsedTime(0),
|
|
||||||
m_receivedDataSize(0)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,23 +51,7 @@ ClientProxy1_5::sendDragInfo(UInt32 fileCount, const char* info, size_t size)
|
||||||
void
|
void
|
||||||
ClientProxy1_5::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
|
ClientProxy1_5::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
|
||||||
{
|
{
|
||||||
String chunk(data, dataSize);
|
FileChunk::send(getStream(), mark, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -90,51 +73,15 @@ ClientProxy1_5::parseMessage(const UInt8* code)
|
||||||
void
|
void
|
||||||
ClientProxy1_5::fileChunkReceived()
|
ClientProxy1_5::fileChunkReceived()
|
||||||
{
|
{
|
||||||
// parse
|
|
||||||
UInt8 mark = 0;
|
|
||||||
String content;
|
|
||||||
ProtocolUtil::readf(getStream(), kMsgDFileTransfer + 4, &mark, &content);
|
|
||||||
|
|
||||||
Server* server = getServer();
|
Server* server = getServer();
|
||||||
switch (mark) {
|
int result = FileChunk::assemble(
|
||||||
case kFileStart:
|
getStream(),
|
||||||
server->clearReceivedFileData();
|
server->getReceivedFileData(),
|
||||||
server->setExpectedFileSize(content);
|
server->getExpectedFileSize());
|
||||||
if (CLOG->getFilter() >= kDEBUG2) {
|
|
||||||
LOG((CLOG_DEBUG2 "recv file data from client: file size=%s", content.c_str()));
|
|
||||||
m_stopwatch.start();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kFileChunk:
|
if (result == kFinish) {
|
||||||
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:
|
|
||||||
m_events->addEvent(Event(m_events->forIScreen().fileRecieveCompleted(), server));
|
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 "server/ClientProxy1_4.h"
|
||||||
#include "base/Stopwatch.h"
|
#include "base/Stopwatch.h"
|
||||||
|
#include "common/stdvector.h"
|
||||||
|
|
||||||
class Server;
|
class Server;
|
||||||
class IEventQueue;
|
class IEventQueue;
|
||||||
|
@ -37,9 +38,4 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IEventQueue* m_events;
|
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
|
* 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
|
* This package is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -17,30 +17,23 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "base/String.h"
|
#include "server/ClientProxy1_5.h"
|
||||||
|
|
||||||
|
class Server;
|
||||||
class IEventQueue;
|
class IEventQueue;
|
||||||
|
|
||||||
class FileChunker {
|
//! Proxy for client implementing protocol version 1.6
|
||||||
|
class ClientProxy1_6 : public ClientProxy1_5 {
|
||||||
public:
|
public:
|
||||||
//! FileChunk data
|
ClientProxy1_6(const String& name, synergy::IStream* adoptedStream, Server* server, IEventQueue* events);
|
||||||
class FileChunk {
|
~ClientProxy1_6();
|
||||||
public:
|
|
||||||
FileChunk(size_t chunkSize) : m_dataSize(chunkSize - 2)
|
|
||||||
{
|
|
||||||
m_chunk = new char[chunkSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
~FileChunk() { delete[] m_chunk; }
|
virtual void setClipboard(ClipboardID id, const IClipboard* clipboard);
|
||||||
|
virtual bool recvClipboard();
|
||||||
public:
|
|
||||||
const size_t m_dataSize;
|
|
||||||
char* m_chunk;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void sendFileChunks(char* filename, IEventQueue* events, void* eventTarget);
|
|
||||||
static String intToString(size_t i);
|
|
||||||
|
|
||||||
private:
|
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_3.h"
|
||||||
#include "server/ClientProxy1_4.h"
|
#include "server/ClientProxy1_4.h"
|
||||||
#include "server/ClientProxy1_5.h"
|
#include "server/ClientProxy1_5.h"
|
||||||
|
#include "server/ClientProxy1_6.h"
|
||||||
#include "synergy/protocol_types.h"
|
#include "synergy/protocol_types.h"
|
||||||
#include "synergy/ProtocolUtil.h"
|
#include "synergy/ProtocolUtil.h"
|
||||||
#include "synergy/XSynergy.h"
|
#include "synergy/XSynergy.h"
|
||||||
|
@ -227,6 +228,10 @@ ClientProxyUnknown::handleData(const Event&, void*)
|
||||||
case 5:
|
case 5:
|
||||||
m_proxy = new ClientProxy1_5(name, m_stream, m_server, m_events);
|
m_proxy = new ClientProxy1_5(name, m_stream, m_server, m_events);
|
||||||
break;
|
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/ClientProxyUnknown.h"
|
||||||
#include "server/PrimaryClient.h"
|
#include "server/PrimaryClient.h"
|
||||||
#include "server/ClientListener.h"
|
#include "server/ClientListener.h"
|
||||||
|
#include "synergy/FileChunk.h"
|
||||||
#include "synergy/IPlatformScreen.h"
|
#include "synergy/IPlatformScreen.h"
|
||||||
#include "synergy/DropHelper.h"
|
#include "synergy/DropHelper.h"
|
||||||
#include "synergy/option_types.h"
|
#include "synergy/option_types.h"
|
||||||
#include "synergy/protocol_types.h"
|
#include "synergy/protocol_types.h"
|
||||||
#include "synergy/XScreen.h"
|
#include "synergy/XScreen.h"
|
||||||
#include "synergy/XSynergy.h"
|
#include "synergy/XSynergy.h"
|
||||||
#include "synergy/FileChunker.h"
|
#include "synergy/StreamChunker.h"
|
||||||
#include "synergy/KeyState.h"
|
#include "synergy/KeyState.h"
|
||||||
#include "synergy/Screen.h"
|
#include "synergy/Screen.h"
|
||||||
#include "synergy/PacketStreamFilter.h"
|
#include "synergy/PacketStreamFilter.h"
|
||||||
|
@ -91,7 +92,8 @@ Server::Server(
|
||||||
m_ignoreFileTransfer(false),
|
m_ignoreFileTransfer(false),
|
||||||
m_enableDragDrop(enableDragDrop),
|
m_enableDragDrop(enableDragDrop),
|
||||||
m_getDragInfoThread(NULL),
|
m_getDragInfoThread(NULL),
|
||||||
m_waitDragInfoThread(true)
|
m_waitDragInfoThread(true),
|
||||||
|
m_sendClipboardThread(NULL)
|
||||||
{
|
{
|
||||||
// must have a primary client and it must have a canonical name
|
// must have a primary client and it must have a canonical name
|
||||||
assert(m_primaryClient != NULL);
|
assert(m_primaryClient != NULL);
|
||||||
|
@ -503,11 +505,18 @@ Server::switchScreen(BaseClientProxy* dst,
|
||||||
m_active->enter(x, y, m_seqNum,
|
m_active->enter(x, y, m_seqNum,
|
||||||
m_primaryClient->getToggleMask(),
|
m_primaryClient->getToggleMask(),
|
||||||
forScreensaver);
|
forScreensaver);
|
||||||
|
// if already sending clipboard, we need to interupt it, otherwise
|
||||||
// send the clipboard data to new active screen
|
// clipboard data could be corrupted on the other side
|
||||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
if (m_sendClipboardThread != NULL) {
|
||||||
m_active->setClipboard(id, &m_clipboards[id].m_clipboard);
|
StreamChunker::interruptClipboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send the clipboard data to new active screen
|
||||||
|
m_sendClipboardThread = new Thread(
|
||||||
|
new TMethodJob<Server>(
|
||||||
|
this,
|
||||||
|
&Server::sendClipboardThread,
|
||||||
|
NULL));
|
||||||
|
|
||||||
Server::SwitchToScreenInfo* info =
|
Server::SwitchToScreenInfo* info =
|
||||||
Server::SwitchToScreenInfo::alloc(m_active->getName());
|
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
|
void
|
||||||
Server::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
|
Server::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
|
||||||
{
|
{
|
||||||
|
@ -2023,13 +2040,13 @@ Server::onMouseWheel(SInt32 xDelta, SInt32 yDelta)
|
||||||
void
|
void
|
||||||
Server::onFileChunkSending(const void* data)
|
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);
|
assert(m_active != NULL);
|
||||||
|
|
||||||
// relay
|
// 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
|
void
|
||||||
|
@ -2068,11 +2085,11 @@ Server::addClient(BaseClientProxy* client)
|
||||||
client->getEventTarget(),
|
client->getEventTarget(),
|
||||||
new TMethodEventJob<Server>(this,
|
new TMethodEventJob<Server>(this,
|
||||||
&Server::handleShapeChanged, client));
|
&Server::handleShapeChanged, client));
|
||||||
m_events->adoptHandler(m_events->forIScreen().clipboardGrabbed(),
|
m_events->adoptHandler(m_events->forClipboard().clipboardGrabbed(),
|
||||||
client->getEventTarget(),
|
client->getEventTarget(),
|
||||||
new TMethodEventJob<Server>(this,
|
new TMethodEventJob<Server>(this,
|
||||||
&Server::handleClipboardGrabbed, client));
|
&Server::handleClipboardGrabbed, client));
|
||||||
m_events->adoptHandler(m_events->forClientProxy().clipboardChanged(),
|
m_events->adoptHandler(m_events->forClipboard().clipboardChanged(),
|
||||||
client->getEventTarget(),
|
client->getEventTarget(),
|
||||||
new TMethodEventJob<Server>(this,
|
new TMethodEventJob<Server>(this,
|
||||||
&Server::handleClipboardChanged, client));
|
&Server::handleClipboardChanged, client));
|
||||||
|
@ -2104,9 +2121,9 @@ Server::removeClient(BaseClientProxy* client)
|
||||||
// remove event handlers
|
// remove event handlers
|
||||||
m_events->removeHandler(m_events->forIScreen().shapeChanged(),
|
m_events->removeHandler(m_events->forIScreen().shapeChanged(),
|
||||||
client->getEventTarget());
|
client->getEventTarget());
|
||||||
m_events->removeHandler(m_events->forIScreen().clipboardGrabbed(),
|
m_events->removeHandler(m_events->forClipboard().clipboardGrabbed(),
|
||||||
client->getEventTarget());
|
client->getEventTarget());
|
||||||
m_events->removeHandler(m_events->forClientProxy().clipboardChanged(),
|
m_events->removeHandler(m_events->forClipboard().clipboardChanged(),
|
||||||
client->getEventTarget());
|
client->getEventTarget());
|
||||||
|
|
||||||
// remove from list
|
// remove from list
|
||||||
|
@ -2326,25 +2343,6 @@ Server::KeyboardBroadcastInfo::alloc(State state, const String& screens)
|
||||||
return info;
|
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
|
bool
|
||||||
Server::isReceivedFileSizeValid()
|
Server::isReceivedFileSizeValid()
|
||||||
{
|
{
|
||||||
|
@ -2354,6 +2352,10 @@ Server::isReceivedFileSizeValid()
|
||||||
void
|
void
|
||||||
Server::sendFileToClient(const char* filename)
|
Server::sendFileToClient(const char* filename)
|
||||||
{
|
{
|
||||||
|
if (m_sendFileThread != NULL) {
|
||||||
|
StreamChunker::interruptFile();
|
||||||
|
}
|
||||||
|
|
||||||
m_sendFileThread = new Thread(
|
m_sendFileThread = new Thread(
|
||||||
new TMethodJob<Server>(
|
new TMethodJob<Server>(
|
||||||
this, &Server::sendFileThread,
|
this, &Server::sendFileThread,
|
||||||
|
@ -2366,7 +2368,7 @@ Server::sendFileThread(void* data)
|
||||||
try {
|
try {
|
||||||
char* filename = reinterpret_cast<char*>(data);
|
char* filename = reinterpret_cast<char*>(data);
|
||||||
LOG((CLOG_DEBUG "sending file to client, filename=%s", filename));
|
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) {
|
catch (std::runtime_error error) {
|
||||||
LOG((CLOG_ERR "failed sending file chunks, error: %s", error.what()));
|
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);
|
m_screen->startDraggingFiles(m_dragFileList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Server::isSecure() const
|
||||||
|
{
|
||||||
|
return m_clientListener->isSecure();
|
||||||
|
}
|
||||||
|
|
|
@ -141,15 +141,6 @@ public:
|
||||||
*/
|
*/
|
||||||
void disconnect();
|
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
|
//! Create a new thread and use it to send file to client
|
||||||
void sendFileToClient(const char* filename);
|
void sendFileToClient(const char* filename);
|
||||||
|
|
||||||
|
@ -178,8 +169,14 @@ public:
|
||||||
//! Return true if recieved file size is valid
|
//! Return true if recieved file size is valid
|
||||||
bool isReceivedFileSizeValid();
|
bool isReceivedFileSizeValid();
|
||||||
|
|
||||||
//! Return expected file size
|
//! Return expected file data size
|
||||||
size_t getExpectedFileSize() { return m_expectedFileSize; }
|
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
|
// send drag info to new client screen
|
||||||
void sendDragInfo(BaseClientProxy* newScreen);
|
void sendDragInfo(BaseClientProxy* newScreen);
|
||||||
|
|
||||||
|
// thread funciton for sending clipboard
|
||||||
|
void sendClipboardThread(void*);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool m_mock;
|
bool m_mock;
|
||||||
|
|
||||||
|
@ -480,4 +480,6 @@ private:
|
||||||
bool m_waitDragInfoThread;
|
bool m_waitDragInfoThread;
|
||||||
|
|
||||||
ClientListener* m_clientListener;
|
ClientListener* m_clientListener;
|
||||||
|
|
||||||
|
Thread* m_sendClipboardThread;
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "synergy/ArgParser.h"
|
#include "synergy/ArgParser.h"
|
||||||
|
|
||||||
|
#include "synergy/StreamChunker.h"
|
||||||
#include "synergy/App.h"
|
#include "synergy/App.h"
|
||||||
#include "synergy/ServerArgs.h"
|
#include "synergy/ServerArgs.h"
|
||||||
#include "synergy/ClientArgs.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")) {
|
else if (isArg(i, argc, argv, NULL, "--enable-crypto")) {
|
||||||
argsBase().m_enableCrypto = true;
|
argsBase().m_enableCrypto = true;
|
||||||
|
StreamChunker::updateChunkSize(true);
|
||||||
}
|
}
|
||||||
else if (isArg(i, argc, argv, NULL, "--profile-dir", 1)) {
|
else if (isArg(i, argc, argv, NULL, "--profile-dir", 1)) {
|
||||||
argsBase().m_profileDirectory = argv[++i];
|
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* kMsgDMouseRelMove = "DMRM%2i%2i";
|
||||||
const char* kMsgDMouseWheel = "DMWM%2i%2i";
|
const char* kMsgDMouseWheel = "DMWM%2i%2i";
|
||||||
const char* kMsgDMouseWheel1_0 = "DMWM%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* kMsgDInfo = "DINF%2i%2i%2i%2i%2i%2i%2i";
|
||||||
const char* kMsgDSetOptions = "DSOP%4I";
|
const char* kMsgDSetOptions = "DSOP%4I";
|
||||||
const char* kMsgDFileTransfer = "DFTR%1i%s";
|
const char* kMsgDFileTransfer = "DFTR%1i%s";
|
||||||
|
|
|
@ -28,9 +28,10 @@
|
||||||
// adds horizontal mouse scrolling
|
// adds horizontal mouse scrolling
|
||||||
// 1.4: adds crypto support
|
// 1.4: adds crypto support
|
||||||
// 1.5: adds file transfer and removes home brew crypto
|
// 1.5: adds file transfer and removes home brew crypto
|
||||||
|
// 1.6: adds clipboard streaming
|
||||||
// NOTE: with new version, synergy minor version should increment
|
// NOTE: with new version, synergy minor version should increment
|
||||||
static const SInt16 kProtocolMajorVersion = 1;
|
static const SInt16 kProtocolMajorVersion = 1;
|
||||||
static const SInt16 kProtocolMinorVersion = 5;
|
static const SInt16 kProtocolMinorVersion = 6;
|
||||||
|
|
||||||
// default contact port number
|
// default contact port number
|
||||||
static const UInt16 kDefaultPort = 24800;
|
static const UInt16 kDefaultPort = 24800;
|
||||||
|
@ -69,13 +70,19 @@ enum EDirectionMask {
|
||||||
kBottomMask = 1 << kBottom
|
kBottomMask = 1 << kBottom
|
||||||
};
|
};
|
||||||
|
|
||||||
// file transfer constants
|
// Data transfer constants
|
||||||
enum EFileTransfer {
|
enum EDataTransfer {
|
||||||
kFileStart = 1,
|
kDataStart = 1,
|
||||||
kFileChunk = 2,
|
kDataChunk = 2,
|
||||||
kFileEnd = 3
|
kDataEnd = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Data received constants
|
||||||
|
enum EDataReceived {
|
||||||
|
kNotFinish,
|
||||||
|
kFinish,
|
||||||
|
kError
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// message codes (trailing NUL is not part of code). in comments, $n
|
// 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;
|
extern const char* kMsgDMouseWheel1_0;
|
||||||
|
|
||||||
// clipboard data: primary <-> secondary
|
// 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
|
// is 0 when sent by the primary. secondary screens should use the
|
||||||
// sequence number from the most recent kMsgCEnter. $1 = clipboard
|
// sequence number from the most recent kMsgCEnter. $1 = clipboard
|
||||||
// identifier.
|
// identifier.
|
||||||
|
|
|
@ -28,7 +28,8 @@
|
||||||
#include "server/Server.h"
|
#include "server/Server.h"
|
||||||
#include "server/ClientListener.h"
|
#include "server/ClientListener.h"
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
#include "synergy/FileChunker.h"
|
#include "synergy/FileChunk.h"
|
||||||
|
#include "synergy/StreamChunker.h"
|
||||||
#include "net/SocketMultiplexer.h"
|
#include "net/SocketMultiplexer.h"
|
||||||
#include "net/NetworkAddress.h"
|
#include "net/NetworkAddress.h"
|
||||||
#include "net/TCPSocketFactory.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 getScreenShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h);
|
||||||
void getCursorPos(SInt32& x, SInt32& y);
|
void getCursorPos(SInt32& x, SInt32& y);
|
||||||
String intToString(size_t i);
|
|
||||||
UInt8* newMockData(size_t size);
|
UInt8* newMockData(size_t size);
|
||||||
void createFile(fstream& file, const char* filename, size_t size);
|
void createFile(fstream& file, const char* filename, size_t size);
|
||||||
|
|
||||||
|
@ -415,38 +415,28 @@ void
|
||||||
NetworkTests::sendMockData(void* eventTarget)
|
NetworkTests::sendMockData(void* eventTarget)
|
||||||
{
|
{
|
||||||
// send first message (file size)
|
// send first message (file size)
|
||||||
String size = intToString(kMockDataSize);
|
String size = synergy::string::sizeTypeToString(kMockDataSize);
|
||||||
size_t sizeLength = size.size();
|
FileChunk* sizeMessage = FileChunk::start(size);
|
||||||
FileChunker::FileChunk* sizeMessage = new FileChunker::FileChunk(sizeLength + 2);
|
|
||||||
char* chunkData = sizeMessage->m_chunk;
|
|
||||||
|
|
||||||
chunkData[0] = kFileStart;
|
|
||||||
memcpy(&chunkData[1], size.c_str(), sizeLength);
|
|
||||||
chunkData[sizeLength + 1] = '\0';
|
|
||||||
m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, sizeMessage));
|
m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, sizeMessage));
|
||||||
|
|
||||||
// send chunk messages with incrementing chunk size
|
// send chunk messages with incrementing chunk size
|
||||||
size_t lastSize = 0;
|
size_t lastSize = 0;
|
||||||
size_t sentLength = 0;
|
size_t sentLength = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
size_t chunkSize = lastSize + kMockDataChunkIncrement;
|
size_t dataSize = lastSize + kMockDataChunkIncrement;
|
||||||
|
|
||||||
// make sure we don't read too much from the mock data.
|
// make sure we don't read too much from the mock data.
|
||||||
if (sentLength + chunkSize > kMockDataSize) {
|
if (sentLength + dataSize > kMockDataSize) {
|
||||||
chunkSize = kMockDataSize - sentLength;
|
dataSize = kMockDataSize - sentLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
// first byte is the chunk mark, last is \0
|
// first byte is the chunk mark, last is \0
|
||||||
FileChunker::FileChunk* fileChunk = new FileChunker::FileChunk(chunkSize + 2);
|
FileChunk* chunk = FileChunk::data(m_mockData, dataSize);
|
||||||
char* chunkData = fileChunk->m_chunk;
|
m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, chunk));
|
||||||
|
|
||||||
chunkData[0] = kFileChunk;
|
sentLength += dataSize;
|
||||||
memcpy(&chunkData[1], &m_mockData[sentLength], chunkSize);
|
lastSize = dataSize;
|
||||||
chunkData[chunkSize + 1] = '\0';
|
|
||||||
m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, fileChunk));
|
|
||||||
|
|
||||||
sentLength += chunkSize;
|
|
||||||
lastSize = chunkSize;
|
|
||||||
|
|
||||||
if (sentLength == kMockDataSize) {
|
if (sentLength == kMockDataSize) {
|
||||||
break;
|
break;
|
||||||
|
@ -455,11 +445,7 @@ NetworkTests::sendMockData(void* eventTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
// send last message
|
// send last message
|
||||||
FileChunker::FileChunk* transferFinished = new FileChunker::FileChunk(2);
|
FileChunk* transferFinished = FileChunk::end();
|
||||||
chunkData = transferFinished->m_chunk;
|
|
||||||
|
|
||||||
chunkData[0] = kFileEnd;
|
|
||||||
chunkData[1] = '\0';
|
|
||||||
m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, transferFinished));
|
m_events.addEvent(Event(m_events.forIScreen().fileChunkSending(), eventTarget, transferFinished));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,12 +513,4 @@ getCursorPos(SInt32& x, SInt32& y)
|
||||||
y = 0;
|
y = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
String
|
|
||||||
intToString(size_t i)
|
|
||||||
{
|
|
||||||
stringstream ss;
|
|
||||||
ss << i;
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // WINAPI_CARBON
|
#endif // WINAPI_CARBON
|
||||||
|
|
|
@ -61,5 +61,6 @@ public:
|
||||||
MOCK_METHOD0(forIKeyState, IKeyStateEvents&());
|
MOCK_METHOD0(forIKeyState, IKeyStateEvents&());
|
||||||
MOCK_METHOD0(forIPrimaryScreen, IPrimaryScreenEvents&());
|
MOCK_METHOD0(forIPrimaryScreen, IPrimaryScreenEvents&());
|
||||||
MOCK_METHOD0(forIScreen, IScreenEvents&());
|
MOCK_METHOD0(forIScreen, IScreenEvents&());
|
||||||
|
MOCK_METHOD0(forClipboard, ClipboardEvents&());
|
||||||
MOCK_CONST_METHOD0(waitForReady, void());
|
MOCK_CONST_METHOD0(waitForReady, void());
|
||||||
};
|
};
|
||||||
|
|
|
@ -82,3 +82,21 @@ TEST(StringTests, removeChar)
|
||||||
|
|
||||||
EXPECT_EQ("fbar", subject);
|
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
|
// after waiting the time limit send another to make sure
|
||||||
// we can log after the time limit passes.
|
// 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 3");
|
||||||
outputter.write(kNOTE, "mock 4");
|
outputter.write(kNOTE, "mock 4");
|
||||||
outputter.sendBuffer();
|
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