* Implemented uds connector script for RDP (just for windows right now)

* Advanced a lot with java removal process
* Implemented usefull things on client part
This commit is contained in:
Adolfo Gómez García 2015-03-31 18:09:45 +02:00
parent 62f50304fe
commit f173146d8f
15 changed files with 584 additions and 64 deletions

View File

@ -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 cancelPushed(self):
self.close()
def version(self, rest):
try:
self.ui.progressBar.setValue(10)
self.processError(rest)
if rest.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'])
self.closeWindow()
return
QtGui.QMessageBox.critical(self, 'Notice', six.text_type(rest.data), QtGui.QMessageBox.Ok)
except Exception as e:
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 showEvent(self, *args, **kwargs):
def cancelPushed(self):
self.close()
@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(data)
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(data['result']['downloadUrl'])
self.closeWindow()
return
self.req = RestRequest('/{}/{}'.format(self.ticket, self.scrambler), self, self.transportDataReceived)
self.req.get()
except Exception as e:
self.showError(e)
@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)

View File

@ -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

View File

@ -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 = '<p>The certificate for <b>{}</b> has the following errors:</p><ul>'.format(cert.subjectInfo(QSslCertificate.CommonName))
for err in errors:
errorString += '<li>' + err.errorString() + '</li>'
errorString += '</ul>'
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)

View File

@ -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

View File

@ -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))

View File

@ -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):
'''

View File

@ -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:

View File

@ -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)

View File

@ -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 = (
"<!DOCTYPE NXClientSettings>\n"
"<NXClientSettings application=\"nxclient\" version=\"1.3\" >\n"
" <group name=\"Advanced\" >\n"
" <option key=\"Cache size\" value=\"{CACHEMEM}\" />\n"
" <option key=\"Cache size on disk\" value=\"{CACHEDISK}\" />\n"
" <option key=\"Current keyboard\" value=\"true\" />\n"
" <option key=\"Custom keyboard layout\" value=\"{KEYLAYOUT}\" />\n"
" <option key=\"Disable ZLIB stream compression\" value=\"false\" />\n"
" <option key=\"Disable TCP no-delay\" value=\"false\" />\n" +
" <option key=\"Disable deferred updates\" value=\"false\" />\n"
" <option key=\"Enable HTTP proxy\" value=\"false\" />\n"
" <option key=\"Enable SSL encryption\" value=\"true\" />\n"
" <option key=\"Enable response time optimisations\" value=\"true\" />\n"
" <option key=\"Grab keyboard\" value=\"false\" />\n"
" <option key=\"HTTP proxy host\" value=\"\" />\n"
" <option key=\"HTTP proxy port\" value=\"8080\" />\n"
" <option key=\"HTTP proxy username\" value=\"\" />\n"
" <option key=\"Remember HTTP proxy password\" value=\"false\" />\n"
" <option key=\"Restore cache\" value=\"true\" />\n"
" <option key=\"StreamCompression\" value=\"\" />\n"
" </group>\n"
" <group name=\"Environment\" >\n"
" <option key=\"Font server host\" value=\"\" />\n"
" <option key=\"Font server port\" value=\"7100\" />\n"
" <option key=\"Use font server\" value=\"false\" />\n"
" </group>\n"
" <group name=\"General\" >\n"
" <option key=\"Automatic reconnect\" value=\"true\" />\n"
" <option key=\"Disable SHM\" value=\"false\" />\n"
" <option key=\"Disable emulate shared pixmaps\" value=\"false\" />\n"
" <option key=\"Link speed\" value=\"{LINKSPEED}\" />\n"
" <option key=\"Remember password\" value=\"{REMEMBERPASS}\" />\n"
" <option key=\"Resolution\" value=\"{RESOLUTION}\" />\n"
" <option key=\"Resolution width\" value=\"{WIDTH}\" />\n"
" <option key=\"Resolution height\" value=\"{HEIGHT}\" />\n"
" <option key=\"Server host\" value=\"{HOST}\" />\n"
" <option key=\"Server port\" value=\"{PORT}\" />\n"
" <option key=\"Session\" value=\"unix\" />\n"
" <option key=\"Desktop\" value=\"{DESKTOP}\" />\n"
" <option key=\"Use default image encoding\" value=\"1\" />\n"
" <option key=\"Use render\" value=\"false\" />\n"
" <option key=\"Use taint\" value=\"true\" />\n"
" <option key=\"Virtual desktop\" value=\"false\" />\n"
" <option key=\"XAgent encoding\" value=\"true\" />\n"
" <option key=\"displaySaveOnExit\" value=\"true\" />\n"
" <option key=\"xdm broadcast port\" value=\"177\" />\n"
" <option key=\"xdm list host\" value=\"localhost\" />\n"
" <option key=\"xdm list port\" value=\"177\" />\n"
" <option key=\"xdm mode\" value=\"server decide\" />\n"
" <option key=\"xdm query host\" value=\"localhost\" />\n"
" <option key=\"xdm query port\" value=\"177\" />\n"
" </group>\n"
" <group name=\"Images\" >\n"
" <option key=\"Disable JPEG Compression\" value=\"0\" />\n"
" <option key=\"Disable all image optimisations\" value=\"false\" />\n"
" <option key=\"Disable backingstore\" value=\"false\" />\n"
" <option key=\"Disable composite\" value=\"false\" />\n"
" <option key=\"Image Compression Type\" value=\"3\" />\n"
" <option key=\"Image Encoding Type\" value=\"0\" />\n"
" <option key=\"Image JPEG Encoding\" value=\"false\" />\n"
" <option key=\"JPEG Quality\" value=\"6\" />\n"
" <option key=\"RDP Image Encoding\" value=\"3\" />\n"
" <option key=\"RDP JPEG Quality\" value=\"6\" />\n"
" <option key=\"RDP optimization for low-bandwidth link\" value=\"false\" />\n"
" <option key=\"Reduce colors to\" value=\"\" />\n"
" <option key=\"Use PNG Compression\" value=\"true\" />\n"
" <option key=\"VNC JPEG Quality\" value=\"6\" />\n"
" <option key=\"VNC images compression\" value=\"3\" />\n"
" </group>\n"
" <group name=\"Login\" >\n"
" <option key=\"User\" value=\"{USERNAME}\" />\n"
" <option key=\"Auth\" value=\"{PASSWORD}\" />\n"
" <option key=\"Guest Mode\" value=\"false\" />\n"
" <option key=\"Guest password\" value=\"\" />\n"
" <option key=\"Guest username\" value=\"\" />\n"
" <option key=\"Login Method\" value=\"nx\" />\n"
" <option key=\"Public Key\" value=\"-----BEGIN DSA PRIVATE KEY-----\n"
"MIIBuwIBAAKBgQCXv9AzQXjxvXWC1qu3CdEqskX9YomTfyG865gb4D02ZwWuRU/9\n"
"C3I9/bEWLdaWgJYXIcFJsMCIkmWjjeSZyTmeoypI1iLifTHUxn3b7WNWi8AzKcVF\n"
"aBsBGiljsop9NiD1mEpA0G+nHHrhvTXz7pUvYrsrXcdMyM6rxqn77nbbnwIVALCi\n"
"xFdHZADw5KAVZI7r6QatEkqLAoGBAI4L1TQGFkq5xQ/nIIciW8setAAIyrcWdK/z\n"
"5/ZPeELdq70KDJxoLf81NL/8uIc4PoNyTRJjtT3R4f8Az1TsZWeh2+ReCEJxDWgG\n"
"fbk2YhRqoQTtXPFsI4qvzBWct42WonWqyyb1bPBHk+JmXFscJu5yFQ+JUVNsENpY\n"
"+Gkz3HqTAoGANlgcCuA4wrC+3Cic9CFkqiwO/Rn1vk8dvGuEQqFJ6f6LVfPfRTfa\n"
"QU7TGVLk2CzY4dasrwxJ1f6FsT8DHTNGnxELPKRuLstGrFY/PR7KeafeFZDf+fJ3\n"
"mbX5nxrld3wi5titTnX+8s4IKv29HJguPvOK/SI7cjzA+SqNfD7qEo8CFDIm1xRf\n"
"8xAPsSKs6yZ6j1FNklfu\n"
"-----END DSA PRIVATE KEY-----\n"
"\" />\n"
" </group>\n"
" <group name=\"Services\" >\n"
" <option key=\"Audio\" value=\"true\" />\n"
" <option key=\"IPPPort\" value=\"631\" />\n"
" <option key=\"IPPPrinting\" value=\"false\" />\n"
" <option key=\"Shares\" value=\"false\" />\n"
" </group>\n"
" <group name=\"VNC Session\" >\n"
" <option key=\"Display\" value=\"0\" />\n"
" <option key=\"Remember\" value=\"false\" />\n"
" <option key=\"Server\" value=\"\" />\n"
" </group>\n"
" <group name=\"Windows Session\" >\n"
" <option key=\"Application\" value=\"\" />\n"
" <option key=\"Authentication\" value=\"2\" />\n"
" <option key=\"Color Depth\" value=\"16\" />\n"
" <option key=\"Domain\" value=\"\" />\n"
" <option key=\"Image Cache\" value=\"true\" />\n"
" <option key=\"Password\" value=\"EMPTY_PASSWORD\" />\n"
" <option key=\"Remember\" value=\"true\" />\n"
" <option key=\"Run application\" value=\"false\" />\n"
" <option key=\"Server\" value=\"\" />\n"
" <option key=\"User\" value=\"\" />\n"
" </group>\n"
" <group name=\"share chosen\" >\n"
" <option key=\"Share number\" value=\"0\" />\n"
" </group>\n"
"</NXClientSettings>"
)
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
)

View File

@ -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'))

View File

@ -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

View File

@ -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 ''

View File

@ -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)

View File

@ -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.')
]

View File

@ -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