diff --git a/server/src/uds/transports/X2GO/BaseX2GOTransport.py b/server/src/uds/transports/X2GO/BaseX2GOTransport.py index 34ae2f70f..649b0ff69 100644 --- a/server/src/uds/transports/X2GO/BaseX2GOTransport.py +++ b/server/src/uds/transports/X2GO/BaseX2GOTransport.py @@ -38,6 +38,7 @@ from uds.core.ui.UserInterface import gui from uds.core.transports.BaseTransport import Transport from uds.core.transports import protocols from uds.core.util import OsDetector +from uds.core.util import connection # This transport is specific for oVirt, so we need to point to it @@ -46,7 +47,7 @@ import six import os import logging -__updated__ = '2016-10-23' +__updated__ = '2016-10-24' logger = logging.getLogger(__name__) @@ -89,10 +90,58 @@ class BaseX2GOTransport(Transport): {'id': 'gnome-session-cinnamon2d', 'text': 'Cinnamon 2.2 (see docs)'}, ], tab=gui.PARAMETERS_TAB) - keyboardLayout = gui.TextField(label=_('Keyboard'), order=12, tooltip=_('Keyboard layout (es, us, fr, ...). Empty value means autodetect.'), - default='', + sound = gui.CheckBoxField( + order=12, + label=_('Enable sound'), + tooltip=_('If checked, sound will be available'), + defvalue=gui.TRUE, + tab=gui.PARAMETERS_TAB + ) + + exports = gui.CheckBoxField( + order=13, + label=_('Redirect root folder'), + tooltip=_('If checked, user home folder will be redirected'), + defvalue=gui.FALSE, + tab=gui.PARAMETERS_TAB + ) + + + soundType = gui.ChoiceField(label=_('Desktop'), order=30, tooltip=_('Desktop session'), + defvalue='pulse', + values=[ + {'id': 'pulse', 'text': 'Pulse'}, + {'id': 'esd', 'text': 'ESD'}, + ], tab=gui.ADVANCED_TAB + ) + + keyboardLayout = gui.TextField(label=_('Keyboard'), order=31, tooltip=_('Keyboard layout (es, us, fr, ...). Empty value means autodetect.'), + defvalue='', tab=gui.ADVANCED_TAB ) + # 'nopack', '8', '64', '256', '512', '4k', '32k', '64k', '256k', '2m', '16m' + # '256-rdp', '256-rdp-compressed', '32k-rdp', '32k-rdp-compressed', '64k-rdp' + # '64k-rdp-compressed', '16m-rdp', '16m-rdp-compressed' + # 'rfb-hextile', 'rfb-tight', 'rfb-tight-compressed' + # '8-tight', '64-tight', '256-tight', '512-tight', '4k-tight', '32k-tight' + # '64k-tight', '256k-tight', '2m-tight', '16m-tight' + # '8-jpeg-%', '64-jpeg', '256-jpeg', '512-jpeg', '4k-jpeg', '32k-jpeg' + # '64k-jpeg', '256k-jpeg', '2m-jpeg', '16m-jpeg-%' + # '8-png-jpeg-%', '64-png-jpeg', '256-png-jpeg', '512-png-jpeg', '4k-png-jpeg' + # '32k-png-jpeg', '64k-png-jpeg', '256k-png-jpeg', '2m-png-jpeg', '16m-png-jpeg-%' + # '8-png-%', '64-png', '256-png', '512-png', '4k-png' + # '32k-png', '64k-png', '256k-png', '2m-png', '16m-png-%' + # '16m-rgb-%', '16m-rle-%' + pack = gui.TextField(label=_('Pack'), order=32, tooltip=_('Pack format. Change with care!'), + defvalue='4k-jpeg', + tab=gui.ADVANCED_TAB + ) + + quality = gui.NumericField(label=_('Quality'), order=33, tooltip=_('Quality value used on some pack formats.'), + length=1, defvalue='8', minValue=1, maxValue=9, required=True, + tab=gui.ADVANCED_TAB) + + def isAvailableFor(self, userService, ip): ''' @@ -100,7 +149,15 @@ class BaseX2GOTransport(Transport): Override this in yours transports ''' logger.debug('Checking availability for {0}'.format(ip)) - return True # Spice is available, no matter what IP machine has (even if it does not have one) + ready = self.cache.get(ip) + if ready is None: + # Check again for ready + if connection.testServer(ip, '22') is True: + self.cache.put(ip, 'Y', READY_CACHE_TIMEOUT) + return True + else: + self.cache.put(ip, 'N', READY_CACHE_TIMEOUT) + return ready == 'Y' def processedUser(self, userService, userName): v = self.processUserPassword(userService, userName, '') @@ -117,7 +174,7 @@ class BaseX2GOTransport(Transport): return {'protocol': self.protocol, 'username': username, 'password': ''} - def getConnectionInfo(self, service, user, password): + def getConnectionInfo(self, service, user, password): # Password is ignored in this transport, auth is done using SSH return self.processUserPassword(service, user, password) def genKeyPairForSsh(self): diff --git a/server/src/uds/transports/X2GO/X2GOTransport.py b/server/src/uds/transports/X2GO/X2GOTransport.py index 7fcf061f0..d15477c48 100644 --- a/server/src/uds/transports/X2GO/X2GOTransport.py +++ b/server/src/uds/transports/X2GO/X2GOTransport.py @@ -32,13 +32,15 @@ ''' from django.utils.translation import ugettext_noop as _ +from uds.core.managers.UserPrefsManager import CommonPrefs from uds.core.util import OsDetector from uds.core.util import tools from .BaseX2GOTransport import BaseX2GOTransport +from . import x2gofile import logging -__updated__ = '2016-10-23' +__updated__ = '2016-10-24' logger = logging.getLogger(__name__) @@ -53,9 +55,60 @@ class X2GOTransport(BaseX2GOTransport): typeDescription = _('X2Go Transport for direct connection (EXPERIMENTAL)') fixedName = BaseX2GOTransport.fixedName - fullScreen = BaseX2GOTransport.fullScreen + # fullScreen = BaseX2GOTransport.fullScreen desktopType = BaseX2GOTransport.desktopType + sound = BaseX2GOTransport.sound + exports = BaseX2GOTransport.exports + + soundType = BaseX2GOTransport.soundType + keyboardLayout = BaseX2GOTransport.keyboardLayout + pack = BaseX2GOTransport.pack + quality = BaseX2GOTransport.quality def getUDSTransportScript(self, userService, transport, ip, os, user, password, request): - self.getAndPushKey('user', userService) - return '' + prefs = user.prefs('nx') + + priv, pub = self.getAndPushKey('user', userService) + + prefs = user.prefs('rdp') + + ci = self.getConnectionInfo(userService, user, password) + username = ci['username'] + + width, height = CommonPrefs.getWidthHeight(prefs) + xf = x2gofile.getTemplate( + pack=self.pack.value, + quality=self.quality.value, + sound=self.sound.isTrue(), + soundSystem=self.sound.value, + windowManager=self.desktopType.value, + exports=self.exports.isTrue()) + + # data + data = { + 'os': os['OS'], + 'ip': ip, + 'port': 22, + 'username': username, + 'key': priv, + 'width': width, + 'height': height, + 'printers': True, + 'drives': self.exports.isTrue(), + 'fullScreen': width == -1 or height == -1, + 'this_server': request.build_absolute_uri('/'), + 'xf': xf + } + + m = tools.DictAsObj(data) + + os = { + OsDetector.Windows: 'windows', + OsDetector.Linux: 'linux', + # OsDetector.Macintosh: 'macosx' + }.get(m.os) + + if os is None: + return super(X2GOTransport, self).getUDSTransportScript(self, userService, transport, ip, os, user, password, request) + + return self.getScript('scripts/{}/direct.py'.format(os)).format(m=m) diff --git a/server/src/uds/transports/X2GO/__init__.py b/server/src/uds/transports/X2GO/__init__.py index 941350dc9..defbf1783 100644 --- a/server/src/uds/transports/X2GO/__init__.py +++ b/server/src/uds/transports/X2GO/__init__.py @@ -35,3 +35,7 @@ from django.utils.translation import ugettext_noop as _ from uds.core.managers.UserPrefsManager import UserPrefsManager, CommonPrefs from .X2GOTransport import X2GOTransport from .TX2GOTransport import TX2GOTransport + + +# We will use same prefs as for NX, X2GO is based on it +UserPrefsManager.manager().registerPrefs('nx', _('NX Protocol'), [CommonPrefs.screenSizePref]) diff --git a/server/src/uds/transports/X2GO/scripts/windows/direct.py b/server/src/uds/transports/X2GO/scripts/windows/direct.py new file mode 100644 index 000000000..822882613 --- /dev/null +++ b/server/src/uds/transports/X2GO/scripts/windows/direct.py @@ -0,0 +1,27 @@ +# This is a template +# Saved as .py for easier editing +from __future__ import unicode_literals + +# pylint: disable=import-error, no-name-in-module +from PyQt4 import QtCore, QtGui +import win32crypt # @UnresolvedImport +import os +import subprocess + +from uds import tools # @UnresolvedImport + +import six + +# The password must be encoded, to be included in a .rdp file, as 'UTF-16LE' before protecting (CtrpyProtectData) it in order to work with mstsc +keyFile = tools.saveTempFile('''{m.key}''') +theFile = '''{m.xf}'''.format(exports='c:\\', keyFile=keyFile.replace('\\', '/'), ip='{m.ip}') +filename = tools.saveTempFile(theFile) + +x2goPath = os.environ['PROGRAMFILES(X86)'] + '\\x2goclient' +executable = tools.findApp('x2goclient.exe', [x2goPath]) + +# executable = tools.findApp('mstsc.exe') +# subprocess.Popen([executable, filename]) +# tools.addFileToUnlink(filename) + +QtGui.QMessageBox.critical(parent, 'Notice', executable + ' -- ' + keyFile + ', ' + filename, QtGui.QMessageBox.Ok) # @UndefinedVariable diff --git a/server/src/uds/transports/X2GO/scripts/windows/tunnel.py b/server/src/uds/transports/X2GO/scripts/windows/tunnel.py new file mode 100644 index 000000000..0f29a9c9e --- /dev/null +++ b/server/src/uds/transports/X2GO/scripts/windows/tunnel.py @@ -0,0 +1,38 @@ +# This is a template +# Saved as .py for easier editing +from __future__ import unicode_literals + +# pylint: disable=import-error, no-name-in-module, too-many-format-args, undefined-variable + +from PyQt4 import QtCore, QtGui +import win32crypt # @UnresolvedImport +import os +import subprocess +from uds.forward import forward # @UnresolvedImport + +from uds import tools # @UnresolvedImport + +import six + +forwardThread, port = forward('{m.tunHost}', '{m.tunPort}', '{m.tunUser}', '{m.tunPass}', '{m.ip}', 3389) + +if forwardThread.status == 2: + raise Exception('Unable to open tunnel') + +tools.addTaskToWait(forwardThread) + +# The password must be encoded, to be included in a .rdp file, as 'UTF-16LE' before protecting (CtrpyProtectData) it in order to work with mstsc +theFile = '''{m.r.as_file}'''.format( + password=win32crypt.CryptProtectData(six.binary_type('{m.password}'.encode('UTF-16LE')), None, None, None, None, 0x01).encode('hex'), + address='127.0.0.1:{{}}'.format(port) +) + +filename = tools.saveTempFile(theFile) +executable = tools.findApp('mstsc.exe') +if executable is None: + raise Exception('Unable to find mstsc.exe') + +subprocess.Popen([executable, filename]) +tools.addFileToUnlink(filename) + +# QtGui.QMessageBox.critical(parent, 'Notice', filename + ", " + executable, QtGui.QMessageBox.Ok) diff --git a/server/src/uds/transports/X2GO/x2gofile.py b/server/src/uds/transports/X2GO/x2gofile.py index e7316b07b..4eceb125d 100644 --- a/server/src/uds/transports/X2GO/x2gofile.py +++ b/server/src/uds/transports/X2GO/x2gofile.py @@ -34,12 +34,12 @@ template = '''[General] UDS=@ByteArray() -[20161019100758147] +[20160101100758147] speed=4 -pack=16m-jpeg -quality=8 +pack={pack} +quality={quality} fstunnel=true -export="/home/user:1;" +{export} iconvto=UTF-8 iconvfrom=ISO8859-1 useiconv=false @@ -57,18 +57,18 @@ xinerama=false clipboard=both usekbd=true type=auto -sound=true -soundsystem=pulse +sound={sound} +soundsystem={soundSystem} startsoundsystem=true soundtunnel=true defsndport=true sndport=4713 print=true -name=UDS/uds-test +name=UDS/connect icon=:/img/icons/128x128/x2gosession.png -host=172.27.0.208 +host={{ip}} user=user -key=/home/user/remotekey.txt +key={{keyFile}} rdpport=3389 sshport=22 autologin=false @@ -78,7 +78,7 @@ directrdp=false rootless=false published=false applications=WWWBROWSER, MAILCLIENT, OFFICE, TERMINAL -command=XFCE +command={windowManager} rdpoptions= rdpserver= xdmcpserver=localhost @@ -93,3 +93,8 @@ sshproxysameuser=false sshproxyautologin=false sshproxykrblogin=false ''' + +def getTemplate(pack, quality, sound, soundSystem, windowManager, exports): + trueFalse = lambda(x): 'true' if x else 'false' + export = 'export="{{export}}"' if exports else '' + return template.format(pack=pack, quality=quality, sound=trueFalse(sound), soundSystem=soundSystem, windowManager=windowManager, export=export)