mirror of
https://github.com/dkmstr/openuds.git
synced 2025-01-08 21:18:00 +03:00
Working on Spice protocol support.
* Added "provider specific" transport support * Fixed dashboard to new transport support
This commit is contained in:
parent
dad83c5c50
commit
c1c54f38cc
1715
server/src/notebook/oVirt.ipynb
Normal file
1715
server/src/notebook/oVirt.ipynb
Normal file
File diff suppressed because it is too large
Load Diff
@ -83,6 +83,7 @@ class ServicesPools(ModelHandler):
|
||||
'id': item.uuid,
|
||||
'name': item.name,
|
||||
'parent': item.service.name,
|
||||
'parent_type': item.service.data_type,
|
||||
'comments': item.comments,
|
||||
'state': item.state if item.service.provider.maintenance_mode is False else State.MAINTENANCE,
|
||||
'thumb': item.image.thumb64 if item.image is not None else DEFAULT_THUMB_BASE64,
|
||||
@ -97,7 +98,7 @@ class ServicesPools(ModelHandler):
|
||||
'restrained': item.isRestrained(),
|
||||
'show_transports': item.show_transports,
|
||||
'permission': permissions.getEffectivePermission(self._user, item),
|
||||
'info': Services.serviceInfo(item.service)
|
||||
'info': Services.serviceInfo(item.service),
|
||||
}
|
||||
|
||||
if item.osmanager is not None:
|
||||
|
@ -94,6 +94,7 @@ class Transports(ModelHandler):
|
||||
'networks': [{'id': n.id} for n in item.networks.all()],
|
||||
'deployed_count': item.deployedServices.count(),
|
||||
'type': type_.type(),
|
||||
'allowedProviders': list(i.type() for i in type_.allowedProviders),
|
||||
'permission': permissions.getEffectivePermission(self._user, item)
|
||||
}
|
||||
|
||||
|
@ -62,8 +62,8 @@ class Transport(Module):
|
||||
# Linux
|
||||
supportedOss = OsDetector.desktopOss # Supported operating systems
|
||||
|
||||
# If this transport is for an specific provider, this will be != None
|
||||
onlyForProviders = None
|
||||
# If this transport is for an specific provider, this will be an non empty tuple/list
|
||||
allowedProviders = ()
|
||||
|
||||
# If this transport is visible via Web, via Thin Client or both
|
||||
webTransport = False
|
||||
|
@ -33,15 +33,15 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
__updated__ = '2014-09-16'
|
||||
|
||||
from django.db import models
|
||||
from uds.core.Environment import Environment
|
||||
from uds.models.UUIDModel import UUIDModel
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
__updated__ = '2015-05-06'
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -63,7 +63,7 @@ class Client(object):
|
||||
pass
|
||||
try:
|
||||
cached_api_key = aKey
|
||||
cached_api = API(url='https://' + self._host + '/api', username=self._username, password=self._password, timeout=self._timeout, insecure=True, debug=False)
|
||||
cached_api = API(url='https://' + self._host + '/api', username=self._username, password=self._password, timeout=self._timeout, insecure=True, debug=True)
|
||||
return cached_api
|
||||
except:
|
||||
logger.exception('Exception connection ovirt at {0}'.format(self._host))
|
||||
@ -116,6 +116,8 @@ class Client(object):
|
||||
|
||||
vms = api.vms.list(query='name!=UDS*')
|
||||
|
||||
logger.debug('oVirt VMS: {}'.format(vms))
|
||||
|
||||
res = []
|
||||
|
||||
for vm in vms:
|
||||
@ -346,7 +348,6 @@ class Client(object):
|
||||
|
||||
api = self.__getApi()
|
||||
|
||||
|
||||
cluster = api.clusters.get(id=clusterId)
|
||||
vm = api.vms.get(id=machineId)
|
||||
|
||||
@ -620,3 +621,34 @@ class Client(object):
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
def getConnetcionInfo(self, machineId):
|
||||
'''
|
||||
Gets the connetion info for the specified machine
|
||||
'''
|
||||
try:
|
||||
lock.acquire(True)
|
||||
api = self.__getApi()
|
||||
|
||||
vm = api.vms.get(id=machineId)
|
||||
|
||||
if vm is None:
|
||||
raise Exception('Machine not found')
|
||||
|
||||
display = vm.get_display()
|
||||
ticket = vm.ticket().get_ticket()
|
||||
return {
|
||||
'type': display.get_type(),
|
||||
'address': display.get_address(),
|
||||
'port': display.get_port(),
|
||||
'secure_port': display.get_secure_port(),
|
||||
'monitors': display.get_monitors(),
|
||||
'cert_subject': display.get_certificate().get_subject(),
|
||||
'ticket': {
|
||||
'value': ticket.get_value(),
|
||||
'expiry': ticket.get_expiry()
|
||||
}
|
||||
}
|
||||
|
||||
finally:
|
||||
lock.release()
|
||||
|
@ -315,8 +315,13 @@ gui.servicesPools.link = (event) ->
|
||||
onNew: (value, table, refreshFnc) ->
|
||||
api.templates.get "pool_add_transport", (tmpl) ->
|
||||
api.transports.overview (data) ->
|
||||
gui.doLog "Data Received: ", servPool, data
|
||||
valid = []
|
||||
for i in data
|
||||
if i.allowedProviders.length == 0 or (servPool.parent_type in i.allowedProviders)
|
||||
valid.push(i)
|
||||
modalId = gui.launchModal(gettext("Add transport"), api.templates.evaluate(tmpl,
|
||||
transports: data
|
||||
transports: valid
|
||||
))
|
||||
$(modalId + " .button-accept").on "click", (event) ->
|
||||
transport = $(modalId + " #id_transport_select").val()
|
||||
|
119
server/src/uds/transports/SPICE/BaseSPICETransport.py
Normal file
119
server/src/uds/transports/SPICE/BaseSPICETransport.py
Normal file
@ -0,0 +1,119 @@
|
||||
# -*- 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.
|
||||
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
|
||||
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
|
||||
|
||||
# This transport is specific for oVirt, so we need to point to it
|
||||
from uds.services.OVirt.OVirtProvider import Provider as oVirtProvider
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
READY_CACHE_TIMEOUT = 30
|
||||
|
||||
|
||||
class BaseSpiceTransport(Transport):
|
||||
'''
|
||||
Provides access via RDP to service.
|
||||
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'
|
||||
protocol = protocols.SPICE
|
||||
allowedProviders = oVirtProvider.offers
|
||||
|
||||
useEmptyCreds = gui.CheckBoxField(
|
||||
label=_('Empty creds'),
|
||||
order=1,
|
||||
tooltip=_('If checked, the credentials used to connect will be emtpy')
|
||||
)
|
||||
fixedName = gui.TextField(
|
||||
label=_('Username'),
|
||||
order=2,
|
||||
tooltip=_('If not empty, this username will be always used as credential')
|
||||
)
|
||||
fixedPassword = gui.PasswordField(
|
||||
label=_('Password'),
|
||||
order=3,
|
||||
tooltip=_('If not empty, this password will be always used as credential')
|
||||
)
|
||||
serverCertificate = gui.TextField(
|
||||
order=2,
|
||||
length=4096,
|
||||
multiline=4,
|
||||
label=_('Certificate'),
|
||||
tooltip=_('Server certificate (public), can be found on your ovirt engine, probably at /etc/pki/ovirt-engine/certs/ca.der'),
|
||||
required=True
|
||||
)
|
||||
|
||||
def isAvailableFor(self, userService, ip):
|
||||
'''
|
||||
Checks if the transport is available for the requested destination ip
|
||||
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)
|
||||
|
||||
def processedUser(self, userService, userName):
|
||||
v = self.processUserPassword(userService, userName, '')
|
||||
return v['username']
|
||||
|
||||
def processUserPassword(self, service, user, password):
|
||||
username = user.getUsernameForAuth()
|
||||
|
||||
if self.fixedName.value != '':
|
||||
username = self.fixedName.value
|
||||
|
||||
if self.fixedPassword.value != '':
|
||||
password = self.fixedPassword.value
|
||||
if self.useEmptyCreds.isTrue():
|
||||
username, password = '', '', ''
|
||||
|
||||
# Fix username/password acording to os manager
|
||||
username, password = service.processUserPassword(username, password)
|
||||
|
||||
return {'protocol': self.protocol, 'username': username, 'password': password}
|
||||
|
||||
def getConnectionInfo(self, service, user, password):
|
||||
return self.processUserPassword(service, user, password)
|
||||
|
||||
def getScript(self, script):
|
||||
with open(os.path.join(os.path.dirname(__file__), script)) as f:
|
||||
data = f.read()
|
||||
return data
|
33
server/src/uds/transports/SPICE/RemoteViewerFile.py
Normal file
33
server/src/uds/transports/SPICE/RemoteViewerFile.py
Normal file
@ -0,0 +1,33 @@
|
||||
'''
|
||||
Created on May 6, 2015
|
||||
|
||||
@author: dkmaster
|
||||
'''
|
||||
|
||||
|
||||
|
||||
'''
|
||||
[virt-viewer]
|
||||
type=spice
|
||||
host=ovirtnode1.dkmon.com
|
||||
port=5900
|
||||
password=S9MATvnrQNGr
|
||||
tls-port=5901
|
||||
fullscreen=0
|
||||
title=ovirt00004:%d - Press SHIFT+F12 to Release Cursor
|
||||
enable-smartcard=0
|
||||
enable-usb-autoshare=1
|
||||
delete-this-file=1
|
||||
usb-filter=-1,-1,-1,-1,0
|
||||
tls-ciphers=DEFAULT
|
||||
host-subject=O=dkmon.com,CN=ovirtnode1.dkmon.com
|
||||
ca=-----BEGIN CERTIFICATE-----\nMIIDsjCCApqgAwIBAgICEAAwDQYJKoZIhvcNAQEFBQAwQTELMAkGA1UEBhMCVVMxEjAQBgNVBAoT\nCWRrbW9uLmNvbTEeMBwGA1UEAxMVb3ZpcnQuZGttb24uY29tLjk5MTAxMCIXETEzMTIxMTE5MDA0\nNCswMDAwFw0yMzEyMTAxOTAwNDRaMEExCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlka21vbi5jb20x\nHjAcBgNVBAMTFW92aXJ0LmRrbW9uLmNvbS45OTEwMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC\nAQoCggEBAMeMD3njBOLjuS5XQY0kUu3+dT5S0HUhk2RyPv0Bf59Zun215jLX7qJLrDWCxq8xcdB0\n79Krn2wm+QpPBArSJGVZNXkwQSqnmQK7PGuC119wfOx/bQbgHGJuL6XzuRnLn1xMmcp9N33Q+Cic\nkV5B8bVF+bV5FUWzDbSQKsWJUMFwUXd7px9mZJWBCkY4cOoXdhdu5EMGWcEm2e4RkE+Fb611RAU2\nPL1iJwEu1qtG/OYvWh8DsO9OfFP4dZuJhy2oXmKudHgalNCm8hWYa498c1DA/uXZCGLjoN5en9yy\nXU2wkTDJGA0M95mkGlwoIaNXJMM4i4Q5lmzkU6xntp3/zNkCAwEAAaOBrzCBrDAdBgNVHQ4EFgQU\nOQAXE0qEasnOi3qEFFtMg19GqqYwagYDVR0jBGMwYYAUOQAXE0qEasnOi3qEFFtMg19GqqahRaRD\nMEExCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlka21vbi5jb20xHjAcBgNVBAMTFW92aXJ0LmRrbW9u\nLmNvbS45OTEwMYICEAAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcN\nAQEFBQADggEBABDS5koE1TcBf8hvRP86AEu07KfWby/BvWXMcGuWRgPnkjrLS0mYshXx2Y40Pblm\nYm/DHXtefrDHJ203T/zkuCLtMSI28peeyX5HS32lGhVAMaylKrQ4mRTBQ+TM2hVLyBv7o16FUcei\nBuGNsm6F8b1RtrGTvSlkNxDUU3haDDH6gRMmMjgYrFqVMrozlPgPyHlFc4A1Q9rM/WIXtRioFgAz\ntCL1n5PK61ZIjk6j6a/upj6jYDfq1G0hpR9XOV8CD1bY3ZaCaMwOaILf1yIzQFqNDmx0p5e949at\nZIflG2pSv/5NA7PoRPF2tHf/PfrNmkA81R03dRXps1UAGg5ds3c=\n-----END CERTIFICATE-----\n
|
||||
toggle-fullscreen=shift+f11
|
||||
release-cursor=shift+f12
|
||||
secure-attention=ctrl+alt+end
|
||||
secure-channels=main;inputs;cursor;playback;record;display;usbredir;smartcard
|
||||
'''
|
||||
|
||||
|
||||
class RemoteViewerFile(object):
|
||||
pass
|
127
server/src/uds/transports/SPICE/SPICETransport.py
Normal file
127
server/src/uds/transports/SPICE/SPICETransport.py
Normal file
@ -0,0 +1,127 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012 Virtual Cable S.L.
|
||||
# All rights reservem.
|
||||
#
|
||||
# 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 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 .BaseSPICETransport import BaseSpiceTransport
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
READY_CACHE_TIMEOUT = 30
|
||||
|
||||
|
||||
class SPICETransport(BaseSpiceTransport):
|
||||
'''
|
||||
Provides access via SPICE to service.
|
||||
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
|
||||
'''
|
||||
typeName = _('SPICE Transport (direct)')
|
||||
typeType = 'SPICETransport'
|
||||
typeDescription = _('SPICE Transport for direct connection')
|
||||
|
||||
useEmptyCreds = BaseSpiceTransport.useEmptyCreds
|
||||
fixedName = BaseSpiceTransport.fixedName
|
||||
fixedPassword = BaseSpiceTransport.fixedPassword
|
||||
serverCertificate = BaseSpiceTransport.serverCertificate
|
||||
|
||||
def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
|
||||
# We use helper to keep this clean
|
||||
prefs = user.prefs('rdp')
|
||||
|
||||
ci = self.getConnectionInfo(userService, user, password)
|
||||
username, password, domain = ci['username'], ci['password'], ci['domain']
|
||||
|
||||
width, height = CommonPrefs.getWidthHeight(prefs)
|
||||
depth = CommonPrefs.getDepth(prefs)
|
||||
|
||||
# r = SPICEFile(width == -1 or height == -1, width, height, depth, target=os['OS'])
|
||||
# r.address = '{}:{}'.format(ip, 3389)
|
||||
# r.username = username
|
||||
# r.password = password
|
||||
# r.domain = 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()
|
||||
#
|
||||
# # data
|
||||
# data = {
|
||||
# 'os': os['OS'],
|
||||
# 'ip': ip,
|
||||
# 'port': 3389,
|
||||
# 'username': username,
|
||||
# 'password': password,
|
||||
# 'hasCredentials': username != '' and password != '',
|
||||
# 'domain': domain,
|
||||
# 'width': width,
|
||||
# 'height': height,
|
||||
# 'depth': depth,
|
||||
# 'printers': self.allowPrinters.isTrue(),
|
||||
# 'smartcards': self.allowSmartcards.isTrue(),
|
||||
# 'drives': self.allowDrives.isTrue(),
|
||||
# 'serials': self.allowSerials.isTrue(),
|
||||
# 'compression': True,
|
||||
# 'wallpaper': self.wallpaper.isTrue(),
|
||||
# 'multimon': self.multimon.isTrue(),
|
||||
# 'fullScreen': width == -1 or height == -1,
|
||||
# 'this_server': request.build_absolute_uri('/'),
|
||||
# 'r': r,
|
||||
# }
|
||||
#
|
||||
# m = tools.DictAsObj(data)
|
||||
#
|
||||
# if m.domain != '':
|
||||
# m.usernameWithDomain = '{}\\\\{}'.format(m.domain, m.username)
|
||||
# else:
|
||||
# m.usernameWithDomain = m.username
|
||||
#
|
||||
# if m.os == OsDetector.Windows:
|
||||
# m.r.password = '{password}'
|
||||
#
|
||||
# os = {
|
||||
# OsDetector.Windows: 'windows',
|
||||
# OsDetector.Linux: 'linux',
|
||||
# OsDetector.Macintosh: 'macosx'
|
||||
#
|
||||
# }.get(m.os)
|
||||
#
|
||||
# if os is None:
|
||||
# return super(SPICETransport, self).getUDSTransportScript(self, userService, transport, ip, os, user, password, request)
|
||||
#
|
||||
# return self.getScript('scripts/{}/direct.py'.format(os)).format(m=m)
|
157
server/src/uds/transports/SPICE/TSPICETransport.py
Normal file
157
server/src/uds/transports/SPICE/TSPICETransport.py
Normal file
@ -0,0 +1,157 @@
|
||||
# -*- 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.
|
||||
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
|
||||
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.models import TicketStore
|
||||
from uds.core.util import OsDetector
|
||||
from uds.core.util import tools
|
||||
|
||||
from .BaseSPICETransport import BaseSpiceTransport
|
||||
|
||||
import logging
|
||||
import random
|
||||
import string
|
||||
import time
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
READY_CACHE_TIMEOUT = 30
|
||||
|
||||
|
||||
class TSPICETransport(BaseSpiceTransport):
|
||||
'''
|
||||
Provides access via SPICE to service.
|
||||
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
|
||||
'''
|
||||
typeName = _('SPICE Transport (tunneled)')
|
||||
typeType = 'TSSPICETransport'
|
||||
typeDescription = _('SPICE Transport for tunneled connection')
|
||||
iconFile = 'rdp.png'
|
||||
needsJava = True # If this transport needs java for rendering
|
||||
protocol = protocols.SPICE
|
||||
|
||||
tunnelServer = gui.TextField(label=_('Tunnel server'), order=1, tooltip=_('IP or Hostname of tunnel server sent to client device ("public" ip) and port. (use HOST:PORT format)'))
|
||||
tunnelCheckServer = gui.TextField(label=_('Tunnel host check'), order=2, tooltip=_('If not empty, this server will be used to check if service is running before assigning it to user. (use HOST:PORT format)'))
|
||||
|
||||
useEmptyCreds = BaseSpiceTransport.useEmptyCreds
|
||||
fixedName = BaseSpiceTransport.fixedName
|
||||
fixedPassword = BaseSpiceTransport.fixedPassword
|
||||
serverCertificate = BaseSpiceTransport.serverCertificate
|
||||
|
||||
def initialize(self, values):
|
||||
if values is not None:
|
||||
if values['tunnelServer'].count(':') != 1:
|
||||
raise Transport.ValidationException(_('Must use HOST:PORT in Tunnel Server Field'))
|
||||
|
||||
def getUDSTransportScript(self, userService, transport, ip, os, user, password, request):
|
||||
# We use helper to keep this clean
|
||||
prefs = user.prefs('rdp')
|
||||
|
||||
ci = self.getConnectionInfo(userService, user, password)
|
||||
username, password, domain = ci['username'], ci['password'], ci['domain']
|
||||
|
||||
width, height = CommonPrefs.getWidthHeight(prefs)
|
||||
depth = CommonPrefs.getDepth(prefs)
|
||||
|
||||
tunpass = ''.join(random.choice(string.letters + string.digits) for _i in range(12))
|
||||
tunuser = TicketStore.create(tunpass)
|
||||
|
||||
sshHost, sshPort = self.tunnelServer.value.split(':')
|
||||
|
||||
logger.debug('Username generated: {0}, password: {1}'.format(tunuser, tunpass))
|
||||
|
||||
# r = SPICEFile(width == -1 or height == -1, width, height, depth, target=os['OS'])
|
||||
# r.address = '{address}'
|
||||
# r.username = username
|
||||
# r.password = password
|
||||
# r.domain = 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()
|
||||
#
|
||||
#
|
||||
# # data
|
||||
# data = {
|
||||
# 'os': os['OS'],
|
||||
# 'ip': ip,
|
||||
# 'tunUser': tunuser,
|
||||
# 'tunPass': tunpass,
|
||||
# 'tunHost': sshHost,
|
||||
# 'tunPort': sshPort,
|
||||
# 'username': username,
|
||||
# 'password': password,
|
||||
# 'hasCredentials': username != '' and password != '',
|
||||
# 'domain': domain,
|
||||
# 'width': width,
|
||||
# 'height': height,
|
||||
# 'depth': depth,
|
||||
# 'printers': self.allowPrinters.isTrue(),
|
||||
# 'smartcards': self.allowSmartcards.isTrue(),
|
||||
# 'drives': self.allowDrives.isTrue(),
|
||||
# 'serials': self.allowSerials.isTrue(),
|
||||
# 'compression': True,
|
||||
# 'wallpaper': self.wallpaper.isTrue(),
|
||||
# 'multimon': self.multimon.isTrue(),
|
||||
# 'fullScreen': width == -1 or height == -1,
|
||||
# 'this_server': request.build_absolute_uri('/'),
|
||||
# 'r': r,
|
||||
# }
|
||||
#
|
||||
# m = tools.DictAsObj(data)
|
||||
#
|
||||
# if m.domain != '':
|
||||
# m.usernameWithDomain = '{}\\\\{}'.format(m.domain, m.username)
|
||||
# else:
|
||||
# m.usernameWithDomain = m.username
|
||||
#
|
||||
# if m.os == OsDetector.Windows:
|
||||
# r.password = '{password}'
|
||||
#
|
||||
# os = {
|
||||
# OsDetector.Windows: 'windows',
|
||||
# OsDetector.Linux: 'linux',
|
||||
# OsDetector.Macintosh: 'macosx'
|
||||
#
|
||||
# }.get(m.os)
|
||||
#
|
||||
# if os is None:
|
||||
# return super(TSPICETransport, self).getUDSTransportScript(self, userService, transport, ip, os, user, password, request)
|
||||
#
|
||||
# return self.getScript('scripts/{}/tunnel.py'.format(os)).format(m=m)
|
45
server/src/uds/transports/SPICE/__init__.py
Normal file
45
server/src/uds/transports/SPICE/__init__.py
Normal file
@ -0,0 +1,45 @@
|
||||
# -*- 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.
|
||||
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
|
||||
from django.utils.translation import ugettext_noop as _
|
||||
from uds.core.managers.UserPrefsManager import UserPrefsManager, CommonPrefs
|
||||
from .SPICETransport import SPICETransport
|
||||
from .TSPICETransport import TSPICETransport
|
||||
|
||||
UserPrefsManager.manager().registerPrefs('rdp',
|
||||
_('Remote Desktop Protocol'),
|
||||
[
|
||||
CommonPrefs.screenSizePref,
|
||||
CommonPrefs.depthPref
|
||||
]
|
||||
)
|
BIN
server/src/uds/transports/SPICE/rdp.png
Normal file
BIN
server/src/uds/transports/SPICE/rdp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
62
server/src/uds/transports/SPICE/scripts/linux/direct.py
Normal file
62
server/src/uds/transports/SPICE/scripts/linux/direct.py
Normal file
@ -0,0 +1,62 @@
|
||||
# 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, invalid-sequence-index
|
||||
from PyQt4 import QtCore, QtGui
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
from uds import tools # @UnresolvedImport
|
||||
|
||||
import six
|
||||
|
||||
|
||||
def execNewXFreeRdp(parent, xfreerdp):
|
||||
import subprocess # @Reimport
|
||||
params = [xfreerdp] + {m.r.as_new_xfreerdp_params} + ['/v:{m.r.address}'] # @UndefinedVariable
|
||||
tools.addTaskToWait(subprocess.Popen(params))
|
||||
|
||||
|
||||
def execRdesktop(parent, rdesktop):
|
||||
import subprocess # @Reimport
|
||||
params = [rdesktop] + {m.r.as_rdesktop_params} + ['{m.r.address}'] # @UndefinedVariable
|
||||
p = subprocess.Popen(params, stdin=subprocess.PIPE)
|
||||
if '{m.password}' != '':
|
||||
p.stdin.write('{m.password}')
|
||||
p.stdin.close()
|
||||
tools.addTaskToWait(p)
|
||||
|
||||
# Try to locate a "valid" version of xfreerdp as first option (<1.1 does not allows drive redirections, so it will not be used if found)
|
||||
xfreerdp = tools.findApp('xfreerdp')
|
||||
rdesktop = tools.findApp('rdesktop')
|
||||
fnc, app = None, None
|
||||
|
||||
if rdesktop is not None:
|
||||
fnc, app = execRdesktop, rdesktop
|
||||
|
||||
if xfreerdp is not None:
|
||||
# Check for nice version
|
||||
try:
|
||||
try:
|
||||
version = subprocess.check_output([xfreerdp, '--version'])
|
||||
except subprocess.CalledProcessError as e:
|
||||
version = e.output
|
||||
|
||||
version = float(re.search(r'version ([0-9]*\.[0-9]*)', version).groups()[0])
|
||||
if version < 1.1:
|
||||
raise Exception()
|
||||
else:
|
||||
fnc, app = execNewXFreeRdp, xfreerdp
|
||||
|
||||
except Exception as e: # Valid version not found, pass to check rdesktop
|
||||
# QtGui.QMessageBox.critical(parent, 'Notice', six.text_type(e), QtGui.QMessageBox.Ok) # @UndefinedVariable
|
||||
pass
|
||||
|
||||
if app is None or fnc is None:
|
||||
raise Exception('''<p>You need to have installed xfreerdp (>= 1.1) or rdesktop, and have them in your PATH in order to connect to this UDS service.</p>
|
||||
<p>Please, install apropiate package for your system.</p>
|
||||
<p>Also note that xfreerdp prior to version 1.1 will not be taken into consideration.</p>
|
||||
''')
|
||||
else:
|
||||
fnc(parent, app) # @UndefinedVariable
|
69
server/src/uds/transports/SPICE/scripts/linux/tunnel.py
Normal file
69
server/src/uds/transports/SPICE/scripts/linux/tunnel.py
Normal file
@ -0,0 +1,69 @@
|
||||
# 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, invalid-sequence-index
|
||||
from PyQt4 import QtCore, QtGui
|
||||
import subprocess
|
||||
import re
|
||||
from uds.forward import forward # @UnresolvedImport
|
||||
|
||||
from uds import tools # @UnresolvedImport
|
||||
|
||||
import six
|
||||
|
||||
|
||||
def execNewXFreeRdp(parent, xfreerdp, port):
|
||||
import subprocess # @Reimport
|
||||
params = [xfreerdp] + {m.r.as_new_xfreerdp_params} + ['/v:127.0.0.1:{{}}'.format(port)] # @UndefinedVariable
|
||||
tools.addTaskToWait(subprocess.Popen(params))
|
||||
|
||||
|
||||
def execRdesktop(parent, rdesktop, port):
|
||||
import subprocess # @Reimport
|
||||
params = [rdesktop] + {m.r.as_rdesktop_params} + ['127.0.0.1:{{}}'.format(port)] # @UndefinedVariable
|
||||
p = subprocess.Popen(params, stdin=subprocess.PIPE)
|
||||
if {m.hasCredentials}: # @UndefinedVariable
|
||||
p.stdin.write('{m.password}')
|
||||
p.stdin.close()
|
||||
tools.addTaskToWait(p)
|
||||
|
||||
# Try to locate a "valid" version of xfreerdp as first option (<1.1 does not allows drive redirections, so it will not be used if found)
|
||||
xfreerdp = tools.findApp('xfreerdp')
|
||||
rdesktop = tools.findApp('rdesktop')
|
||||
fnc, app = None, None
|
||||
|
||||
if rdesktop is not None:
|
||||
fnc, app = execRdesktop, rdesktop
|
||||
|
||||
if xfreerdp is not None:
|
||||
# Check for nice version
|
||||
try:
|
||||
try:
|
||||
version = subprocess.check_output([xfreerdp, '--version'])
|
||||
except subprocess.CalledProcessError as e:
|
||||
version = e.output
|
||||
|
||||
version = float(re.search(r'version ([0-9]*\.[0-9]*)', version).groups()[0])
|
||||
if version < 1.1:
|
||||
raise Exception()
|
||||
else:
|
||||
fnc, app = execNewXFreeRdp, xfreerdp
|
||||
|
||||
except Exception as e: # Valid version not found, pass to check rdesktop
|
||||
# QtGui.QMessageBox.critical(parent, 'Notice', six.text_type(e), QtGui.QMessageBox.Ok) # @UndefinedVariable
|
||||
pass
|
||||
|
||||
if app is None or fnc is None:
|
||||
raise Exception('''<p>You need to have installed xfreerdp (>= 1.1) or rdesktop, and have them in your PATH in order to connect to this UDS service.</p>
|
||||
<p>Please, install apropiate package for your system.</p>
|
||||
<p>Also note that xfreerdp prior to version 1.1 will not be taken into consideration.</p>
|
||||
''')
|
||||
else:
|
||||
# Open tunnel
|
||||
forwardThread, port = forward('{m.tunHost}', '{m.tunPort}', '{m.tunUser}', '{m.tunPass}', '{m.ip}', 3389)
|
||||
|
||||
if forwardThread.status == 2:
|
||||
raise Exception('Unable to open tunnel')
|
||||
|
||||
fnc(parent, app, port) # @UndefinedVariable
|
111
server/src/uds/transports/SPICE/scripts/macosx/direct.py
Normal file
111
server/src/uds/transports/SPICE/scripts/macosx/direct.py
Normal file
@ -0,0 +1,111 @@
|
||||
# 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, invalid-sequence-index
|
||||
from PyQt4 import QtCore, QtGui
|
||||
import subprocess
|
||||
import os
|
||||
import urllib
|
||||
|
||||
from uds import tools # @UnresolvedImport
|
||||
|
||||
import six
|
||||
|
||||
theFile = '''{m.r.as_file}'''
|
||||
|
||||
# First, try to locate Remote Desktop Connection (version 2, from Microsoft website, not the app store one)
|
||||
|
||||
|
||||
filename = tools.saveTempFile(theFile)
|
||||
msrdc = '/Applications/Remote Desktop Connection.app/Contents/MacOS/Remote Desktop Connection'
|
||||
cord = "/Applications/CoRD.app/Contents/MacOS/CoRD"
|
||||
|
||||
if os.path.isfile(msrdc):
|
||||
executable = msrdc
|
||||
elif os.path.isfile(cord):
|
||||
executable = cord
|
||||
else:
|
||||
executable = None
|
||||
|
||||
|
||||
def onExit():
|
||||
import subprocess # @Reimport
|
||||
subprocess.call(
|
||||
[
|
||||
'security',
|
||||
'delete-generic-password',
|
||||
'-a', '{m.usernameWithDomain}',
|
||||
'-s', 'Remote Desktop Connection 2 Password for {m.ip}',
|
||||
]
|
||||
)
|
||||
|
||||
if executable is None:
|
||||
QtGui.QMessageBox.critical(parent, 'Notice', # @UndefinedVariable
|
||||
'''<p><b>Microsoft Remote Desktop Connection not found</b></p>
|
||||
<p>In order to connect to UDS RDP Sessions, you need to have at least one of the following:<p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><b>Microsoft Remote Desktop Connection version 2.</b> (Recommended)</p>
|
||||
<p>You can get it from <a href="http://www.microsoft.com/es-es/download/details.aspx?id=18140">this link</a></p>
|
||||
<p>Remember that you need to use the One from the Microsoft site (the link provided), not the one from the AppStore</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><b>CoRD</b> (A bit unstable from 10.7 onwards)</p>
|
||||
<p>You can get it from <a href="{m.this_server}static/other/CoRD.pkg">this link</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>If both apps are installed, Remote Desktop Connection will be used as first option</p>
|
||||
|
||||
''', QtGui.QMessageBox.Ok)
|
||||
elif executable == msrdc:
|
||||
try:
|
||||
if {m.hasCredentials}: # @UndefinedVariable
|
||||
subprocess.call(
|
||||
[
|
||||
'security',
|
||||
'add-generic-password',
|
||||
'-w', '{m.password}',
|
||||
'-U',
|
||||
'-a', '{m.usernameWithDomain}',
|
||||
'-s', 'Remote Desktop Connection 2 Password for {m.ip}',
|
||||
'-T', '/Applications/Remote Desktop Connection.app',
|
||||
]
|
||||
)
|
||||
tools.addExecBeforeExit(onExit)
|
||||
# Call and wait for exit
|
||||
tools.addTaskToWait(subprocess.Popen([executable, filename]))
|
||||
|
||||
tools.addFileToUnlink(filename)
|
||||
except Exception as e:
|
||||
QtGui.QMessageBox.critical(parent, 'Notice', six.text_type(e), QtGui.QMessageBox.Ok) # @UndefinedVariable
|
||||
else: # CoRD
|
||||
url = 'rdp://'
|
||||
|
||||
username, domain = '{m.username}', '{m.domain}'
|
||||
|
||||
if username != '':
|
||||
url += username
|
||||
if '{m.password}' != '':
|
||||
url += ':' + urllib.quote('{m.password}')
|
||||
url += '@'
|
||||
url += '{m.ip}/'
|
||||
if domain != '':
|
||||
url += domain
|
||||
|
||||
url += '?screenDepth={m.r.bpp}'
|
||||
|
||||
if {m.r.fullScreen}: # @UndefinedVariable
|
||||
url += '&fullscreen=true'
|
||||
else:
|
||||
url += 'screenWidth={m.r.width}&screenHeight={m.r.height}'
|
||||
|
||||
url += '&forwardAudio=' + '01'[{m.r.redirectAudio}] # @UndefinedVariable
|
||||
|
||||
if {m.r.redirectDrives}: # @UndefinedVariable
|
||||
url += '&forwardDisks=true'
|
||||
|
||||
if {m.r.redirectPrinters}: # @UndefinedVariable
|
||||
url += '&forwardPrinters=true'
|
||||
|
||||
tools.addTaskToWait(subprocess.Popen(['open', url]))
|
120
server/src/uds/transports/SPICE/scripts/macosx/tunnel.py
Normal file
120
server/src/uds/transports/SPICE/scripts/macosx/tunnel.py
Normal file
@ -0,0 +1,120 @@
|
||||
# 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, invalid-sequence-index
|
||||
from PyQt4 import QtCore, QtGui
|
||||
import subprocess
|
||||
import os
|
||||
import urllib
|
||||
from uds.forward import forward # @UnresolvedImport
|
||||
|
||||
from uds import tools # @UnresolvedImport
|
||||
|
||||
import six
|
||||
|
||||
# First, try to locate Remote Desktop Connection (version 2, from Microsoft website, not the app store one)
|
||||
msrdc = '/Applications/Remote Desktop Connection.app/Contents/MacOS/Remote Desktop Connection'
|
||||
cord = "/Applications/CoRD.app/Contents/MacOS/CoRD"
|
||||
|
||||
if os.path.isfile(msrdc):
|
||||
executable = msrdc
|
||||
elif os.path.isfile(cord):
|
||||
executable = cord
|
||||
else:
|
||||
executable = None
|
||||
|
||||
|
||||
def onExit():
|
||||
import subprocess # @Reimport
|
||||
subprocess.call(
|
||||
[
|
||||
'security',
|
||||
'delete-generic-password',
|
||||
'-a', '{m.username}',
|
||||
'-s', 'Remote Desktop Connection 2 Password for 127.0.0.1',
|
||||
]
|
||||
)
|
||||
|
||||
if executable is None:
|
||||
QtGui.QMessageBox.critical(parent, 'Notice', # @UndefinedVariable
|
||||
'''<p><b>Microsoft Remote Desktop Connection not found</b></p>
|
||||
<p>In order to connect to UDS RDP Sessions, you need to have at least one of the following:<p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><b>Microsoft Remote Desktop Connection version 2.</b> (Recommended)</p>
|
||||
<p>You can get it from <a href="http://www.microsoft.com/es-es/download/details.aspx?id=18140">this link</a></p>
|
||||
<p>Remember that you need to use the One from the Microsoft site (the link provided), not the one from the AppStore</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><b>CoRD</b> (A bit unstable from 10.7 onwards)</p>
|
||||
<p>You can get it from <a href="{m.this_server}static/other/CoRD.pkg">this link</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>If both apps are installed, Remote Desktop Connection will be used as first option</p>
|
||||
|
||||
''', QtGui.QMessageBox.Ok)
|
||||
|
||||
|
||||
forwardThread, port = forward('{m.tunHost}', '{m.tunPort}', '{m.tunUser}', '{m.tunPass}', '{m.ip}', 3389)
|
||||
|
||||
if forwardThread.status == 2:
|
||||
raise Exception('Unable to open tunnel')
|
||||
|
||||
else:
|
||||
theFile = '''{m.r.as_file}'''.format(
|
||||
address='127.0.0.1:{{}}'.format(port)
|
||||
)
|
||||
filename = tools.saveTempFile(theFile)
|
||||
tools.addFileToUnlink(filename)
|
||||
|
||||
if executable == msrdc:
|
||||
try:
|
||||
if {m.hasCredentials}: # @UndefinedVariable
|
||||
subprocess.call(
|
||||
[
|
||||
'security',
|
||||
'add-generic-password',
|
||||
'-w', '{m.password}',
|
||||
'-U',
|
||||
'-a', '{m.username}',
|
||||
'-s', 'Remote Desktop Connection 2 Password for 127.0.0.1'.format(port),
|
||||
'-T', '/Applications/Remote Desktop Connection.app',
|
||||
]
|
||||
)
|
||||
tools.addExecBeforeExit(onExit)
|
||||
# Call but do not wait for exit
|
||||
tools.addTaskToWait(subprocess.Popen([executable, filename]))
|
||||
|
||||
tools.addFileToUnlink(filename)
|
||||
except Exception as e:
|
||||
QtGui.QMessageBox.critical(parent, 'Notice', six.text_type(e), QtGui.QMessageBox.Ok) # @UndefinedVariable
|
||||
else: # CoRD
|
||||
url = 'rdp://'
|
||||
username, domain = '{m.username}', '{m.domain}'
|
||||
|
||||
if username != '':
|
||||
url += username
|
||||
if '{m.password}' != '':
|
||||
url += ':' + urllib.quote('{m.password}')
|
||||
url += '@'
|
||||
url += '127.0.0.1:3389/'
|
||||
if domain != '':
|
||||
url += domain
|
||||
|
||||
url += '?screenDepth={m.r.bpp}'
|
||||
|
||||
if {m.r.fullScreen}: # @UndefinedVariable
|
||||
url += '&fullscreen=true'
|
||||
else:
|
||||
url += 'screenWidth={m.r.width}&screenHeight={m.r.height}'
|
||||
|
||||
url += '&forwardAudio=' + '01'[{m.r.redirectAudio}] # @UndefinedVariable
|
||||
|
||||
if {m.r.redirectDrives}: # @UndefinedVariable
|
||||
url += '&forwardDisks=true'
|
||||
|
||||
if {m.r.redirectPrinters}: # @UndefinedVariable
|
||||
url += '&forwardPrinters=true'
|
||||
|
||||
tools.addTaskToWait(subprocess.Popen(['open', url]))
|
23
server/src/uds/transports/SPICE/scripts/windows/direct.py
Normal file
23
server/src/uds/transports/SPICE/scripts/windows/direct.py
Normal file
@ -0,0 +1,23 @@
|
||||
# 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
|
||||
theFile = '''{m.r.as_file}'''.format(password=win32crypt.CryptProtectData(six.binary_type('{m.password}'.encode('UTF-16LE')), None, None, None, None, 0x01).encode('hex'))
|
||||
|
||||
filename = tools.saveTempFile(theFile)
|
||||
executable = tools.findApp('mstsc.exe')
|
||||
subprocess.call([executable, filename])
|
||||
tools.addFileToUnlink(filename)
|
||||
|
||||
# QtGui.QMessageBox.critical(parent, 'Notice', filename + ", " + executable, QtGui.QMessageBox.Ok)
|
36
server/src/uds/transports/SPICE/scripts/windows/tunnel.py
Normal file
36
server/src/uds/transports/SPICE/scripts/windows/tunnel.py
Normal file
@ -0,0 +1,36 @@
|
||||
# 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')
|
||||
|
||||
# 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.call([executable, filename])
|
||||
tools.addFileToUnlink(filename)
|
||||
|
||||
# QtGui.QMessageBox.critical(parent, 'Notice', filename + ", " + executable, QtGui.QMessageBox.Ok)
|
Loading…
Reference in New Issue
Block a user