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:
'.format(cert.subjectInfo(QSslCertificate.CommonName))
+
+ for err in errors:
+ errorString += '- ' + err.errorString() + '
'
+
+ errorString += '
'
+
+ 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"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \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