diff --git a/client/src/UDSClient.py b/client/src/UDSClient.py index 9b7149e3..0a845f7c 100644 --- a/client/src/UDSClient.py +++ b/client/src/UDSClient.py @@ -38,7 +38,10 @@ import six from uds.rest import RestRequest from uds.forward import forward +from uds import tools + import webbrowser +import time from UDSWindow import Ui_MainWindow @@ -47,6 +50,10 @@ VERSION = '1.9.5' class UDSClient(QtGui.QMainWindow): + + ticket = None + scrambler = None + def __init__(self): QtGui.QMainWindow.__init__(self) self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint) @@ -57,46 +64,76 @@ class UDSClient(QtGui.QMainWindow): self.ui.progressBar.setValue(0) self.ui.cancelButton.clicked.connect(self.cancelPushed) + self.ui.info.setText('Initializing...') + self.activateWindow() def closeWindow(self): self.close() - def processError(self, rest): - if 'error' in rest.data: - raise Exception(rest.data['error']) + def processError(self, data): + if 'error' in data: + raise Exception(data['error']) # QtGui.QMessageBox.critical(self, 'Request error', rest.data['error'], QtGui.QMessageBox.Ok) # self.closeWindow() # return + def showError(self, e): + self.ui.progressBar.setValue(100) + self.ui.info.setText('Error') + QtGui.QMessageBox.critical(self, 'Error', six.text_type(e), QtGui.QMessageBox.Ok) + self.closeWindow() + def cancelPushed(self): self.close() - def version(self, rest): + @QtCore.pyqtSlot() + def getVersion(self): + self.req = RestRequest('', self, self.version) + self.req.get() + + @QtCore.pyqtSlot(dict) + def version(self, data): try: self.ui.progressBar.setValue(10) - self.processError(rest) + self.processError(data) - if rest.data['result']['requiredVersion'] > VERSION: + self.ui.info.setText('Processing...') + + if data['result']['requiredVersion'] > VERSION: QtGui.QMessageBox.critical(self, 'Upgrade required', 'A newer connector version is required.\nA browser will be opened to download it.', QtGui.QMessageBox.Ok) - webbrowser.open(rest.data['result']['downloadUrl']) + webbrowser.open(data['result']['downloadUrl']) self.closeWindow() return - QtGui.QMessageBox.critical(self, 'Notice', six.text_type(rest.data), QtGui.QMessageBox.Ok) + self.req = RestRequest('/{}/{}'.format(self.ticket, self.scrambler), self, self.transportDataReceived) + self.req.get() except Exception as e: - QtGui.QMessageBox.critical(self, 'Error', six.text_type(e), QtGui.QMessageBox.Ok) - self.closeWindow() + self.showError(e) - def showEvent(self, *args, **kwargs): + @QtCore.pyqtSlot(dict) + def transportDataReceived(self, data): + try: + self.ui.progressBar.setValue(20) + self.processError(data) + + script = data['result'] + print script + + six.exec_(script, globals(), {'parent': self}) + + self.closeWindow() + except Exception as e: + self.showError(e) + + def start(self): ''' Starts proccess by requesting version info ''' - self.ui.info.setText('Requesting required connector version...') - self.req = RestRequest('', self, self.version) - self.req.get() + self.ui.info.setText('Initializing...') + QtCore.QTimer.singleShot(100, self.getVersion) def done(data): @@ -104,7 +141,13 @@ def done(data): sys.exit(0) if __name__ == "__main__": + # Initialize app app = QtGui.QApplication(sys.argv) + + # Set several info for settings + QtCore.QCoreApplication.setOrganizationName('Virtual Cable S.L.U.') + QtCore.QCoreApplication.setApplicationName('UDS Connector') + app.setStyle(QtGui.QStyleFactory.create('plastique')) if six.PY3 is False: @@ -118,7 +161,7 @@ if __name__ == "__main__": raise Exception() ssl = uri[3] == 's' - host, ticket, scrambler = uri.split('//')[1].split('/') + host, UDSClient.ticket, UDSClient.scrambler = uri.split('//')[1].split('/') except Exception: QtGui.QMessageBox.critical(None, 'Notice', 'This program is designed to be used by UDS', QtGui.QMessageBox.Ok) @@ -131,8 +174,14 @@ if __name__ == "__main__": try: win = UDSClient() win.show() + win.start() - sys.exit(app.exec_()) + exitVal = app.exec_() + + time.sleep(3) + tools.unlinkFiles() + + sys.exit(exitVal) except Exception as e: QtGui.QMessageBox.critical(None, 'Error', six.text_type(e), QtGui.QMessageBox.Ok) diff --git a/client/src/uds/osDetector.py b/client/src/uds/osDetector.py new file mode 100644 index 00000000..88085956 --- /dev/null +++ b/client/src/uds/osDetector.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2015 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +''' +@author: Adolfo Gómez, dkmaster at dkmon dot com +''' +from __future__ import unicode_literals + +import sys + + +LINUX = 'Linux' +WINDOWS = 'Windows' +MAC_OS_X = 'Mac os x' + + +def getOs(): + if sys.platform.startswith('linux'): + return LINUX + elif sys.platform.startswith('win'): + return WINDOWS + elif sys.platform.startswith('darwin'): + return MAC_OS_X diff --git a/client/src/uds/rest.py b/client/src/uds/rest.py index fe115ab6..33f3aa0b 100644 --- a/client/src/uds/rest.py +++ b/client/src/uds/rest.py @@ -32,25 +32,27 @@ ''' from __future__ import unicode_literals -from PyQt4.QtCore import pyqtSignal -from PyQt4.QtCore import QObject, QUrl +from PyQt4.QtCore import pyqtSignal, pyqtSlot +from PyQt4.QtCore import QObject, QUrl, QSettings +from PyQt4.QtCore import Qt from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply, QSslCertificate from PyQt4.QtGui import QMessageBox import json +import osDetector import six class RestRequest(QObject): restApiUrl = '' # - done = pyqtSignal(QObject) + + done = pyqtSignal(dict, name='done') def __init__(self, url, parentWindow, done): # parent not used super(RestRequest, self).__init__() # private self._manager = QNetworkAccessManager() - self.data = None self.url = QUrl(RestRequest.restApiUrl + url) # connect asynchronous result, when a request finishes @@ -58,9 +60,10 @@ class RestRequest(QObject): self._manager.sslErrors.connect(self._sslError) self._parentWindow = parentWindow - self.done.connect(done) + self.done.connect(done, Qt.QueuedConnection) # private slot, no need to declare as slot + @pyqtSlot(QNetworkReply) def _finished(self, reply): ''' Handle signal 'finished'. A network request has finished. @@ -69,30 +72,37 @@ class RestRequest(QObject): if reply.error() != QNetworkReply.NoError: raise Exception(reply.errorString()) - data = six.text_type(reply.readAll()) - self.data = json.loads(data) + data = json.loads(six.text_type(reply.readAll())) except Exception as e: - self.data = { + data = { 'result': None, 'error': six.text_type(e) } + self.done.emit(data) + reply.deleteLater() # schedule for delete from main event loop - self.done.emit(self) + @pyqtSlot(QNetworkReply, list) def _sslError(self, reply, errors): - - print "SSL Error" - print reply + settings = QSettings() cert = errors[0].certificate() - errorString = 'The certificate for "{}" has the following errors:\n'.format(cert.subjectInfo(QSslCertificate.CommonName)) - for err in errors: - errorString += err.errorString() + '\n' + digest = six.text_type(cert.digest().toHex()) - if QMessageBox.warning(self._parentWindow, 'SSL Error', errorString, QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes: + approved = settings.value(digest, False).toBool() + + errorString = '

The certificate for {} has the following errors:

' + + if approved or QMessageBox.warning(self._parentWindow, 'SSL Warning', errorString, QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes: + settings.setValue(digest, True) reply.ignoreSslErrors() def get(self): - print self.url request = QNetworkRequest(self.url) + request.setRawHeader('User-Agent', osDetector.getOs() + " - UDS Connector") self._manager.get(request) diff --git a/client/src/uds/tools.py b/client/src/uds/tools.py index a18f5446..352d0706 100644 --- a/client/src/uds/tools.py +++ b/client/src/uds/tools.py @@ -31,3 +31,33 @@ @author: Adolfo Gómez, dkmaster at dkmon dot com ''' from __future__ import unicode_literals + +import tempfile +import string +import random +import os + +_unlinkFiles = [] + + +def saveTempFile(content, filename=None): + if filename is None: + filename = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(16)) + filename = filename + '.uds' + filename = os.path.join(tempfile.gettempdir(), filename) + with open(filename, 'w') as f: + f.write(content) + + return filename + + +def addFileToUnlink(filename): + _unlinkFiles.append(filename) + + +def unlinkFiles(): + for f in _unlinkFiles: + try: + os.unlink(f) + except Exception: + pass diff --git a/server/src/uds/REST/methods/client.py b/server/src/uds/REST/methods/client.py index c66bc884..98f87add 100644 --- a/server/src/uds/REST/methods/client.py +++ b/server/src/uds/REST/methods/client.py @@ -92,7 +92,6 @@ class Client(Handler): Processes get requests ''' logger.debug("Client args for GET: {0}".format(self._args)) - # return Client.result(error=errors.ACCESS_DENIED) if len(self._args) == 0: url = self._request.build_absolute_uri(reverse('ClientDownload')) @@ -102,24 +101,30 @@ class Client(Handler): 'downloadUrl': url }) - if len(self._args) != 2: + try: + ticket, scrambler = self._args + except Exception: raise RequestError('Invalid request') try: - data = TicketStore.get(self._args[0]) + data = TicketStore.get(ticket) except Exception: return Client.result(error=errors.ACCESS_DENIED) self._request.user = User.objects.get(uuid=data['user']) try: + logger.debug(data) res = getService(self._request, data['service'], data['transport']) + logger.debug('Res: {}'.format(res)) if res is not None: ip, userService, userServiceInstance, transport, transportInstance = res - password = cryptoManager().xor(self._args[1], data['password']).decode('utf-8') + password = cryptoManager().xor(data['password'], scrambler).decode('utf-8') + logger.debug('Password: {}'.format(password)) - transportInfo = transportInstance.getUDSTransportData(userService, transport, ip, self.request.os, self._request.user, password, self._request) - return Client.result(transportInfo) + transportScript = transportInstance.getUDSTransportScript(userService, transport, ip, self._request.os, self._request.user, password, self._request) + logger.debug('Transport script: {}'.format(transportScript)) + return Client.result(transportScript) except Exception as e: logger.exception("Exception") return Client.result(error=six.text_type(e)) diff --git a/server/src/uds/core/transports/BaseTransport.py b/server/src/uds/core/transports/BaseTransport.py index 17334502..c2d0d074 100644 --- a/server/src/uds/core/transports/BaseTransport.py +++ b/server/src/uds/core/transports/BaseTransport.py @@ -158,15 +158,12 @@ class Transport(Module): ''' return user.name - def getUDSTransportData(self, userService, transport, ip, os, user, password, request): + def getUDSTransportScript(self, userService, transport, ip, os, user, password, request): ''' - Must override if transport does not provides its own link (that is, it is an UDS native transport) - Returns the transport data needed to connect with the userService - This is invoked right before service is accesed (secuentally). - - The class must provide either this method or the getLink method + If this is an uds transport, this will return the tranport script needed for executing + this on client ''' - return None + return '' def getLink(self, userService, transport, ip, os, user, password, request): ''' diff --git a/server/src/uds/core/util/OsDetector.py b/server/src/uds/core/util/OsDetector.py index 4b8a3ad9..644bc2b6 100644 --- a/server/src/uds/core/util/OsDetector.py +++ b/server/src/uds/core/util/OsDetector.py @@ -55,6 +55,7 @@ def getOsFromUA(ua): ''' Basic OS Client detector (very basic indeed :-)) ''' + logger.debug('Examining user agent {}'.format(ua)) if ua is None: os = DEFAULT_OS else: diff --git a/server/src/uds/core/util/request.py b/server/src/uds/core/util/request.py index 2858d882..3b945931 100644 --- a/server/src/uds/core/util/request.py +++ b/server/src/uds/core/util/request.py @@ -38,7 +38,7 @@ from uds.models import User import threading import logging -__updated__ = '2015-03-27' +__updated__ = '2015-03-31' logger = logging.getLogger(__name__) @@ -61,7 +61,7 @@ class GlobalRequestMiddleware(object): # Add IP to request GlobalRequestMiddleware.fillIps(request) # Ensures request contains os - OsDetector.getOsFromRequest(request) + request.os = OsDetector.getOsFromUA(request.META.get('HTTP_USER_AGENT')) # Ensures that requests contains the valid user GlobalRequestMiddleware.getUser(request) diff --git a/server/src/uds/transports/NX/NXFile.py b/server/src/uds/transports/NX/NXFile.py new file mode 100644 index 00000000..f1591910 --- /dev/null +++ b/server/src/uds/transports/NX/NXFile.py @@ -0,0 +1,207 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +''' +Created on Jul 29, 2011 + +@author: Adolfo Gómez, dkmaster at dkmon dot com + +''' +from __future__ import unicode_literals + +EMPTY_PASSWORD = "EMPTY_PASSWORD" + +NXTEMPLATE = ( + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "" +) + + +class NXFile(object): + fullScreen = False + width = '800' + height = '600' + cachemem = '4' + cachedisk = '32' + keyboardLayout = '' + linkSpeed = 'wan' + host = '' + port = '' + username = '' + password = '' + desktop = 'gnome' + + def __init__(self, fullScreen, width, height): + self.fullScreen = fullScreen + self.width = width + self.height = height + + def get(self): + rememberPass = 'true' + # password = NXPassword.scrambleString(self.password) + password = '' + if password == '': + rememberPass = "false" + password = EMPTY_PASSWORD + + resolution = self.width + "x" + self.height + if self.fullScreen: + resolution = "fullscreen" + + return NXTEMPLATE.format( + CACHEMEM=self.cachemem, + CACHEDISK=self.cachedisk, + KEYLAYOUT=self.keyboardLayout, + LINKSPEED=self.linkSpeed, + REMEMBERPASS=rememberPass, + RESOLUTION=resolution, + WIDTH=self.width, + HEIGHT=self.height, + HOST=self.host, + PORT=self.port, + DESKTOP=self.desktop, + USERNAME=self.username, + PASSWORD=self.password + ) diff --git a/server/src/uds/transports/RDP/BaseRDPTransport.py b/server/src/uds/transports/RDP/BaseRDPTransport.py index 2ef98f65..99f9d6a6 100644 --- a/server/src/uds/transports/RDP/BaseRDPTransport.py +++ b/server/src/uds/transports/RDP/BaseRDPTransport.py @@ -52,7 +52,6 @@ class BaseRDPTransport(Transport): This transport can use an domain. If username processed by authenticator contains '@', it will split it and left-@-part will be username, and right password ''' iconFile = 'rdp.png' - needsJava = True # If this transport needs java for rendering protocol = protocols.RDP useEmptyCreds = gui.CheckBoxField(label=_('Empty creds'), order=1, tooltip=_('If checked, the credentials used to connect will be emtpy')) diff --git a/server/src/uds/transports/RDP/RDPFile.py b/server/src/uds/transports/RDP/RDPFile.py new file mode 100644 index 00000000..cd42747f --- /dev/null +++ b/server/src/uds/transports/RDP/RDPFile.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- + +# +# Copyright (c) 2012 Virtual Cable S.L. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of Virtual Cable S.L. nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +''' +Created on Jul 29, 2011 + +@author: Adolfo Gómez, dkmaster at dkmon dot com + +''' +from __future__ import unicode_literals + +import six + + +class RDPFile(object): + fullScreen = False + width = '800' + height = '600' + bpp = '32' + address = '' + username = '' + domain = '' + password = '' + redirectSerials = False + redirectPrinters = False + redirectDrives = False + redirectSmartcards = False + redirectAudio = True + compression = True + displayConnectionBar = True + showWallpaper = False + multimon = False + + def __init__(self, fullScreen, width, height, bpp): + self.width = six.text_type(width) + self.height = six.text_type(height) + self.bpp = six.text_type(bpp) + self.fullScreen = fullScreen + + def get(self): + password = "{password}" + screenMode = self.fullScreen and "2" or "1" + audioMode = self.redirectAudio and "0" or "2" + serials = self.redirectSerials and "1" or "0" + drives = self.redirectDrives and "1" or "0" + scards = self.redirectSmartcards and "1" or "0" + printers = self.redirectPrinters and "1" or "0" + compression = self.compression and "1" or "0" + bar = self.displayConnectionBar and "1" or "0" + disableWallpaper = self.showWallpaper and "0" or "1" + useMultimon = self.multimon and "0" or "1" + + res = '' + res += 'screen mode id:i:' + screenMode + '\n' + res += 'desktopwidth:i:' + self.width + '\n' + res += 'desktopheight:i:' + self.height + '\n' + res += 'session bpp:i:' + self.bpp + '\n' + res += 'use multimon:i:' + useMultimon + '\n' + res += 'auto connect:i:1' + '\n' + res += 'full address:s:' + self.address + '\n' + res += 'compression:i:' + compression + '\n' + res += 'keyboardhook:i:2' + '\n' + res += 'audiomode:i:' + audioMode + '\n' + res += 'redirectdrives:i:' + drives + '\n' + res += 'redirectprinters:i:' + printers + '\n' + res += 'redirectcomports:i:' + serials + '\n' + res += 'redirectsmartcards:i:' + scards + '\n' + res += 'redirectclipboard:i:1' + '\n' + res += 'displayconnectionbar:i:' + bar + '\n' + if len(self.username) != 0: + res += 'username:s:' + self.username + '\n' + res += 'domain:s:' + self.domain + '\n' + res += 'password 51:b:' + password + '\n' + + res += 'alternate shell:s:' + '\n' + res += 'shell working directory:s:' + '\n' + res += 'disable wallpaper:i:' + disableWallpaper + '\n' + res += 'disable full window drag:i:1' + '\n' + res += 'disable menu anims:i:' + disableWallpaper + '\n' + res += 'disable themes:i:' + disableWallpaper + '\n' + res += 'bitmapcachepersistenable:i:1' + '\n' + res += 'authentication level:i:0' + '\n' + res += 'enablecredsspsupport:i:1' + '\n' + res += 'prompt for credentials:i:0' + '\n' + res += 'negotiate security layer:i:1' + '\n' + + return res diff --git a/server/src/uds/transports/RDP/RDPTransport.py b/server/src/uds/transports/RDP/RDPTransport.py index 30ff35c9..d8983a31 100644 --- a/server/src/uds/transports/RDP/RDPTransport.py +++ b/server/src/uds/transports/RDP/RDPTransport.py @@ -35,10 +35,9 @@ from django.utils.translation import ugettext_noop as _ from uds.core.managers.UserPrefsManager import CommonPrefs 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 connection -from .web import generateHtmlForRdp, getHtmlComponent +from uds.core.util import OsDetector from .BaseRDPTransport import BaseRDPTransport +from .RDPFile import RDPFile import logging @@ -68,7 +67,45 @@ class RDPTransport(BaseRDPTransport): wallpaper = BaseRDPTransport.wallpaper multimon = BaseRDPTransport.multimon - def renderForHtml(self, userService, transport, ip, os, user, password): + def windowsScript(self, data): + r = RDPFile(data['fullScreen'], data['width'], data['height'], data['depth']) + r.address = '{}:{}'.format(data['ip'], 3389) + r.username = data['username'] + r.password = '{password}' + r.domain = data['domain'] + r.redirectPrinters = self.allowPrinters.isTrue() + r.redirectSmartcards = self.allowSmartcards.isTrue() + r.redirectDrives = self.allowDrives.isTrue() + r.redirectSerials = self.allowSerials.isTrue() + r.showWallpaper = self.wallpaper.isTrue() + r.multimon = self.multimon.isTrue() + + # 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 + return ''' +from __future__ import unicode_literals + +from PyQt4 import QtCore, QtGui +import win32crypt +import os +import subprocess +import time + +from uds import tools + +import six + +file = \'\'\'{file}\'\'\'.format(password=win32crypt.CryptProtectData(six.binary_type('{password}'.encode('UTF-16LE')), None, None, None, None, 0x01).encode('hex')) + +filename = tools.saveTempFile(file) +executable = os.path.join(os.path.join(os.environ['WINDIR'], 'system32'), 'mstsc.exe') +subprocess.call([executable, filename]) +tools.addFileToUnlink(filename) + +# QtGui.QMessageBox.critical(parent, 'Notice', filename + ", " + executable, QtGui.QMessageBox.Ok) + +'''.format(os=data['os'], file=r.get(), password=data['password']) + + def getUDSTransportScript(self, userService, transport, ip, os, user, password, request): # We use helper to keep this clean prefs = user.prefs('rdp') @@ -78,8 +115,14 @@ class RDPTransport(BaseRDPTransport): width, height = CommonPrefs.getWidthHeight(prefs) depth = CommonPrefs.getDepth(prefs) - # Extra data - extra = { + # data + data = { + 'os': os['OS'], + 'ip': ip, + 'port': 3389, + 'username': username, + 'password': password, + 'domain': domain, 'width': width, 'height': height, 'depth': depth, @@ -89,7 +132,11 @@ class RDPTransport(BaseRDPTransport): 'serials': self.allowSerials.isTrue(), 'compression': True, 'wallpaper': self.wallpaper.isTrue(), - 'multimon': self.multimon.isTrue() + 'multimon': self.multimon.isTrue(), + 'fullScreen': width == -1 or height == -1 } - return generateHtmlForRdp(self, userService.uuid, transport.uuid, os, ip, '3389', username, password, domain, extra) + if data['os'] == OsDetector.Windows: + return self.windowsScript(data) + + return '' diff --git a/server/src/uds/transports/X2GO/X2GOTransport.py b/server/src/uds/transports/X2GO/X2GOTransport.py index dbb4c37a..83854618 100644 --- a/server/src/uds/transports/X2GO/X2GOTransport.py +++ b/server/src/uds/transports/X2GO/X2GOTransport.py @@ -56,7 +56,6 @@ class X2GOTransport(Transport): typeType = 'X2GOTransport' typeDescription = _('X2GO Transport for direct connection') iconFile = 'x2go.png' - needsJava = True # If this transport needs java for rendering protocol = protocols.NX useEmptyCreds = gui.CheckBoxField(label=_('Empty creds'), order=1, tooltip=_('If checked, the credentials used to connect will be emtpy')) @@ -117,7 +116,8 @@ class X2GOTransport(Transport): self.cache().put(ip, 'N', READY_CACHE_TIMEOUT) return ready == 'Y' - def getUDSTransportInfo(self, userService, transport, ip, os, user, password, request): + def getUDSTransportScript(self, userService, transport, ip, os, user, password, request): + logger.debug('Getting X2Go Transport info') prefs = user.prefs('nx') @@ -138,8 +138,8 @@ class X2GOTransport(Transport): # Fix username/password acording to os manager username, password = userService.processUserPassword(username, password) - # Extra data - return { + # data + data = { 'username': username, 'password': password, 'width': width, @@ -150,3 +150,15 @@ class X2GOTransport(Transport): 'cacheDisk': self.cacheDisk.value, 'cacheMem': self.cacheMem.value } + + return ''' +from PyQt4 import QtCore, QtGui +import six +from uds import osDetector + +data = {data} +osname = {os} + +QtGui.QMessageBox.critical(parent, 'Notice ' + osDetector.getOs(), six.text_type(data), QtGui.QMessageBox.Ok) +'''.format(data=data, os=os) + diff --git a/server/src/uds/web/errors.py b/server/src/uds/web/errors.py index 55b2be5b..4bf7644b 100644 --- a/server/src/uds/web/errors.py +++ b/server/src/uds/web/errors.py @@ -80,7 +80,7 @@ strings = [ _('Invalid request received'), _('Your browser is not supported. Please, upgrade it to a modern HTML5 browser like Firefox or Chrome'), _('The requested service is in maintenance mode'), - _('The service is not ready') + _('The service is not ready.\nPlease, try again in a few moments.') ] diff --git a/server/src/uds/web/views/service.py b/server/src/uds/web/views/service.py index 40682e12..a154c027 100644 --- a/server/src/uds/web/views/service.py +++ b/server/src/uds/web/views/service.py @@ -30,7 +30,7 @@ ''' from __future__ import unicode_literals -__updated__ = '2015-03-27' +__updated__ = '2015-03-31' from django.utils.translation import ugettext as _ from django.http import HttpResponse, HttpResponseRedirect @@ -226,7 +226,7 @@ def clientEnabler(request, idService, idTransport): _x, ads, _x, trans, _x = res data = { - 'service': ads.uuid, + 'service': 'A' + ads.uuid, 'transport': trans.uuid, 'user': request.user.uuid, 'password': password